Skip to content
WM KeyboardWM Keyboard
Accessibility

Deep links & launcher shortcuts

Every settings screen and setting has a wmkeyboard:// address. The full route table, the launcher shortcuts, the addon links, and the file associations.

WM Keyboard opens straight to a specific screen from three different starting points: a long-press on the app icon, a wmkeyboard:// link, or a file you tap in a file manager. Every settings screen has a link of its own, and so does every individual setting. This page lists every form each one takes.

Long-press the app icon to get four shortcuts straight into the app, without passing through the home page first: Tools, Languages, AI chat, and Themes. They’re drawn bottom-up, so on most launchers Themes lands closest to the icon and Tools furthest away, even though this list states them in the order they’re declared.

Each shortcut is a static entry. The platform publishes them at install time, and no runtime code is involved. They only exist on Android 7.1 (API 25) and above. WM Keyboard’s minimum supported version is Android 7.0 (API 24), where the platform ignores the shortcuts file entirely and long-pressing the icon does nothing extra.

A settings screen reached from a launcher shortcut — this one opened straight to Themes.
A settings screen reached from a launcher shortcut — this one opened straight to Themes.
A settings screen reached from a launcher shortcut. This one opened straight to Themes.

Every settings screen in the app has an address:

wmkeyboard://settings/<route>

Tap one anywhere on the device and WM Keyboard opens on that screen. A bare wmkeyboard://settings opens the settings home list. Every launcher shortcut carries one of these links, and so does every route in the table further down. The link builder writes any of them for you, with the screen list, a setting search and every alternative spelling, and reads an existing link back into plain words.

So does every settings-location chip on this site, when you read it on an Android phone that has WM Keyboard installed. Tap WM KeyboardTypingAutomatic corrections and the app opens on that screen, or scrolls to the row the chip names. Chrome checks that the app is there before the chip becomes a link; a browser that cannot check, such as Firefox, links it anyway. On a desktop the chip is plain text, since nothing there can open the address.

You can send the same link yourself, for example from adb:

Terminal window
adb shell am start -a android.intent.action.VIEW -d "wmkeyboard://settings/typing/corrections"

A link opens a screen and changes nothing on the keyboard. To switch the layout, the theme or another setting from adb or an automation app, send one of the broadcasts on Automation: adb and Tasker instead.

Matching is case-insensitive for the screen name, so wmkeyboard://settings/Themes and wmkeyboard://settings/themes are the same address. Both URI forms parse: the regular wmkeyboard://settings/themes and the opaque wmkeyboard:settings/themes with no slashes after the colon. A route the app doesn’t have opens no screen. Nothing crashes, and the app doesn’t fall back to the home screen either, because landing you somewhere else would read as the link having worked. It tells you instead, and looks for a newer version that has the screen. See Links from a newer version.

Some addresses carry an id: a language, a theme you made, a tool. Write it as the last part of the address.

Terminal window
adb shell am start -a android.intent.action.VIEW -d "wmkeyboard://settings/language/bn"
adb shell am start -a android.intent.action.VIEW -d "wmkeyboard://settings/tool/CLIPBOARD"

Unlike a screen name, an id is matched exactly, since it is your data rather than a word this documentation chose. Percent-encode anything a URL can’t hold directly. An id the app doesn’t recognize opens the screen anyway, in whatever empty state it draws for one that has been deleted.

A link can point at a single row. The row scrolls itself into view and pulses once. Nothing is switched on or off. You still touch it yourself.

Name the row by the resource name of its title. That is the app’s own internal name for the row, and it survives both translation and an app update, which the words on screen do not.

wmkeyboard://setting/<name>
Terminal window
adb shell am start -a android.intent.action.VIEW -d "wmkeyboard://setting/typing_autocorrect_title"

The app looks the row up and opens whichever screen holds it, so you don’t need to know where a setting lives to link to it. If you do know, name both. This is the same address written the long way:

wmkeyboard://settings/typing/corrections?setting=typing_autocorrect_title

The rows you can address this way are exactly the rows settings search can find, around six hundred of them, across every screen. To get a row’s name, search for it in the app and note which screen it lands on, then read the name off app/src/main/res/values/strings.xml, or off the Settings reference pages, which are organized by the same screens. A name the app doesn’t have on any screen gets the same answer as an unknown route: with a screen named alongside it the screen still opens, and either way the app says it doesn’t have that setting and looks for a newer version. A name the app has on a different screen from the one the link names is not missing: the named screen opens, without the row flashing.

