fix(android): load KMP files from app-external sources - #16392
Conversation
This PR provides two main services: - It allows on-device KMP file providers (such as Chrome downloads or the local Files app) to smoothly pass those KMP files into Keyman for Android for installation. - It also removes wildcard-host http / https deep-links from the app manifest, as these are invalid. Fixes: #14854 Fixes: #10133 Upon my investigation into related documentation, I've determined that with current versions of Android, setting up a general, "anywhere on the web" deep link for KMP files is not supported and should not be attempted. How, then, do we facilitate external apps passing off KMP links or files to Keyman for Android? Turns out... we've already done the work for downloaded files with `file:` and `content:` scheme deep-links. The issue is that some of our permissions-checking code logic was bad. With a little work to correct them - removing the bad checks that never should have existed anyway - the files load nicely! Build-bot: skip release:android
User Test ResultsTest specification and instructions
Test Artifacts |
|
I tested downloading a file from Chrome on my Samsung Galaxy A553 5G on Android 16 (One UI 8.0), Chrome version 151.0.7922.137 (11 Aug 2026). It worked fine -- Keyman appeared on the list of available apps to open the .kmp file, and no errors were encountered. Samsung Files continues to fail with the error as shown above. 'Files by Google' works fine -- so suggest we recommend that as an alternative file manager on Samsung devices. See also: #10967 (comment) |
|
I think we should have a user test for:
We should at some point also test, once we have a stable release build:
|
…ent-filters Fixes: #14122
Would the test be for it to download, then install from download? Or would it be to instantly load into the Keyman app package-install process? |
I think this -- we want to verify that existing package install pathways have not been impacted (not that I think they have -- but for the avoidance of unpleasant surprises...) |
They were, but by other changes - we internationalized keyman.com, which prefixes the language code onto all paths. I'm working on this now. |
Can you open an issue as a first step? |
mcdurdin
left a comment
There was a problem hiding this comment.
The AndroidManifest.xml still seems a bit chaotic. It would be good to review each intent-filter and ensure that the comments make sense, and group them appropriately.
| <category android:name="android.intent.category.DEFAULT" /> | ||
|
|
||
| <data android:scheme="file" /> | ||
| <data android:scheme="content" /> |
There was a problem hiding this comment.
Matches both file:/// and content:// URIs. Often times, what we get between apps appears to be the content:// one.
It's an OR, not an AND - allowing us to share the host and pathPattern entries for both protocols.
There was a problem hiding this comment.
I am not sure what this is fixing though -- does this impact the underlying reported issue?
There was a problem hiding this comment.
Still wondering if there is a reported issue that this is addressing?
There was a problem hiding this comment.
This helps fix the issue of trying to "open" a KMP file with Keyman from outside of the app.
Often times, what we get between apps appears to be the content:// one.
The way this <intent-filter> is set up, both file:/// and content:// URLs will be handled appropriately.
There was a problem hiding this comment.
I understood the first time, but I just didn't find the "Often times..." explanation compelling -- just anecdotal, and I wanted to understand why. So I searched and found the reference documentation I was looking for, relevant snippet below:
This last rule, rule (d), reflects the expectation that components are able to get local data from a file or content provider. Therefore, their filters can list just a data type and don't need to explicitly name the content: and file: schemes.
https://developer.android.com/guide/components/intents-filters#DataTest (called "rule 4", not "rule (d)" in the linked content)
This means we should actually delete both the file and content scheme tests.
There was a problem hiding this comment.
Makes sense once we're certain our MIME-type has fully landed within all relevant Android APIs. Are we certain that this has sufficiently occurred, though? What if some of the older devices we support don't associate .kmps properly?
There was a problem hiding this comment.
I think the point is that the file and content schemes are implicitly supported and we don't need to list them explicitly. The mime type will not change the situation here.
There was a problem hiding this comment.
a component is presumed to support content: and file: data if its filter lists only a MIME type.
Are you saying that because we list a MIME type there, whether or not it's actually supported by any given API of Android, this will apply?
Should we then have a version of it without a MIME-type entry to handle such potential cases?
There was a problem hiding this comment.
This intent-filter already has two MIME type tests: one for the future, vnd.keyman.kmp+zip, and one for today, octet-stream. So the host and scheme tests should not be needed because the MIME type matches will get us a good enough match, along with the pathPattern test (except for Samsung Files which is buggy).
<data android:pathPattern=".*\\.kmp" />
<data android:mimeType="application/vnd.keyman.kmp+zip" />
<!--
Backup MIME intents
-->
<data android:mimeType="application/octet-stream" />
Test ResultsGROUP_API_28:Test Specs
GROUP_API_30:Test Specs
GROUP_API_33:Test Specs
|
|
Hello, @jahorton. Could you help confirm the steps for the TEST_INSTALL_VIA_FILES are the same as TEST_DOWNLOAD_AND_INSTALL? |
commented
Aug 20, 2026
Sorry about that. I've updated the instructions for that test now. |
Build-bot: skip release:android
abb2b82 to
af0a2b6
Compare
|
Oops, merged the (approved) child PR into this one, rather than this into master, then that into master. Almost redid the split, but I managed to reverse the changes. (Interesting that there's no second force-push entry shown here for when I put things back to abb2b82.) |
| <category android:name="android.intent.category.DEFAULT" /> | ||
|
|
||
| <data android:scheme="file" /> | ||
| <data android:scheme="content" /> |
There was a problem hiding this comment.
Still wondering if there is a reported issue that this is addressing?
| Backup MIME intents | ||
| --> | ||
| <data | ||
| android:mimeType="application/octet-stream" |
There was a problem hiding this comment.
Ideally we won't need this in the future? It seems a bit broad!
| <data android:scheme="file" /> | ||
| <data android:scheme="content" /> |
There was a problem hiding this comment.
| <data android:scheme="file" /> | |
| <data android:scheme="content" /> |
Per https://developer.android.com/guide/components/intents-filters#DataTest
| <data android:scheme="file" /> | ||
| <data android:scheme="content" /> | ||
|
|
||
| <data android:host="*" /> |
There was a problem hiding this comment.
| <data android:host="*" /> |
Per https://developer.android.com/guide/components/intents-filters#DataTest
commented
Sep 9, 2026
|
@keymanapp-test-bot retest all There have been enough changes that it may be wise to verify that the new version of the fixes still work. |
commented
Sep 10, 2026
|
@jahorton, the Android Test build has failed. I shouldn't proceed, right? |
| <category android:name="android.intent.category.DEFAULT" /> | ||
|
|
||
| <data android:scheme="file" /> | ||
| <data android:scheme="content" /> |
There was a problem hiding this comment.
This intent-filter already has two MIME type tests: one for the future, vnd.keyman.kmp+zip, and one for today, octet-stream. So the host and scheme tests should not be needed because the MIME type matches will get us a good enough match, along with the pathPattern test (except for Samsung Files which is buggy).
<data android:pathPattern=".*\\.kmp" />
<data android:mimeType="application/vnd.keyman.kmp+zip" />
<!--
Backup MIME intents
-->
<data android:mimeType="application/octet-stream" />| <!-- needed for mangled email messages --> | ||
| <data | ||
| android:mimeType="application/keyman" | ||
| android:mimeType="application/vnd.keyman.kmp+zip" | ||
| android:scheme="content" /> | ||
| <!-- needed for mangled email messages --> | ||
| <data | ||
| android:mimeType="application/octet-stream" | ||
| android:scheme="content" /> |
There was a problem hiding this comment.
We should only need android:scheme="content" once in the filter, if we specify it, and it's clearer to read if split out from the mimeType:
<data android:scheme="content" />
<data android:mimeType="application/vnd.keyman.kmp+zip" />
<data android:mimeType="application/octet-stream" />
| <data android:scheme="http" /> | ||
| <data android:scheme="https" /> |
There was a problem hiding this comment.
To match the order found in a URI, can we put these before the android:host lines?
| android:pathPattern="/.*\\.kmp" | ||
| android:scheme="https" /> | ||
| <data android:host="*" /> | ||
| <data android:pathPattern=".*\\.kmp" /> |
There was a problem hiding this comment.
| <data android:pathPattern=".*\\.kmp" /> | |
| <data android:pathSuffix=".kmp" /> |
| <data android:scheme="file" /> | ||
| <data android:host="*" /> | ||
| <data android:pathPattern="/.*\\.kmp" /> | ||
| <data android:pathPattern=".*\\.kmp" /> |
There was a problem hiding this comment.
So sorry. It turns out that after all this, we do need to specify scheme and host because otherwise path is ignored in the filter:
These attributes are meaningful only if the scheme and host attributes are also specified for the filter.
Sorry for the confusion... Also, pathSuffix is available which I think seems safer than pathPattern, which is fragile (read the docs for all the edge cases).
| <data android:pathPattern=".*\\.kmp" /> | |
| <data android:scheme="content" /> | |
| <data android:scheme="file" /> | |
| <data android:host="*" /> | |
| <data android:pathSuffix=".kmp" /> |
There was a problem hiding this comment.
pathSuffix and pathAdvancedPattern were introduced in API level 31.
https://developer.android.com/guide/topics/manifest/data-element#path
As we support earlier API, I don't think it's safe to rely on pathSuffix.
commented
Sep 11, 2026
Test ResultsGROUP_API_28:Test Specs
GROUP_API_30:Test Specs
GROUP_API_33:Test Specs
|
commented
Sep 11, 2026
|
Changes in this pull request will be available for download in Keyman version 19.0.285-alpha |

This PR provides two main services:
Relates-to: #14854
Upon my investigation into related documentation, I've determined that with current versions of Android, setting up a general, "anywhere on the web" deep link for KMP files is not supported and should not be attempted.
How, then, do we facilitate external apps passing off KMP links or files to Keyman for Android? Turns out... we've already done the work for downloaded files with
file:andcontent:scheme deep-links. The issue is that some of our permissions-checking code logic was bad. With a little work to correct them - removing the bad checks that never should have existed anyway - the files load nicely!Build-bot: skip release:android
Notes: upon reviewing #10133 and #14122, it sounds like there may be variance among devices. Samsung is likely to be trickier to handle, while Google devices using plain Chrome may be more straightforward. I may need to alter the user tests accordingly.
User Testing
GROUP_API_28: Perform these tests on devices with Android API 28 (Android 9).
GROUP_API_30: Perform these tests on devices with Android API 30 (Android 11).
GROUP_API_33: Perform these tests on devices with Android API 33+ (Android 13).
TEST_DOWNLOAD_AND_INSTALL: Using Keyman for Android, download a KMP from within the Chrome app, then install it from Chrome's download section.
TEST_INSTALL_VIA_FILES: Using Keyman for Android, download a KMP from within the Chrome app, then install it from the device's Files app.