Rebuilding Sofle-Choc on Android
I built a custom Android keyboard. It got weird.
Rebuilding Sofle-Choc on Android
I built a custom Android keyboard. It got weird.
A full IME — written in Kotlin, handling every keypress, suggestion, and cursor movement myself. The idea was to replicate the Sofle Choc split. Because.
How it all started
I was stuck at a hospital all day, with nothing but my phone, getting bored and worried sick, I needed some way to vent.
Termux saved the day. I had used termux sparingly for some experimentation, but never considered it be a proper dev environment. After poking around with it, I found out its possible to run X using pdistro.
So I installed:
- pdistro and debian
- a bunch of libraries I could find on a reddit thread
- neovim
- opencode
- github’s gh cli
Since there was no way to run the builds fast enough, the build setup went something like this:
- generate the templates for ADRs, Tasks with Acceptance Criterias’
- typescript and kotlin linter (got rid of the kotlin one later)
- some instructions on claude.md to adopt
Test First - when stuck look for other libraries and solutions on internet
- github actions to build and run tests
- opencode with deepseek v4 flash free set to max (for ~85% of the build)
Before any UI building the instructions were to build it out using html-css and use python server to locally host it out.
One of the problems that I still face today is the way to share screenshots. The method I adopted was a pretty crude one.
- Take the screenshot and convert to base64
- Share the base64 to opencode and use a
/command - Decode and save it as image, and let
deepseek-v4-proread and understand - Write the understanding back to a debug log.
Some realizations and surprises while building this:
Backspace into a committed word is a whole thing:
Once a word is committed, the suggestion engine loses track of it. Backspacing into it looks like a new character to the IME. Getting autocomplete to pick up the fragment again meant wrapping three API calls in beginBatchEdit/endBatchEdit to stop the editor firing callbacks mid-operation. AnySoftKeyboard does the same thing. Took me way too long to figure out why.
Next-word prediction is more interesting than I expected:
After every committed word, surface what probably comes next — T9 style, but without the hardware. Two layers: a static English seed (Norvig’s bigram corpus, MIT licensed) and a personal layer that learns from your actual typing. The scoring uses temporal decay from librime — recent use contributes a near-full boost, something you typed 1000 commits ago contributes almost nothing. Old habits fade. New ones take over within a few hundred keystrokes.
Learned about bigrams, infigrams, suffix-array with binary search and a new shit called beam_search
Mixed language (Banglish, Hinglish) works without any language detection, kinda — the personal layer is just a string map. It doesn’t care what language you’re in. Again, ASK showed the way for MVP.
The suggestion interface mirrors librime’s Grammar pattern, context passes through the function call, not stored as shared mutable state. Took one refactor to get there but it cleaned up everything downstream.
This can be further improved by looking into how HeliBoard does prediction. I found a bunch of dictionaries on codeberg, which can be compiled to a dict.
A seven part series on the same: https://ikouchiha47.github.io/keyboard/
메타데이터
- post_id
- decf5ab1e18f
- slug
- rebuilding-sofle-choc-on-android-decf5ab1e18f
- url
- https://medium.com/@amitavaa.ag/rebuilding-sofle-choc-on-android-decf5ab1e18f
- canonical_url
- https://medium.com/@amitavaa.ag/rebuilding-sofle-choc-on-android-decf5ab1e18f
- author_url
- https://medium.com/@amitavaa.ag
- status
- ok
- fetched_at
- 2026-08-16 01:45:07