With a screen named, setting= can also be a group’s heading, and then the whole group scrolls into view and flashes. Settings search doesn’t list the headings, so this only works in the long form, where the link says which screen the group is on:

wmkeyboard://settings/accessibility?setting=accessibility_vision_title

A link can point at a screen or a setting that arrived after the version on your phone. That happens most with links from this site, which describes the newest code, and with links shared for a setting that is on GitHub but not in a release yet. So a settings link can say which version it needs:

wmkeyboard://settings/typing/corrections?setting=typing_auto_close_brackets_title&since=0.5.12
wmkeyboard://setting/layout_globe_recent_title?since=0.5.12

since= changes nothing about where the link goes. A version that has the screen opens it and ignores the number. A version that does not has something to tell you now, instead of opening nothing. It shows a dialog, and the dialog asks the app’s own update source, the same one as Check for updates on the About screen:

What the check findsWhat the dialog says
A release at or past the link’s versionWhich version is out and where, with the same Download, Update now or Open F-Droid button as the update card. Open the link again after the update.
No release that new yetThe version is not released yet. Wait for the official release, or build the app from its source code on GitHub. A Source code button opens the repository.
The link says your version already has itAn update does not help: the link has a mistake in it, or the setting moved or was removed.
No update source, as in a debug buildThe version to look for, and a Releases button for GitHub’s list.

A link without since= gets the same dialog. It just cannot say which version to wait for, so it says the link may be for an unreleased version or may be mistyped. An F-Droid build asks before it checks, because it never checks for updates on its own. On the other builds, opening the dialog is the check.

Versions from before this dialog existed ignore since= and open nothing, as they always did. The link builder and the settings chips on this site add since= for you, and only when the screen or row is newer than the first version that opens links at all, so most links stay short.

112 screens. Four more exist and are deliberately not addressable. Three have nowhere to land: the first-run wizard (onboarding), the sticker editor (sticker_editor), and a single photo’s page (photo_detail). Each of those three draws from something the screen before it handed over, so opened cold it would show nothing. The fourth is the preview of one Signal sticker pack (signal_pack/{packId}/{packKey}). It carries a pack key and asks Signal’s servers for the pack as it opens, so it only opens from a pack link you pasted or shared into the app yourself.

