한국어 | English — ReteKey IME v0.2.3 — APK(Android 9.0+) · legacy APK(Android 4.0+)
Is your phone's web browser too old to download files from GitHub? Try ReteGet, a file and APK downloader for old Android phones.
An Android Hangul keyboard for developers and power users — for anyone who needs Esc, Tab, Ctrl chords, function and arrow keys on a phone, and a keyboard that behaves itself in a terminal.
It is small and asks for almost nothing. The release APK is about 470 KB, most of that the Hanja
tables; it is plain Java with no third-party runtime dependencies, and the Hanja tables are read
straight out of the APK rather than loaded into memory. It declares one permission, VIBRATE —
an ordinary permission that is never requested at runtime and does nothing but the buzz under your
finger, which you can turn down to zero. No network, no account, no analytics.
And nothing is missing for it: Cheonjiin(천지인), Naratgeul(나랏글) and 2beolsik(2벌식), Hanja conversion, physical keyboards, and the special keys most keyboards leave out.
This English README is the canonical version; the Korean one is its translation.
- Download
- What it looks like
- Who it is for
- Features
- Layout
- User layouts
- Floating keyboard
- Notepad
- Hanja conversion
- Physical keyboards
- Settings
- Theming
- Android version support
- For developers
- The name
- License
ReteKey is on F-Droid. If you have the F-Droid client, that is the easiest way to get it and to keep it updated:
F-Droid builds the app from this source on its own servers and checks the result against the APK published here before shipping it, so what you install is what this repository builds.
Or take an APK directly:
⬇ Android 9+ · ⬇ Android 4.0+ · all releases
Current release: v0.2.3 — retekey-0.2.3.apk (703 KB; the Android 4.0 build is 563 KB)
Take the first link unless your phone is older than Android 9; the two are the same app and one replaces the other. More about the two builds. F-Droid carries the Android 9+ build; the Android 4.0 one lives on the releases page.
- Install it. Open the downloaded APK on the phone and confirm. Older Android versions may first ask you to allow installs from unknown sources.
- Turn the keyboard on. Android hides new keyboards until you enable them: Settings → System → Languages & input → On-screen keyboard → Manage keyboards, and switch ReteKey on.
- Choose it. Tap any text field and pick ReteKey from the keyboard chooser, or use Settings → Default keyboard.
- Try it. ReteKey's own launcher screen has a button for each of those two steps and a text box to type in, so you can do the whole thing without hunting through system settings.
To move between Korean, English and the 12-key layouts afterwards, tap the layout key at the bottom right. It is captioned with where it goes rather than where
you are — QWE, DVO, 2BS, CJI, NRG, ARW, NUM, in bold capitals — so you can see what the next press will give you.
Hold it for the menu page. Which layouts that key walks through, and in what order, is yours to set
in the settings.
The keyboards below are drawn by the keyboard's own rendering code, at the size a 480-pixel-wide screen gives it — the same pixels the app puts on a phone. The floating panels are drawn over a page of writing, because a panel you can see through has nothing to show against a blank ground.
2-beolsik: raised keys, each with its hold alternate in the corner, a bar for the space key, and the layout key naming where it goes next.
The ☰ page: editing and cursor keys on the right, the keyboard's own controls on the left, and settings in the corner a thumb reaches without looking.
![]() |
![]() |
| the Hanja window: keyboard-wide, 훈음 beside each character, nine to a page | the floating keyboard, translucent and confined to its half of the screen |
Every layout, page and panel is shown in Layout below, and the older-Android build has screenshots of its own.
Most keyboards assume you are writing messages. This one assumes you are also, sometimes, working: editing a config file over ssh, running a command in Termux, moving through a long document with the arrow keys, or pasting into a terminal that does not behave like a chat box.
So the keys that usually go missing are here — Esc, Tab, Ctrl, Alt, Meta, F1–F12, arrows,
Home/End, PgUp/PgDn, Ins, forward-delete, PrtSc (Prt) — sending real key events rather than typing
characters that look like them. Modifier chords the keyboard has no use for are handed to the app,
so an editor's own shortcuts keep working, and a soft Ctrl plus a letter sends a genuine control
code to a terminal.
None of that gets in the way of ordinary typing. Korean composes properly on all three Korean layouts, including every compound final (많, 삶, 앉), Hanja conversion works both ways, and if you never open the keypad page you will never see the parts you do not need.
- Thirty-five layouts — 2beolsik(2벌식), QWERTY, Dvorak, Colemak, Spanish, Portuguese, Italian, Polish, Vietnamese (Telex), German, Turkish (QWERTY and F), French AZERTY, Greek, Hebrew, Persian, Arabic, Urdu, Thai (Kedmanee and Pattachote), Hindi, Russian (ЙЦУКЕН and phonetic), Ukrainian, Bulgarian (phonetic and БДС), Macedonian, Serbian, Georgian, Armenian, Japanese Romaji and the 12-key Japanese Flick, and the 12-key Cheonjiin(천지인) and Naratgeul(나랏글) phone modes — with Arrows, Keypad, the IPA phonetic page and a layout of your own as extras. The layout key walks the ones you enabled, in the order you set; holding it opens the menu.
- A layout you write yourself — three rows of keys and what each one holds, in a text file you
share into the keyboard or open as a
.rklfile. No build, no account, no waiting for a release. How to write one. - Phonetic symbols on a page of their own — the IPA alphabet where the letters sound like it (X-SAMPA's reading of a QWERTY board), each key holding its own family, Shift for the plain letters a transcription is full of, and, when the page does not have it, a symbol found by name or by family rather than hunted for.
- Text that was never copied — any app's Share can hand ReteKey a password or a link, which it keeps in its own storage and types when you pick it. The system clipboard is neither read nor written for it, so there is nothing on it for a keyboard in the background to find.
- Numbers get a keypad — a field that takes a phone number, an amount, a PIN or a date opens on the 12-key Keypad. The layout key walks your own list from there, and the next ordinary field is back on the layout you were using.
- An action bar, if you want one — a strip above the keys for selecting a word, select all, cut/copy/paste and the arrows, in the order you choose. Off until you turn it on.
- Out of the system's way — the hide-keyboard and switch-keyboard buttons many ROMs draw at the bottom of the screen keep their band, so no key of ours is stranded underneath them. Only what is genuinely over the keyboard is reserved — the keyboard measures its own bottom edge against the bar rather than trusting the insets, since two phones can report the same bar and place the keyboard differently. Settings can force it either way, and shows what your phone reports and what it measured.
- Stateful 2-beolsik Hangul composer with compound vowels and final consonants, consonant migration, and reversible backspace (닭 → 달 → 다).
- Hanja conversion in both directions, with 훈음 glosses, paging, and number-key selection, in a window of its own that appears whichever keyboard you are using — or, if you choose, docked above the keyboard or in the floating keyboard's own place.
- Notepad on a key: a full-screen panel above the keyboard, with a list that sorts and selects like a file manager and notes stamped the moment they are made.
- Floating keyboard for a tablet in landscape: a translucent panel confined to one half of the screen, draggable, resizable, and mirrored to the other half with one key.
- Physical keyboard support: user-assignable KO/EN and Hanja keys, modifier chords, and 2-beolsik mapping for Bluetooth or wired keyboards.
- Held-key auto-repeat for space, enter, backspace and the arrows, with a configurable start delay and interval.
- Raised, rounded keys with press feedback — the pressed key tints, the gaps between the keys blink, and the character you just typed appears large in a box above them, plus haptics and sound, each independently adjustable.
- Hold a key to type its alternate straight away: no popup, no drag — half a second, a little
longer than the system's own long press, so an unhurried letter is still a letter. The letter pages hold
1234567890on the top row,!@#$%^&*;on the middle one, and_-:='"?on the bottom one. - Light, dark, or the system's own — your choice, with the Material You palette on Android 12+.
- For E-Ink: a monochrome drawing, outlined black-and-white keys, and a no-motion switch that drops the press shade, the flash, the echo box and the layout-name pop-up (which can also be turned off on its own).
- The box that echoes what you typed can be turned off, or made as see-through as you like, so the keys under it stay readable; a hold that offers a choice is drawn in a colour of its own, so choosing never looks like typing.
The touch layout is one orthogonal ten-column grid with equal keys and no staggered rows. The
bottom row is the same on every page of the full-size layouts:
Ctrl · Meta · Alt · Tab · space · !# · 한글, with space three columns wide. The menu and the keypad
page do not have keys of their own — they are opened by holding the layout key and !#, which say so with a
small m and p in the corner. One cell is left over beside !#. Each layout puts what its own users reach for
there: 漢 for Hanja on 2-beolsik, Esc on QWERTY, Dvorak and Colemak — a real
KEYCODE_ESCAPE, for vi over ssh — and nothing on the rest. The two 12-key pages keep the same frame in
a different shape: the modifiers own the leftmost column, every Hangul key
is two columns wide, and the right-hand column carries backspace, space, then the period and Enter,
with !# and the layout key closing the bottom row. The cell beside Tab carries 漢 on both of them while Hangul is being typed, which converts on a
tap; under an overlay there is no reading to convert, so the cell is left empty there. Shift is
one cell up, beside Meta, on both pads and whatever the overlay — tapped for one key (Shift+arrow
selects) or held to stay down; a key that moved when the pad changed would be a key you had to look
for. The two cells above it toggle what the twelve Hangul keys show: 123 puts the
phone keypad's digits on them, Move the cursor cluster (arrows, Home/End, PgUp/PgDn, Ins, Esc,
Del); the same key puts the Hangul back. 천지인 puts Next to the left of ㅇㅁ and !? to its
right, which works the way the Hangul keys around it do: tapping moves through the characters on the
face, and dragging left or right picks the one written on that side. The full stop and comma are on
the key beside Enter.
Seven letter layouts share that grid:
| Layout | Shape |
|---|---|
| 2beolsik(2벌식) | the standard Korean full keyboard |
| QWERTY | English |
| Dvorak | English, in its own 7/10/9 shape; the three cells the top row does not need for letters carry Enter, backspace and the period, on the left |
| Colemak | English, on QWERTY's grid with every letter where Colemak puts it — nine across the top, ten on the home row, seven below; backspace takes the top-right cell the letters leave free. Holds follow the rows: symbols up top, digits on the home row, marks below |
| Spanish (Spain and Latin America) | QWERTY with ñ ending the home row, so backspace drops to the bottom letter row and the period sits beside space holding , ¿ ¡. á é í ó ú ü are a flick up on their vowels (ü down), and a still hold is still the digit |
| Portuguese (Portugal and Brazil) | QWERTY; the accents ride flicks the mark's own way — grave left, circumflex up, acute right, tilde/cedilla/diaeresis down (à â á ã on a, ô ó õ on o, ç under c); ª and º replace the group holds on a and o |
| Italian | QWERTY; the grave vowels are a flick left (è also é right) |
| Polish | QWERTY; each letter's mark is one flick — ogonek down (ą ę), acute/stroke right (ć ń ó ś ź ł), dot up (ż) |
| Greek | each letter on its PC position — ; ς ε ρ τ υ θ ι ο π up top (; is the Greek question mark, holding the ano teleia ·), tone vowels a flick right (ά …), the diaereses down (ϊ ϋ) and both together up (ΐ ΰ); the question mark flicks up to the ano teleia. Indonesian, Malay, Filipino and Swahili need nothing beyond QWERTY — use it as is |
| Hindi(हिन्दी) | InScript's positions as four letter rows plus a signs row, five rows in the same keyboard height. InScript's system rides the upward flicks: a matra flicks up to its independent vowel (ा↑आ), a plain consonant to its aspirate (क↑ख). The candra family shares one key (ॉ with ऑ ऍ ॅ), the sibilants share स (श up, ष down), the nasal signs share ं (ँ up, ः down), the danda doubles upward, and ₹ ़ ऽ ॐ have their own keys. Text commits as typed; your editor draws the conjuncts |
| Thai(ไทย) | Kedmanee's (TIS 820) positions as four letter rows — the first five-row page, every row one fifth of the same keyboard height. Kedmanee's Shift layer rides the upward flicks, key for key (ุ↑ู, ำ↑ฎ, ื↑์ …); the letters the ten columns squeeze out ride their kin — the obsolete ฃ ฅ under ข ค, the twenty-word ใ under ไ, ง below its fellow velar ก, and ฟ carries ฤ ฝ ๅ ฦ on its four ways. Thai digits ๑–๙ ๐ are the held digits, and ๆ sits beside space with ฯ held and ฿ a flick down. No Shift, one page. Pattachote, the 1960s ergonomic standard, is offered beside it as a page of its own, its 33 base characters in rows of ten with the second set on the upward flicks |
| Russian(Русский) and friends | ЙЦУКЕН's three rows plus a fourth for х ъ ж э б ю ё and the quotes — ё also a flick up on е. Ukrainian swaps in і ї є with ґ (also г flicked up); Russian also comes as a Phonetic page, the letters where their Latin sound is (screen only: Windows reaches five of its letters through dead keys, which this keyboard cannot compose yet); Bulgarian comes both ways — the Phonetic layout its phones use and the official BDS 5237, whose order is nothing like QWERTY's; Macedonian and Serbian sit on their standard positions |
| Arabic(العربية) | the 101 layout's positions, reading from the right, with the hamza row below and the Shift layer's tashkil riding the upward flicks where the 101 puts them; the period holds ، with « ؟ » ؛ on its flicks. Urdu follows its phonetic layout, the paired letters (ج↑چ, د↑ڈ …) riding the flicks |
| Georgian(ქართული) | its QWERTY-phonetic positions, one page, no case |
| Armenian(Հայերեն) | the Eastern typewriter positions; ռ rides ր, the everyday ligature և rides ե, and the full stop is Armenian's own ։ with ՝ ՞ ՜ ՛ on its flicks |
| Persian(فارسی) | ISIRI 9147's positions in ten columns, reading from the right, with Persian's own ک (U+06A9) and ی (U+06CC) and no Shift. The dotted twins ث ذ ظ ژ are a flick up on ت د ط ز — the dots sit on top — and the hamza family rides ا (آ أ إ ء) و ی ه. The digits under the keys are Persian's ۰–۹, the period carries « » ؟ ؛ with ، held, and the space bar flicks up to the ZWNJ, the half-space Persian spelling cannot do without. Backspace ends the first row and Enter the third, each at the right edge. A US physical keyboard types Persian too, on the Windows Persian layout's positions, Shift layer and Shift+Space ZWNJ included |
| Japanese Flick(フリック) | the 12-key pad Japanese phones use, flick-only: tap a key for its あ-column kana, flick left/up/right/down for い う え お — the same drag Cheonjiin uses, with the same hold guide. ゛゜小 turns the character before the cursor along its cycle (か→が, は→ば→ぱ, つ→づ→っ, a vowel to its small form); わ carries を ん ー on its flicks and 、。?! cycles. Digits ride the holds. Kana-to-kanji conversion is not in yet |
| Japanese Romaji(ローマ字) | QWERTY as it is; what you type becomes hiragana as you go — ka か, sha/sya しゃ, a doubled consonant っ (gakkou → がっこう), nn ん and a lone n before most consonants too, - ー, x/l for the small kana. The word composes under the cursor and commits on space or punctuation; backspace takes back one keystroke; a physical keyboard types the same way. Kana-to-kanji conversion is a dictionary's job and not in yet |
| Hebrew | the standard layout's positions, reading from the right (ק ר א ט ו ן ם פ across the top). Hebrew has no capitals, so there is no Shift and one page; backspace is two columns wide in the corner the letters leave free, and the period sits beside space — the comma held, geresh gershayim maqaf on its flicks. The text direction is the editor's own — the keyboard needs nothing special |
| French (AZERTY) | AZERTY as phones draw it, 10/10/6: azertyuiop / qsdfghjklm / ⇧ w x c v b n ⌫ . ⏎. The fifteen accented letters ride flicks the mark's own way — è left, ê up, é right, ë down, and so on for every vowel, æ œ to the right, ç ÿ down; the period flicks to « , » and a still hold anywhere is still the digit or symbol |
| German | QWERTZ in ten columns — y and z swapped, ü ö ä a flick up on u o a, ß a flick down on s (ẞ with Shift) |
| Turkish | Two: Q in ten columns — ü ö ğ a flick up, ş ç ı a flick down — and F, the Turkish Standards Institution's own 1955 arrangement, built around the letter frequencies of Turkish and still what Turkish typists are trained on. Shift follows Turkish casing in both, so i → İ and ı → I |
| Vietnamese (Telex) | QWERTY as it is; the Telex rules make the letters as you type — aa→â ee→ê oo→ô aw→ă ow→ơ uw→ư dd→đ, a lone w is ư, tones s f r x j (sắc huyền hỏi ngã nặng), z takes a tone off, and a mark or tone key pressed again gives the letter itself. The word composes under the cursor and commits on space or punctuation; backspace takes back one keystroke. A physical keyboard types Telex too. â ă ê ô ơ ư đ also ride flicks on their letters for anyone who does not know Telex |
| Cheonjiin(천지인) | 12-key: the elements ㅣ ㆍ ㅡ build the vowels and each consonant key cycles its group (ㄱ → ㅋ → ㄲ). A drag off a key (at least 18 dp) types at once what the taps would reach; a shorter slide is still a tap. Off a consonant: left the plain letter (ㄱ), right the aspirate (ㅋ), down the tense one (ㄲ), and nothing above — the digit is held for, not dragged to. A group with no tense letter (ㄴㄹ, ㅇㅁ) has no cell below it either. Off a vowel key the direction points at the letter: from ㆍ, left ㅓ · right ㅏ · up ㅗ · down ㅜ; from ㅣ, left ㅔ · right ㅐ · up ㅒ · down ㅖ; from ㅡ, left ㅝ · right ㅘ · up ㅚ · down ㅟ. A dragged vowel goes on combining (drag ㅗ, tap ㅣ, and it is ㅚ). Holding a key raises a guide of those cells with the key's digit in the middle, and waits: drag to one and lift to type it, or lift without moving for the digit. The ten Hangul keys sit where a phone keypad's do and hold what it holds — 1–9, and 0 under ㅇㅁ. Two taps of one key in a row are one cycle, so a pause — or the Next key — starts the next letter |
| Naratgeul(나랏글) | 12-key: a consonant block with 획 adding a stroke (ㄴ → ㄷ → ㅌ) and 쌍 doubling (ㅅ → ㅆ); a vowel key pressed twice reaches its pair (ㅏ → ㅓ, ㅗ → ㅜ) and 획 iotates it (ㅏ → ㅑ, ㅗ → ㅛ). Its twelve keys sit where a phone keypad's do, so holding one types what a phone keypad holds: 1–9, 0 under ㅡ, * and # either side |
| Arrows | the cursor cluster on the 12-key frame — Home ↑ PgUp / ← Ins → / End ↓ PgDn / Esc Del — as a layout rather than an overlay, for when you are moving around a document rather than glancing at an arrow mid-word. Shift sits beside Meta: tap it for one key, so Shift+arrow selects, or hold it to keep it down. Off until you turn it on in settings |
| Keypad | the phone keypad on the same frame: 1–9, * 0 # typed with a tap, and the calculator set on hold, with Shift beside Meta as on Arrows. Off until you turn it on in settings |
| IPA | the International Phonetic Alphabet, for writing a pronunciation beside the word. Each symbol sits at the letter it sounds like, the way X-SAMPA reads a keyboard — e types ə (and holds ɛ ɜ ɚ ɝ), a types æ (holding ɑ ɐ ɶ), t θ, d ð, s ʃ, z ʒ, n ŋ, j ʤ, r ɹ, v ʌ, o ɔ, u ʊ, i ɪ — and every key holds the rest of its family, so the alphabet is one page and a hold deep. The last cell of the bottom row carries the stress and length marks ˈ ˌ ː ˑ. Shift is not a second set of symbols but the plain letters where QWERTY has them, because a transcription is full of ordinary p, t, k, s; the same key then types /, holding [ and ]. A physical keyboard types both pages the same way. The 🔎 key at the bottom is the way to everything the page does not hold: tap it and type what you call the symbol — palm, schwa, fricative, or the X-SAMPA code T — and the matches appear above the keys to pick from; hold it for the families instead (vowels, plosives, nasals, fricatives, affricates, approximants, laterals, trills and taps, clicks and implosives, stress and length, diacritics), where two taps reach any of them. Off until you turn it on in settings |
The layout key — captioned with the layout it goes to next (2BS, QWE, DVO, CJI, NRG,
ARW, NUM, IPA, in bold capitals) — walks the layouts you ticked in settings, in the order you put them there, naming
each one as it arrives. Holding it opens the menu page. Hanja conversion is the 漢 key: on the
2beolsik letters page, on both 12-key pads while Hangul is being typed, and on the keypad page.
Holding a letter key types a character instead of repeating it. The alternates come in three groups
— 1234567890, !@#$%^&*; and _-:='"? — one to a row. Their sizes are 10/9/7, which is QWERTY's
row shape and Dvorak's 7/10/9 read in a different order, so Dvorak carries the same three groups
rotated to fit: rows 1-2-3 become 3-1-2, and no group is split or padded. Space, enter, backspace
and the arrows have no alternate and keep repeating when held.
A physical keyboard is not affected by any of this: it keeps its own layout, and the on-screen choice only changes what the on-screen keys do.
The layouts, as they are:
2beolsik(2벌식) — 漢 beside !# converts what you just typed:
QWERTY — Esc beside space, for vi over ssh:
Dvorak — the same bottom row, its own 7/10/9 letter block:
Colemak — the same bottom row, the letters in Colemak's places, backspace top right:
Spanish — ñ on the home row, backspace below it, the period beside space with , ¿ ¡:
Portuguese, Italian, Polish, Vietnamese, German (y/z swapped) and Turkish keep QWERTY's shape and hold their own letters; Vietnamese types through Telex:
A US physical keyboard types every scripted layout. While a layout is selected, a plugged-in or Bluetooth US keyboard follows that language's own Windows layout: Greek, Hebrew, Thai (Kedmanee's number-row letters and Thai digits on Shift included) and Hindi InScript remap wholesale; German adds ü ö ä ß on its right-hand keys; French puts é è ç à ù on the digit row as AZERTY does; Turkish types ı from the i key; Spanish adds ñ, Portuguese ç, Italian its accented vowels; Persian and Korean have mappers of their own, and Vietnamese and Japanese type through their composers. Keys whose output is plain ASCII, and dead keys, stay with the editor for now.
Languages whose own letters are too frequent for flicks got a fourth letter row — German (ü ö ä ß with the German quotes and €), Turkish (ü ı ö ş ğ ç), Polish (ą ć ę ł ń ó ś ź ż), Portuguese (ç ã õ and the accented vowels), French (the ten commonest accents), Greek (the tone vowels) and Persian (the letters the ten columns had squeezed out) — five rows in the same keyboard height, each row a fifth of it. The flicks stay, so both paths type the same characters. Spanish, Italian, Vietnamese, Hebrew and Japanese Romaji stay four rows, where flicks are enough.
A language's own letters are flicks, not lists. Drag a key the mark's own way — grave left, circumflex up, acute right, cedilla down — and the letter types at once, exactly the drag the Korean Cheonjiin and Japanese flick pads use. Hold instead and the four-way guide appears around the key with the digit in the middle: lift where you want, or lift in place for the digit. The digit and symbol holds never crowd a language's essentials onto one key — where a language needs a fifth character, the digit is the one that goes (Portuguese ª º). The period key carries each language's own quotation marks on its sideways flicks («» „“ “”), its currency down (€ ₺ ₫ ₪), and Spanish's ¿ ¡ up and right. (A strip on the row above still appears for the rare key whose extra characters outnumber the four directions.)
French AZERTY — its own 10/10/6 shape, and the strip that makes fifteen accents workable:
Cheonjiin(천지인) — Shift beside Meta, 漢 beside Tab, and Next and !? either side of ㅇㅁ:
Naratgeul(나랏글) — 획 and 쌍 on the bottom letter row, Shift beside Meta, 漢 beside Tab:
Arrows — the cursor cluster as a layout of its own:
Keypad — the digits on a tap, the calculator set on hold:
The two cells beside Tab turn the twelve Hangul keys into the phone keypad's digits, or into the cursor cluster, and back again:
The !# key opens the special-characters page, where every key commits its symbol. Holding a key
types its alternate immediately — there is no popup to aim at and nothing to drag to. The top row
holds the digits it shares keys with on a physical keyboard, so ! holds 1 and ) holds 0;
; holds a comma and : a full stop; holding ` types ~, and holding - types +. Esc sits beside the space bar:
Holding !# opens the special-keys page: a right-hand keypad plus the special keys. It opens
with Num on, showing digits — num lock means digits here as it does on any keyboard. The
digits and + - = . commit text; Esc, Prt, Scr, Brk and Menu send key events:
Turning Num off swaps the keypad for arrows and navigation — Home/End, PgUp/PgDn, Ins, and
forward-delete:
Fn swaps the whole page to the function and media keys. F1–F12 send key events; F13–F15 (which
have no Android key code), the media keys and Back stay muted:
Holding the layout key opens it. The right half is the editing hand — copy, cut and paste down its near edge, the arrows in a cross with select-all at their centre, the jump keys around them, and settings in the corner a thumb reaches without looking. The left half is everything about the keyboard itself.
| Key | Does | |
|---|---|---|
| Undo | undo | undo the last edit |
| Redo | redo | redo it |
| Date | date | insert the current date and time as text |
| Emoji | emoji | not built yet — drawn muted |
| Clip | clipboard | opens the clipboard list, the same one the action bar's Clip opens |
| Copy | copy | copy the selection |
| Cut | cut | cut the selection |
| Paste | paste | paste the clipboard |
| SelA | select all | select everything in the field |
| Size− / Size+ | height | shrink or grow the keyboard by two percentage points a press, remembered per orientation |
| Switch | switch keyboard | open the system input-method picker |
| Manage | manage keyboards | open the system screen for enabling keyboards |
| Flt | floating | turn the floating panel on or off |
| 1Hand / Full | one-handed | not built yet |
| Theme | theme | walk the colour scheme: system → light → dark |
| ★1 / ★2 | custom | not built yet |
| Set | settings | open ReteKey's own settings |
| ← ↑ → ↓ | arrows | move the cursor; they repeat when held |
| Home / End | line ends | jump to the start or end |
| PgUp / PgDn | page | jump a page |
| Ins | insert | the Insert key |
| Del | forward delete | delete forward |
| 0 / F10 / Del | beside space | whatever the current mode is missing: the keypad's zero, the tenth function key, forward delete |
The muted keys are drawn but do nothing: they are the places the features will go, marked so the page does not shift under you when they arrive.
A layout can be a text file you wrote. It names the letters on the three rows of keys and what each key holds; the bottom row, Shift, backspace, Enter and the layout key stay the keyboard's own, which is what keeps the format small enough to promise:
retekey-layout 1
name: Greek phonetic
cap: grk
row: α β γ|ϝ δ ε|έ ζ η|ή θ ι|ί κ
row: λ μ ν ξ ο|ό π ρ σ|ς τ
row: υ|ύ φ χ ψ ω|ώ
Install it by selecting the text anywhere and choosing Share → Install a ReteKey layout, or by
saving it with a .rkl extension and opening the file. ReteKey shows what arrived — its name, its
key cap, its first row — and which layout it would replace, and installs nothing until you press
Install. Sharing to plain ReteKey does something
else entirely — it keeps the text to type later — so a note that happens to begin with
retekey-layout is a note. Then tick the layout in Settings → Keyboard layouts, in the etc
group, and the layout key will walk to it wearing the three letters from cap:.
One layout at a time; installing another replaces it. Settings shows the whole example above, selectable, so the format can be copied off the screen you are already on.
The full guide: writing a ReteKey layout file — every line, what a key may type, how many keys fit on each row, what Shift does, what happens to a file it cannot read, and what a layout file deliberately cannot do.
The menu page's ❐ floating key turns the keyboard into a translucent panel that floats over the app instead of docking to the bottom edge — the shape a tablet in landscape wants, where a full-width keyboard is both too wide to type on and too tall to see past.
The panel is confined to one half of the screen, and which half that is follows the screen's shape: a screen wider than it is tall splits left and right, and a screen taller than it is wide splits top and bottom. Halving a phone held upright down the middle would leave two columns too narrow to type in, and halving a tablet held sideways across would leave two strips too short to read, so the panel always takes the half worth having. The other half stays yours. Its title bar carries, left to right:
| Key | Does |
|---|---|
| ☰ | drag to move the panel, anywhere within its half — dragging any free part of the bar does the same |
| ‹ / › | send the panel to the other half, mirrored about the centre line |
| ✕ | leave floating mode and go back to the ordinary keyboard |
| ⇲ | drag to resize the panel |
Dragging stops at the centre line: crossing over is the arrow key's job, and it reflects the panel rather than dropping it somewhere new, so a panel hugging the left edge comes back hugging the right one. Everything outside the panel still belongs to the app — the keyboard takes no touches there and the app is not resized to make room for it. The mode and the panel's placement are remembered, and a rotation rescales the panel instead of stranding it off-screen. How solid the panel is, is yours to set — the opacity slider in settings runs from barely-there to fully solid.
The menu page's Memo key opens a notepad above the keyboard. The window takes the whole screen while it is open, and everything above the keys is the note — translucent, so the app you are copying from is still readable behind it. What you type goes into the note rather than into that app, Hangul included: the notepad composes syllables itself.
The editing keys work on the note too, not on the app behind it: a soft Ctrl with A, C, X, V, Z or Y (on the Korean page, the key in that letter's place — Ctrl+ㅊ is Ctrl+C), and the action bar's arrows, Home/End, page keys, word and select-all, cut, copy and paste. Shift with a movement key selects, and Ctrl with an arrow jumps a word.
A note's first line is its stamp and its title: 20260713-1448, written the moment the note is
made and never edited, with the title beside it. Everything from the second line down is the body,
and select all takes the body alone — the line that names the note is not something anyone
means to select.
The list behaves like a file manager's details view:
| Date / Title | press a column to sort by it; press it again to turn the sort around (▲ ▼ shows which) |
| checkbox | one per row, and one in the header that takes all of them or none |
| ▲ ▼ | move a note up or down, for an order of your own |
| New | start a note, stamped now |
| Del / DelAll | delete the ticked notes, or every one |
| List | back to the list from a note; Close puts the keyboard back |
| Cp Cut Paste Del Un Re | copy, cut, paste, delete, undo, redo — on the selection, or on the whole field when nothing is selected. Undo is the notepad's own, so it works the same on Android 4 as on 16 |
| Send | types the note into the app you were writing in, at its cursor — the selection if you made one, the whole note otherwise. It goes out as typed input rather than a paste, so a terminal or a remote desktop takes it like anything else the keyboard types. The notepad closes, since what you sent is behind it |
| pinch | two fingers set the text size on either screen, from 60% to 300%, and it is remembered |
The links sit on one row and wrap onto a second only when the screen is too narrow for them all, rather than running off the edge.
Notes are kept in the keyboard's own storage and written through on every change, because a keyboard can be torn down between two keystrokes.
Pressing the 한자 key — the one on the special-keys page, or a physical key you assigned — converts Korean to Hanja:
- With a selection, the whole selection is converted.
- With no selection, the reading immediately before the cursor is converted, preferring the
longest match, so
학교becomes學校rather than converting교alone. - Pressing it on Hanja converts back to its reading, including whole Hanja words.
Candidates appear in a floating panel of their own, in a paged grid of nine, with each 훈음 gloss beside its character (家 집 가), wrapping onto a second line when the column is narrow. Drag the panel by its bar, resize it, and it is as see-through as your floating keyboard's opacity setting says — it is the same frame. It replaces the keyboard while it is up and gives it back afterwards, and it comes up the same way when an external keyboard is doing the typing with no keyboard on screen at all.
Tap a candidate, or press its number key 1–9; ‹/› in the header — or ←/→ and
PageUp/PageDown on a keyboard — turn the page, and ✕ leaves without converting, as Esc
does. What you typed stays as you typed it: it is a way out, not an undo.
The dictionary is bundled and searched where it lies: the tables are sorted files, memory-mapped out of the APK and bisected per lookup, so they cost the Java heap nothing and the kernel is free to drop their pages under memory pressure. Parsing the same data into hash maps used to cost about 2.6 MB of heap. See THIRD_PARTY_NOTICES.md for provenance and licences.
A consonant on its own followed by the Hanja key opens the row of symbols that consonant has stood for since the KS X 1001 tables — ㅁ the general symbols, ㅅ the Greek alphabet, ㅇ the circled numbers, ㄹ the units, ㄷ the mathematics, ㄴ the brackets, and so on for all fourteen. They arrive in the same candidate window as Hanja, and each one is glossed with its code point, since a symbol has no reading and the number is what you would look it up by.
For a character with no key and no consonant to reach it, the menu page's Uni key opens a code point entry. It is a floating panel over what you are writing — drag it, resize it, and it is as see-through as your floating keyboard's opacity setting says:
Type hex digits, up to six of them, so everything to U+10FFFF is within reach; the code and the character it names are shown along the top. The pad is two rows: the digits, then A–F with Bksp, Cancel and OK beside them. Nothing is typed until OK, because the code is a composition — so Cancel, or the panel's ✕, leaves at any point with the document untouched and the previous keyboard back on the layout you left it. With a hardware keyboard the panel comes up just the same, and the digits, backspace, Enter and Esc all work from the physical keys. A physical key can be bound to the entry itself in settings — none, one, or several, as with 한/영 and 한자.
With a Bluetooth or wired keyboard, letter keys are mapped to 2-beolsik jamo while Korean mode is on. In settings you can assign several physical keys per function:
- KO/EN toggle —
Shift+Spaceand the keyboard's own 한/영 key are registered from the start, along with theLANGUAGE_SWITCHcode some makers send instead. Add your own —Right Ctrl, say — or remove any of them. - Hanja — the keyboard's own 한자 key is registered from the start;
F9orRight Altare a tap away if you would rather have those. - Unicode entry —
Ctrl+Shift+Ufrom the start, the combination Linux input methods have used for years. It opens theU+code point entry; hex digits build the character, Enter commits it, Esc leaves.
What is registered by default is registered, not fixed: the list shows those keys like any other
and Remove takes one off for good. Pressing the shortcut is all the registration takes — the
whole combination is read, Shift+Space included, and while the screen is waiting the keyboard
stands aside so a key that already does something can still be offered.
A binding may be a lone key or a modifier chord; a modifier pressed on its own registers as itself.
Modifier chords the IME does not claim are passed through, so application shortcuts keep working,
and a soft Ctrl plus a letter sends a real chord — terminals receive the control code, editors
run select-all/copy/paste/undo.
Terminals. A terminal is a program on the other side of a pipe, not a text box: it has no
composing region to underline and no buffer to delete out of. So the syllable you are building is
shown on the keyboard's own strip (on a floating keyboard, in its title bar) and sent to the terminal once it is finished; nothing is
ever taken back. Backspace inside a syllable edits the strip, and a Backspace with nothing on it
is a real backspace key. (A setting, Terminals on the general page, brings back the older
way: the syllable is typed into the terminal as it grows and taken back with each stroke, using
the terminal's own erase character.) So Korean works in vim and over ssh,
in Termux and in the SSH clients, in
either of Termux's input modes. (Password fields are composed into too; a field being private
means this keyboard never reads it and never remembers it, which is unchanged.) A physical keyboard
types Korean into a terminal the same way; the keys the layout does not use — Ctrl chords, arrows,
Enter, Esc, Tab — still go straight to the program, after the syllable before them. The action bar's editing
keys are actions, not keystrokes: its Paste puts the clipboard in — in a terminal by typing
it out, since a shell reads Ctrl+V as quoted-insert and pasted nothing at all in Termux and Termius
— and its Copy, Cut and Select all say plainly that a terminal has no selection instead of sending
Ctrl+C, which interrupts whatever is running. The keyboard's own Ctrl with a letter is the
other half of that pair: it is sent as the chord it looks like, so Ctrl+C is Ctrl+C and Ctrl+V is
Ctrl+V, straight through to the program. Remote desktops keep the chord for the bar too, because
there is no editor behind their connection to act on and Ctrl+V is a real paste on the far side. With the
older way turned on, a syllable left unfinished for a second and a half is let go of, so that a
terminal's own arrow buttons, which the keyboard cannot see, do not drag it to a new place.
The settings screen uses stock controls only and follows the system theme:
- Colour scheme — System, Light, or Dark. System is the default and follows the device; the other two hold whichever you pick, for the keyboard and for the app's own screens alike. On Android 12+ the colours themselves still come from the system's Material You palette.
- Portrait settings and Landscape settings — the keyboard's height, the action bar's height, the gap between keys and the floating panel are remembered separately for a screen held upright and one held sideways, and each screen has a page of its own. Which orientation you are setting is the page you are on.
- Keyboard height — how much of the screen's height the keyboard takes, from 1% to 50%. The number on the slider is the number on the screen: 25% is a quarter of it. A keyboard set before this was a percentage keeps exactly the size it had.
- Action bar height — the bar's own height on this screen, from 2% to 25% of the screen's height: the same unit as the keyboard height. The bar is part of the keyboard's height, not added to it, so a keyboard at 30% with a bar at 8% leaves the keys 22%, and the bar never takes more than half the keyboard. Until you set it, it is the height it always was.
- Gap between keys — how much space lies between two keys on this screen, from none to 40% of the width of one key cell (a key one column wide; wider keys get the same gap). The gap is not part of any key: a touch between two keys presses nothing. Until you set it, it is the gap it always was. Back to usual forgets either setting.
- Floating keyboard — whether to use it on this screen, and how solid it is (1–100%).
- Layout (portrait) and Layout (landscape) — which layouts the layout key walks through, and their order, kept separately for each screen. They have pages of their own rather than sharing the settings above: the list is one row per layout, and there are many. The layouts you have not enabled are grouped by language and each group starts shut, so the page opens as a list of languages rather than of layouts. Each of these pages also carries which layout a plugged-in or Bluetooth keyboard follows while one of them is on the screen — the two need not match: type Dvorak on the screen and Colemak on the keys, or leave the keys as their caps are printed. Each layout answers to itself unless you say otherwise — Colemak on the screen means Colemak on the keys — and a layout with no physical form of its own, like 천지인, answers to the one its language has. A layout's three-letter abbreviation says which side it is on: capitals for the screen, as the layout key paints them, lower case for the keys. Only layouts with more than one physical form are offered: English's three, Turkish's two, Bulgarian's two and Thai's two; Korean has one, 2벌식. The pairing is kept per screen too, like the list it sits under.
- Key-press feedback — visual, vibration, and sound strengths, each 0–100% (30, 10 and 10 by default).
- Key auto-repeat — on/off, start delay, and repeat interval.
- Physical keyboard shortcuts — register and remove KO/EN and Hanja keys.
A Back to main screen button at the top returns to the app's main screen at any time.
What you just typed, over the keys. A finger covers the key it presses, so the character appears in a box at the top of the keyboard for as long as the keystroke's blink lasts. It is a setting — Show what was typed over the keyboard, under feedback in settings — and its opacity is one too, set to 60% so the row underneath reads through the box; the character itself stays legible however faint the box is. A character you chose rather than pressed — held for, flicked to, picked off the strip of alternates — is echoed in a different colour from a pressed key, and the strip itself is painted in that colour, so a hold that landed on the wrong alternate is visible as such at a glance.
Off by default. It has a screen of its own — Action bar settings, from the app's main screen or from the button in settings — where you turn it on and decide what it carries. Drag a row by its ≡ handle to order it, nudge one with ▲ ▼, take one off with ✕, and Default order puts the shipped bar back. It sits above the keys, scrolls sideways when the list is longer than the screen, and shares the keyboard's height with them: the bar is part of the keyboard, not an addition to it, so turning it on does not push the keyboard further up into the app. If the rows feel short with it on, raise the keyboard's height.
Stage one carries the things that need nothing remembered:
| Slot | What it does |
|---|---|
| Word | selects the word around the cursor — the run between spaces or punctuation, in Korean as in English |
| All | select all |
| Cut / Copy / Paste | the editor's own three |
| ← → ↑ ↓ | move the cursor; hold to repeat |
| Home / End / PgUp / PgDn | jump; hold to repeat |
| Clip | the clipboard list, below; it closes with the keyboard |
| Sym | the symbols page, from wherever you are |
| Memo | raises the notepad |
Two kinds of slot are yours to make:
- Text — anything you type into the box: a signature, an address, a command you keep retyping. A tap types it, and holding it repeats, the way holding a key does.
- Key combination — modifiers plus a key: Ctrl+B, Alt+F4, Esc on its own. A tap sends it once, and holding presses the key and leaves it down until you press the slot again, which is how Shift stays held for a selection.
Give either one a label and that is what the bar says; leave it blank and it shows the text itself, or the combination's name.
Every key on the bar chords with the modifiers that are down, wherever they are held: the bar's own
Ctrl/Alt/Meta slots, the keyboard's Ctrl, Alt, Meta and Shift, and a physical keyboard's. Hold Ctrl
on a keyboard and tap the bar's → to jump a word; arm Ctrl and tap a text slot of c to copy —
a text slot of one letter or digit goes out as that key, and Shift alone capitalises it. A one-shot
toggle is spent by the press, so a held arrow keeps its Ctrl for every repeat.
Macros are the stage after this one.
Word needs the editor to say where the cursor is. A few — some terminals — never do, and there the button does nothing rather than selecting the wrong thing.
Clip opens what the keyboard remembers of what was cut and copied — through its own bar and in any other app, since it follows Android's clipboard. It does not only wait to be told: every time the keyboard comes up it reads the clipboard, so something copied in a browser is already in the list by the time you reach the field you meant to paste it into. (Android tells an app about a copy only while it is on screen, and several ROMs stop telling a keyboard that is not, which is how the list and the clipboard used to drift apart.) The clip on the clipboard now is at the top when the list opens, and a clip you pick becomes the clipboard as well as being typed. Tap a clip to paste it; ☆ pins it so it is never aged out; ✕ forgets one — and a clip you forget stays forgotten even while it is still on the clipboard — and Clear forgets all the unpinned ones.
Kept in ReteKey — text that was never copied. Any app's Share menu (and a text selection's own menu) can send text straight to ReteKey: it is kept in the keyboard's own storage under a Kept heading at the top of the same list, and tapping it types it into whatever you are writing in. The system clipboard is not read and not written for these — which is the point. On some makers' ROMs a keyboard that is not even on screen reads every copy and files it in a history you cannot turn off; text that was never copied cannot be read that way. Nothing extra is asked of you and no permission is involved: a share is the sending app's own decision. Kept text ages out on your clock — 10 minutes, 1 hour, 1 day, or until you remove it — in settings, under Clipboard and kept text, where you can also turn Follow the system clipboard off entirely: then the keyboard never looks at the clipboard at all, and sharing is the only way text reaches it.
A layout of your own. Share a layout file to ReteKey the same way and it is installed as a keyboard page — one at a time, replacing whatever was there. A file is text, small enough to write in a message:
retekey-layout 1
name: Greek phonetic
cap: grk
row: α β γ|ϝ δ ε|έ ζ η|ή θ ι|ί κ
row: λ μ ν ξ ο|ό π ρ σ|ς τ
row: υ|ύ φ χ ψ ω|ώ
Each cell is the character the key types; anything after a bar is what the key holds. Three rows, up to ten keys across; everything else — Shift, backspace, Enter, the bottom row, the layout key — is the keyboard's own and is not in the file. That is deliberate: a format published in a release has to be read for ever, so this one says as little as it can. Lines it does not know are ignored rather than refused, so a file written for a later version still works. Turn the layout on like any other, in settings; Your own layout there shows what is installed and removes it.
What it will not do matters as much as what it will:
- Nothing is recorded from a password or other sensitive field, nor anything an app marks as sensitive when it copies it (password managers do). Those must not survive the keystroke, and a clipboard list is exactly where they would.
- Twenty unpinned clips are kept, newest first; pinned ones do not count towards that.
- Copying the same thing twice moves it up rather than storing it twice.
- A clip longer than 4000 characters is truncated: this is for pasting a line back, not for storing a document.
- It lives in the app's own private storage, and nothing is sent anywhere.
The keyboard resolves its colours from the device theme rather than hardcoding them:
- The user's own choice comes first: System, Light or Dark, set in the settings screen or by the menu page's Theme key, which walks the three in turn.
- Left on System, the device's light/dark mode is honoured (
Configuration.UI_MODE_NIGHT_MASK). - Colours are assigned by Material role — background as surface, keys as an elevated surface, labels as on-surface, active keys as primary, and the press effect as a primary state layer.
- On Android 12+ the user's Material You palette is used, so the keyboard matches their theme; older versions fall back to a tuned light/dark palette.
The keys, the long-press popup, the Hanja candidate window, and the floating panel all share one palette. The launcher and settings screens follow the same choice through the platform's own DeviceDefault themes, so a forced light or dark applies to the whole app.
There are two builds of the same app. Install whichever your device takes; they carry the same package name, so one replaces the other.
| Build | Runs on | APK |
|---|---|---|
| modern | Android 9 – 16 (API 28–36) | retekey-<version>.apk |
| legacy | Android 4.0 – 16 (API 14–36) | retekey-<version>-legacy.apk |
Both are built from the same source, target API 36, and behave identically wherever the platform lets them. The split exists because reaching down to Android 4 means taking an older road in a few places, and there is no reason to make a current phone take it.
Three things, all of them on the device's side of a version check rather than in a separate codebase:
| On older Android | What happens | From |
|---|---|---|
| Vibration | one fixed strength instead of a strength dial — VibrationEffect is API 26, and the older call only takes a duration |
below API 26 |
| Sliders | the keyboard-height and opacity sliders run from zero internally and are offset — SeekBar.setMin is API 26 |
below API 26 |
| Deletion | code-point deletion is not available, so the UTF-16 fallback already used for older editors handles it; a syllable or emoji still disappears in one press | below API 24 |
| Shadows | the Hanja window has no drop shadow — PopupWindow.setElevation is API 21 |
below API 21 |
| Theme | the screens use the platform's own DeviceDefault rather than Material | below API 21 |
| Panel bar | the floating panel's bar says Menu < > X Size in words where the modern build draws ☰ ‹ › ✕ ⇲ — the glyph font those come from arrives with API 21 | below API 21 |
Everything else — the five layouts, the 2-beolsik and 12-key composers, Hanja conversion, the
floating keyboard, physical keyboards, auto-repeat, theming — is the same code on both. The app
uses no java.time, no java.util.function, and no library desugaring, which is why the legacy
APK is smaller than the modern one rather than larger.
Both work in full on the legacy build, and this is worth spelling out because both are the kind of feature that usually needs a newer platform.
The notepad (Memo) — every part of it: the list with its sortable columns, the checkboxes and
the header one that takes all of them, the ▲▼ reordering, delete and delete-all, the forced stamp
line and the title beside it, writing Hangul into the note through the keyboard's own composer, and
the second row of editing links (Cp Cut Paste Del Un Re). Nothing in it is gated on a
version. Two details are worth naming:
- Undo and redo are the notepad's own.
TextView's built-in undo is API 23, which the legacy build cannot use, so the notepad keeps a history of its own — the same sixty steps, the same behaviour, from Android 4.0 upward. - The clipboard goes through
ClipboardManager.setPrimaryClip, which has been there since API 11, so copy, cut and paste move text between the notepad and other apps exactly as on a new phone.
The one difference is invisible: the note fields ask the system not to raise a keyboard over them
(setShowSoftInputOnFocus, API 21). Below that the call is skipped — and nothing happens anyway,
because an IME window holds no system focus for a keyboard to be raised into.
Direct code-point entry (Uni) — works in full: the floating pad, up to six hex digits, OK
committing and Cancel leaving with the document untouched, the same from a hardware keyboard.
Codes above U+FFFF are inserted correctly as surrogate pairs; whether you then see the character
is the device font's business, and an Android 4.4 phone has no glyph for most emoji or for the
later CJK extensions, so it will show a box while holding the right character. Paste it somewhere
with a fuller font and it is there. The pad's own labels are all ASCII, so they draw everywhere.
Hanja conversion is the same code on both builds too, including the floating candidate panel.
The two pictures below are the real views drawn by the legacy APK on an Android 4.4.2 image — the
notepad with both rows of links, and the code-point pad in its floating frame. They also show the
two limitations named above: the panel bar reads Menu < X Size in words, and the character beside
U+2318 is missing because that font has no glyph for it, though the code point itself is what
gets typed.
![]() |
![]() |
Android 4.0 is where it stops, and the reason is structural rather than stubborn. Below API 11
there is no InputMethodSubtype (so no 한/영 mode), no KeyEvent.isCtrlPressed or F-key codes (so
no physical-keyboard support), no Insets.touchableRegion (so no floating keyboard); below API 9
there is no getSelectedText (so no Hanja on a selection) and no MotionEvent.getActionMasked.
Reaching Android 2 would not be a port of this keyboard; it would be a different, much smaller
one.
Android 12 (API 31) and newer still take the Material You palette on both builds; below it, both fall back to the tuned light/dark palette.
These are screencaps from a KitKat emulator running the legacy APK — not renders, and not the modern build with an older label on it.
The labels read as words rather than glyphs here, and that is the legacy build doing its job: a
screenshot of an earlier attempt showed the menu key as an empty cell, because Android 4.4 has no
font for ☰. Below the version where those glyphs became dependable, the keys say Menu, Copy,
Bksp, Lang instead of drawing nothing.
| Version | How | |
|---|---|---|
| Verified by an automated lane | Android 13 — API 33 | AOSP x86_64 emulator, IME lifecycle test |
| Verified by hand | Android 13 — API 33 | Galaxy Note20, the project's primary device |
| Verified by hand | Android 13 — API 33 | the legacy build, on the same emulator |
| Verified by hand | Android 4.4 — API 19 | the legacy build on a KitKat emulator: Korean types, 한자 converts, settings opens |
The rows above are the versions an actual run has covered. The rest of each range is supported by construction: every platform call the app makes exists at that build's floor, and the ones that do not are behind an explicit version check. Lint agrees at both floors, with no errors.
Nothing in this part is needed to use the keyboard.
The app uses Android's standard InputMethodService entry point. The input core is deliberately
Android-free so it can be unit-tested on the JVM: event normalisation, semantic jamo, the 2-beolsik
hardware mapping, dispatch disposition, immutable transition plans, checked editor execution,
Unicode-safe deletion, the Hanja tables, and the key-repeat and shortcut settings.
Cursor state follows the AOSP LatinIME model: a passive cache that the editor's own
onUpdateSelection always overrides, tolerating unknown (-1) selections. The keyboard never
refuses input because its idea of the cursor disagrees with the editor's.
The keyboard is drawn on a canvas. The unpressed keyboard is rendered once into a cached bitmap and
reused until the layout, highlight state, size, or theme changes; a key press only tints one key, so
the raised styling costs nothing per frame. That cache is RGB_565 — the keyboard is opaque, so it
needs no alpha channel, and half the bytes per pixel matters on a tablet.
- Java / JDK 21 (the toolchain the F-Droid buildserver provides; bytecode target 17)
- Android SDK 36 (
targetSdk 36;minSdk 28modern,minSdk 14legacy) - Android Gradle Plugin 9.2.1, Gradle wrapper 9.4.1
- R8 minification; no third-party runtime libraries
Local builds require JDK 21 and Android SDK platform 36 with Build Tools 36.0.0. Use the checked-in wrapper rather than a system Gradle:
./gradlew testModernDebugUnitTest assembleModernDebug # Android 9+
./gradlew assembleLegacyDebug # Android 4.0+A signed release build additionally needs a local keystore.properties; without it the release
variant is simply unsigned.
A manual for building an Android input method: how an IME is put together, a minimal working one you can copy, the reference implementations worth reading, and then the parts that are hard — each with the failure from this project that shaped it.
It covers:
- how an IME is structured — the service,
method.xml, input and candidates views, how the system finds/enables/selects it, and the data flow from a touch to the editor; - a minimal working IME — manifest,
method.xml, and a service you can paste and run, plus the order in which to grow it; - reference implementations and where to get them — AOSP LatinIME and the
SoftKeyboardsample, with clone commands and what to read in each; - the
InputMethodServicelifecycle, including the real measured teardown order, which is not the tidy one the documentation implies; - the
InputConnectioncontract, and why insertion and relative deletion never need to know the cursor position; - the editor is authoritative — the passive cursor cache that follows from it, and why a keyboard must never refuse input over its own bookkeeping;
- editor kinds: terminals (
TYPE_NULL, selection-1) and everything that breaks in them; - composing text, hardware keyboards and modifier chords, and the candidates view;
- drawing a custom keyboard cheaply, theming it to the system and Material You palettes, and keeping settings live on a running keyboard;
- what a headless emulator can and cannot prove about an IME;
- a detailed anti-pattern chapter — real failures from this project, each with what was built, what went wrong, the fix, and the resulting rule, with wrong-versus-right code;
- a pre-release checklist.
The format a layout you write yourself is in, line by line: what each line means, what a key may type and hold, how many keys fit on each row, what Shift does, the two ways to install one, the four reasons it can be refused, and what the format deliberately cannot say. Every example in it is parsed by a unit test, so the guide cannot drift away from the app that reads it.
The English version is canonical and the Korean version is its translation; the two link to each other. It is a living document, updated whenever ReteKey's IME implementation changes, so it describes working code rather than intentions.
Design RFCs, the verification catalog, decisions, and the changelog live in a private companion repository and are not part of this public surface.
rete is Latin for net, and -key is just a key.
The intended pronunciation is the Latin one: RAY-teh (two syllables, rē-te; the first vowel is
the long e of they, and the final e is pronounced, never silent).
If you would rather say it the way English usually treats this word, that is fine too. English borrowed rete as an anatomical term and pronounces it REE-tee, so "REE-tee-key" is a perfectly good reading. Say it however you like; the keyboard does not mind.
MIT — see LICENSE. Bundled third-party data and ported code are credited in THIRD_PARTY_NOTICES.md.

