RouteOpens
homeThe settings home list. Also what a bare wmkeyboard://settings opens.
searchSettings search.
RouteOpens
typingTyping.
typing/correctionsAutomatic corrections.
typing/suggestionsSuggestions.
typing/autopilotAutopilot.
typing/octopusWords on the keys.
typing/chipsSmart chips.
typing/codesOne-time codes.
typing/gesturesGestures.
typing/hardwarePhysical keyboard.
hwshortcutsTool shortcuts list.
dictionaryPersonal dictionary.
customdictionariesCustom dictionaries.
blacklistSuggestion blacklist.
learnedcorrectionsLearned corrections.
RouteOpens
keypressKey press.
keypress/hapticsHaptics and sound.
keypress/popupKey popup.
keypress/shortcutsPress and hold shortcuts.
RouteOpens
appearanceAppearance.
appearance/toolbarToolbar.
appearance/toolboxToolbox.
themesKeyboard themes.
theme_edit/{themeId}The theme editor for one of your themes.
fontsKeyboard font.
fonts/{script}The font picker for one script, named in capitals: fonts/LATIN, fonts/BENGALI.
iconsIcons.
photosPhoto services.
photo_browseFind a photo online.
photo_libraryPhoto collection.
photo_rotationRotating background.
RouteOpens
layoutLayout and size.
layout/sizeSize and position.
layout/onehandedOne-handed, split and floating.
rowsRows and bars.
rows/symbolThe symbol row: height, lines, scrolling and the symbol sets.
keymapsKey layouts.
keymap_edit/{layoutId}The layout editor for one of your key layouts.
keymap_json/{layoutId}That layout as JSON.
panel_edit/{panel}A panel’s layout. The panel is EMOJI, CLIPBOARD, TEXT_EDIT or TRACKPAD.
panel_json/{panel}That panel’s layout as JSON.
RouteOpens
languagesLanguages and layouts.
add_languageAdd language.
language/{langId}One language’s page, for example language/en or language/bn.
language/{langId}/moreThe full layout catalogue for that language.
RouteOpens
emojiEmoji.
emoji/panelEmoji panel.
emojikeywordsEmoji keywords.
emojicategoriesEmoji categories: the order of the panel’s tabs, and which ones it shows.
emojiorder/{category}The emoji inside one category, to drag into your own order. The category is its id in lowercase: emojiorder/smileys, emojiorder/flags.
clipboardClipboard.
phoneformatsPhone number formats.
sticker_packsSticker packs.
signal_stickersSignal sticker packs.
sticker_pack/{packId}One of your sticker packs.
sticker_pack/{packId}/addOne of your sticker packs, with the photo picker open to add stickers.
RouteOpens
toolsTools.
tool/{toolName}One tool’s page. The name is the tool’s own, in capitals: tool/CLIPBOARD, tool/TRANSLATE.
vocab/packsVocabulary packs.
vocab/listsMy lists.
vocab/list/{packId}One of your vocabulary lists.
vocab/reviewReview.
vocab/browseBrowse words.
vocab/word/{packId}/{word}One word’s page in the vocabulary browser.
voiceVoice typing.
expanderText expansion.
expander/folder/{folderId}One snippet folder.
expander/folder/{folderId}/newA new snippet inside that folder.
expander/edit/{snippetId}One snippet. expander/edit/0 opens an empty new one.
musicappsMusic players.
kdeconnect/devicesKDE Connect: paired computers and the ones nearby.
ai_actionsAI actions.
ai_action_edit/{actionId}One AI action.
ai_historyAI history.
ai_chatAI chat.
ai_chat/{conversationId}One saved conversation.
symbol_set_edit/{setId}One symbol set.
RouteOpens
advancedAdvanced.
modesKeyboard modes.
mode_edit/{modeId}One keyboard mode.
addonsAdd-ons.
addon_repo/{repoUrl}One repository. The address is percent-encoded, so prefer wmkeyboard://repo?url= below.
addon/{repoUrl}/{addonId}One addon. Same caveat: prefer wmkeyboard://addon?repo=&id= below.
pluginsPlugins.
plugin/{pluginId}One installed plugin.
plugin_idePlugin editor.
plugin_ide/{draftId}One plugin project in the editor.
datasaverData saver.
serversServers: every service’s address, read by the F-Droid edition only.
notificationsNotifications.
selection_macrosSelection actions.
selection_macros/actionsSelection actions: the list of actions and their order.
selection_macros/aiSelection actions: the AI buttons.
selection_macros/zonesSelection actions: the time zones.
RouteOpens
privacyPrivacy.
permissionsPermissions.
network_activityNetwork activity: every request the keyboard makes, and where it went.
applockFingerprint lock.
automationAllowed actions: which commands other apps may send the keyboard. See Automation.
accessibilityAccessibility.
backupBackup and restore.
backup/autoAutomatic backup.
backup/syncSync devices.
backup/contentsWhat goes in an export.
RouteOpens
aboutAbout.
storageStorage.
storage/{category}One storage category, for example storage/themes.
statisticsStatistics.
debug_logDiagnostics.
licensesOpen-source licences.
license_text/{asset}One licence’s full text.
egg_gameKeycap catcher.

An app holding an Intent rather than writing a URL can name the same two things as extras on an explicit intent to MainActivity:

ExtraHolds
open_routeA route from the table above
open_settingA row’s resource name, with or without open_route
Terminal window
adb shell am start -n com.wasimaster.wmkeyboard/.app.MainActivity \
--es open_route typing/corrections \
--es open_setting typing_autocorrect_title

Both go through the same checks as a link, so an unknown route opens nothing rather than throwing.

A repository’s README, a forum post, or a message can link straight into the addon store with three link forms:

LinkOpens
wmkeyboard://addonsThe Addons screen
wmkeyboard://repo?url=<address>Addons, with the add-repository dialog pre-filled
wmkeyboard://addon?repo=<address>&id=<addonId>That addon’s detail page
wmkeyboard://import?url=<address>The import dialog for any https:// address: a file in a repository, a release asset, a gist, a build artifact. See Importing from a link.

The url and repo values go through the same address resolution as pasting one by hand: a bare github.com/user/repo becomes a raw-content URL, a plain address gets wmkeyboard-repo.json appended, and so on. See Installing addons for the full resolution table rather than duplicating it here. If the address doesn’t resolve, or an addon link is missing its repo or id parameter, the link goes nowhere rather than opening a broken screen.

Following a wmkeyboard://repo?url=… link pre-fills the add-repository dialog — nothing is fetched until you tap Add.
Following a wmkeyboard://repo?url=… link pre-fills the add-repository dialog — nothing is fetched until you tap Add.
Following a wmkeyboard://repo?url=… link pre-fills the add-repository dialog, but nothing is fetched until you tap Add.

Where a custom scheme cannot be a link at all, such as most chat apps, put https://wmkeyboard.pages.dev/open/# in front of any of these addresses. WM Keyboard claims that page, so on a phone that has the app installed the link opens the screen directly and the page never draws. Without the app, the page loads and offers to install it. The link builder writes that form for you, and explains what happens when the claim cannot be verified.

Like settings links, these are reachable from the web: the manifest declares a browsable VIEW filter for the addons, repo, and addon hosts, so tapping one of these links anywhere on the device opens WM Keyboard. If the app is already open, a second link is delivered to the running instance instead of stacking a second copy. Following the same link twice, or the same link the app already handled, is harmless.

One link that isn’t WM Keyboard’s own is answered as well: sgnl://addstickers?pack_id=...&pack_key=..., the form Signal and Molly open a sticker pack with. It is what the “Add to Signal” button on a pack’s web page fires, so with it you can look through packs in a browser and bring one into the keyboard in a press. With Signal or Molly installed, Android asks which app to use, and WM Keyboard is listed as “Add stickers to WM Keyboard”. Without them, the link opens WM Keyboard directly.

Only the addstickers host of that scheme is claimed. The rest of sgnl:// (chats, device linking, group invites) is Signal’s and stays Signal’s. The https://signal.art/addstickers/ form of the same link is not claimed either, because that domain isn’t this app’s to verify. Paste or share that one instead, as described under Signal sticker packs.

Like every other link, this one only navigates. It opens a confirm naming the server the pack comes from, then a preview of the pack, and nothing is added until you press Add this pack.

WM Keyboard registers a file association for thirteen extensions. Most are things it exports itself, plus five it only ever reads: plugin and sound pack files are built by their authors rather than the app, the settings-only backup has no export button left, a .flex belongs to FlorisBoard, and a .kmp belongs to Keyman. Tapping any of these in a file manager offers to import the file instead of opening it as plain text, and so does sharing one into WM Keyboard from another app’s share sheet.

ExtensionWhat it holds
.wmtheme.jsonA theme
.wmlayout.jsonA keyboard layout
.wmsnippets.jsonA snippet pack
.wmvocab.jsonA vocabulary pack
.wmstickersA sticker pack (ZIP)
.wmiconsAn icon pack (ZIP)
.wmsoundpackA key-sound pack: many recordings of one keyboard (ZIP)
.wmpluginA Lua plugin package (ZIP)
.wmconfig.jsonA full backup: any combination of settings, themes, dictionary, clipboard, snippets, stickers, icons, custom word lists, and addon repositories, whichever you chose to include when it was exported
.wmconfig.encThe same backup, passphrase-encrypted
.wmsettings.jsonAn older, settings-only backup format
.flexA FlorisBoard theme extension, converted rather than installed. Not WM Keyboard’s own format at all, and claiming it is the point: someone moving over opens the file they already have
.kmpA Keyman keyboard package, converted to a layout rather than installed

This opens a separate, minimal activity, not the settings app itself, since tapping a theme in a file manager shouldn’t drop you into full settings. It always shows exactly one confirmation dialog before doing anything, and the dialog tells you what happens next.

The one-time confirm dialog shown before any file import goes through.
The one-time confirm dialog shown before any file import goes through.
The one-time confirm dialog shown before any file import goes through.

Most formats are also previewed above that wording. Themes and layouts are drawn as a miniature keyboard, sticker packs show their images, sound packs give a play button per recording, and snippet and vocabulary packs list their first few entries. Backups and plugins are not previewed: the sections and the permissions listed below are the whole question for those two.

  • Theme. Added to your themes and applied immediately as the active keyboard theme.
  • Layout. Added to your layouts but not switched on. Turn it on under Languages when you’re ready.
  • Snippets. Added alongside what you already have, with fresh IDs, so importing the same pack twice creates two independent copies rather than overwriting.
  • Vocabulary pack. Added to your vocabulary packs. Its words get cards, nudges and flashcards.
  • Stickers. Added to your own sticker packs, images copied onto the device. Capped at 50 packs. Past that, the dialog tells you to delete one first.
  • Icons. Installed and switched on immediately, replacing the built-in icon set (any icons you’ve set individually are kept).
  • Key sounds (.wmsoundpack). Installed and switched on immediately, so your keys play its recordings in place of the built-in sound. Capped at 20 packs.
  • Config backup (.wmconfig.json). Settings merge into your current ones. Dictionary, clipboard, and snippets replace what’s already on the device. If the file contains API keys, the dialog warns that they’ll overwrite the ones you have set.
  • Encrypted config backup (.wmconfig.enc). The dialog draws a passphrase field instead of the usual “this file contains…” summary, because nothing inside can be read until it opens. Type the passphrase and confirm, and it imports exactly as the plain .wmconfig.json above does. A wrong passphrase and a damaged file are indistinguishable, so the failure message names both.
  • Settings backup (.wmsettings.json). Merges into your current settings the same way a config backup’s settings do: entries named in the file overwrite the matching ones on the device, and anything the file doesn’t mention (including a setting added in a version newer than the backup) is left untouched. Same warning for API keys.
  • FlorisBoard theme (.flex). Converted, never installed as one of ours. The dialog leads with how many of the file’s style rules found a home, credits the author and licence, and lists what got dropped. The converted themes are saved but not switched to, which is the opposite of a native theme import. A FlorisBoard extension that is a language pack rather than a theme, or that uses the older Snygg v1 syntax, is refused with a reason.
  • Keyman keyboard (.kmp). Its key grid becomes a layout, and its typing rules install with it when the package has any. A package with no on-screen keyboard has nothing to add.
  • Plugin (.wmplugin). This one has an extra gate. See below.

A .wmtheme.json file has no internal tag: any JSON object decodes successfully to an all-defaults theme, so the file’s name is the only signal that it was meant to be a theme. Renaming a .wmtheme.json file, or pointing one at unrelated JSON, will “succeed” as an all-defaults theme rather than failing.

Those extensions are not the whole story, because a file’s name is not always available to match on: a browser download is often passed to other apps as an anonymous id with no name attached. WM Keyboard therefore also accepts JSON files and files of unknown type by type alone, which is what makes a just-downloaded theme openable at all, and which is why WM Keyboard appears in the “Open with” list for files that are none of its business. A file that turns out not to be one of the formats above gets a plain “not a WM Keyboard file” dialog, and if it is readable text, the offer to open it in the app’s own editor instead. See File formats.

Opening a .wmplugin file doesn’t install it the way the other formats do; it’s gated twice:

Opening a plugin file while the Plugins subsystem is off — nothing installs or runs.
Opening a plugin file while the Plugins subsystem is off — nothing installs or runs.
Opening a plugin file while the Plugins subsystem is off: nothing installs or runs.
  1. If the Plugins subsystem is off, the dialog just says plugins are turned off and how to enable them. It doesn’t show the plugin’s name or anything else about it, and nothing installs.
  2. If it’s on, the dialog reads the plugin’s manifest. It shows the name, description, version, author, and the specific capabilities the plugin declares, or a line saying it asks for nothing beyond its own panel. One more line always follows: plugins run in a sandbox and can’t see what you type, read your clipboard, or use the internet. Only after that do you get an Import button, the same confirm button every other file type on this page uses. Installs are capped at 50 plugins.
WM KeyboardToolsPlugins

The Plugins subsystem is off by default. This is the only setting that changes how a deep link or file import behaves: every other link and shortcut on this page works unconditionally. See Plugins overview for what the sandbox allows once it’s on. Opening a .wmplugin file goes through the exact same sandbox and the same toggle as installing one from an addon repository.

  • Custom scheme, not App Links, for every address above. They all use wmkeyboard:// rather than https:// App Links, so there’s no domain to verify and no assetlinks.json to depend on: nothing about a link needs a real web page to exist on the other end. The one https address the app does claim is /open/ on this site, which exists only to carry one of these addresses to a phone that cannot be handed a custom scheme.
  • Every link form is browsable. Settings links, setting links and addon links are all declared browsable, so they work from anywhere a link can be tapped: a page on this site, a chat message, a support reply, another app. A launcher shortcut carries the same link on an explicit intent instead, which reaches the activity directly.
  • File imports never touch MainActivity. They land in their own activity, excluded from the recents list, so a stray import screen doesn’t linger in your app switcher.
  • Content URIs only. File-association matching is by content URI and file name, not MIME type. Every JSON format uses a compound extension like .wmtheme.json specifically so it doesn’t claim every plain .json file on the device. The ZIP formats (.wmstickers, .wmicons, .wmplugin) need no such trick, since the extension is already ours alone. Bare file:// links aren’t matched.
  • A repeated link is harmless. Because the app launches in single-top mode, tapping the same addon link twice, or tapping one while the app is already open, delivers it to the running instance rather than opening a second copy. Pasting a repository you’ve already added just reopens it.
  • A backup that isn’t one fails safely. If a .wmconfig.json or .wmsettings.json file’s content doesn’t actually decode as a backup, or a settings backup can’t be applied, the dialog says so and nothing changes on the device.