Sharing, selecting, and viewing data cross-app with Web Activities and deep links
Web Activities
Like App Intents on iOS and Intents on Android, Web Activities are the way to share, pick, and view across apps on KaiOS. The interfaces are very similar across platforms, consisting of two components: a name (or action in Android) and an arbitrary data payload. Again similar to Android’s Intents, Web Activities are registered on KaiOS within an app’s static manifest.
Web Activites have many uses, including:
- Launching a specific Settings page
- Sending an email or SMS
- Sharing text or an image across apps
- Opening a specific file format like a PDF
- Selecting a photo from the Gallery
- Viewing a website
- Setting the default ringtone
- Responding to alarms via the Alarm API
Web Activities are available in all KaiOS app types, including hosted web apps and packaged privileged or certified apps. Furthermore, Web Activities are even available to websites! In 2020 this is how I stumbled upon a universal jailbreak for KaiOS that let’s users open the hidden Developer menu from their browser.
Differences in KaiOS 2.5 and KaiOS 3.0
On KaiOS 2.5, this API is MozActivity
and on KaiOS 3.0, it’s
WebActivity
. This feature has been called deep linking and it’s one of the most common ways to interact with System apps. On KaiOS 2.5, the MozActivity
inherits from DOMRequest
while on KaiOS 3.0 the WebActivity
class has a start
method that returns a Promise
. Here’s a simple exmaple on both versions that would open a website in the Browser app.
KaiOS 2.5 | KaiOS 3.0 |
|
|
If you’re building an app using WebActivities, it’s easy to standardize the APIs for consistent, cross-version support. Here’s a function that works on both KaiOS 2.5 and KaiOS 3.0 with a consistent Promise
return type.
1function startActivity(name, data) {
2 // KaiOS 2.5 uses MozActivity
3 if (typeof MozActivity !== 'undefined') {
4 return new Promise((resolve, reject) => {
5 let activity = new MozActivity({ name, data });
6 activity.onsuccess = () => resolve(activity);
7 activity.onerror = (e) => reject(e);
8 });
9 // KaiOS 3.0 uses WebActivity
10 } else if (typeof WebActivity !== 'undefined') {
11 let activity = new WebActivity(name, data);
12 return activity.start();
13 }
14
15 // Not KaiOS?
16 return Promise.resolve(null);
17}
Registering your app for an activity
So far we’ve covered launching Web Activities, but it’s also possible to respond to activity requests from within your app. To do so, you need to register an activity handler.
Static activity registration is declared in the manifest.webapp
or manifest.webmanifest
.
1{
2 "name": "My App",
3 "description": "Awesome app",
4 "activities": {
5 "view": {
6 "filters": {
7 "type": "url",
8 "url": {
9 "required": true,
10 "regexp":"/^https?:/"
11 }
12 },
13 "returnValue": false
14 }
15 }
16}
Note: returnValue
is false
by default. Set it to true
to allow your activity to return a value via postResult()
.
KaiOS 2.5
Then within app code, an activity handler callback can be registered and invoked when the system sends a message to your app. In KaiOS 2.5 the API is simple:
1let register = navigator.mozSetMessageHandler('view', (e) => {
2 if (e.source.name === 'view') {
3 console.log(e.source.data);
4 }
5});
6
7register.onerror = () => {
8 console.log("Failed to register");
9};
You can check if your application has queued messages by calling navigator.mozHasPendingMessage()
with a given name
as the parameter. When a message handler is set, all queued messages will be asynchronously delivered to the application.
Registering to receive system messages like activity
or serviceworker-notification
(covered in Push Notifications) requires an additional property be set in manifest.webapp
:
1{
2 "name": "My App",
3 "description": "Awesome app",
4 "messages": [
5 { "serviceworker-notification": "/index.html" },
6 { "activity": "/index.html" }
7 ],
8}
There’s also one other important function, navigator.mozSetMessageHandlerPromise
, which accepts a Promise
as input. This method can only be called from the activity handler callback allows the page to set a Promise
to keep alive the app until an asynchronous operation is fully completed. This could be used to process data or access a remote resource before returning a response.
1let register = navigator.mozSetMessageHandler('view', (e) => {
2 let url = e.source.data.url;
3 if (url.startsWith('https')) {
4 navigator.mozSetMessageHandlerPromise(fetch(url))
5 .then((response) => response.text())
6 .then((responseText) => e.postResult(responseText)));
7 } else {
8 e.postError(new Error('Insecure URL.'));
9 }
10});
For instance, in this trivial example above an activity handler is registered for the view
event that requires a url
be provided in the data
payload. If the URL is insecure (doesn’t start with HTTPS), then an error is posted. Otherwise, the resource is downloaded via fetch
and the response text is passed as the result to the activity caller.
Note: a real-world example should perform better input validation and sanitization before downloading a remote resource.
KaiOS 3.0
For KaiOS 3.0, activity registration is declared in the b2g_features
, section of manifest.webmanifest
.
1{
2 "name": "My App",
3 "description": "Awesome app",
4 "b2g_features": {
5 "activities": {
6 "view": {
7 "filters": {
8 "type": "url",
9 "url": {
10 "required": true,
11 "regexp":"/^https?:/"
12 }
13 }
14 }
15 }
16 }
17}
The process for handling activity requests from within a KaiOS 3.0 app is more complicated than on KaiOS 2.5. First, it requires an active and registered ServiceWorker. Second, the ServiceWorker needs to handle the KaiOS-specific systemmessage
event.
1// Context: application code
2if (swRegistration.systemMessageManager) {
3 swRegistration.systemMessageManager.subscribe('view');
4}
5
6// Context: ServiceWorker
7self.addEventListener('systemmessage', (e) => {
8 if (e.name === 'view') {
9 console.log(e.data);
10 }
11});
Often this means posting messages and opening windows using the Clients
interface to respond to the message within the app’s UI.
Note: see
SystemMessageService.cpp for a list of system messages that can be received like media-button
.
Common Activities
KaiOS ships with a number of pre-installed apps including Settings, Browser, Music, FM Radio, Gallery, and more. Each of these apps has a manifest with registered activities. Additionally, popular third-party apps like WhatsApp often register activity handlers as well. Here’s a selection of popular activities you might want to include in your apps.
Settings
The Settings app can be launched using the configure
name and an optional section
for specific pages.
1let request = new MozActivity({
2 name: "configure",
3 data: {
4 target: "device",
5 section: "TODO"
6 },
7});
There’s easily over 100 different values for section
, but here’s a few popular ones:
developer
- Developer menuvolume
- Volume controldownloads
- Downloads listbattery
- Battery infoappPermissions
- Permissions pagedateTime
- Date & Timebluetooth
- Bluetoothwifi
- WiFigeolocation
- Geolocation & GPS
Launching the Settings app to a specific page can be a great UX enhancement! For instance, if your app requires an internet connection and you detect none is available, you can prompt the user to connect and easily navigate back to your app.
Note: it’s important to test these activities on real devices because it’s not uncommon for the Settings app to launch a blank page even for sections is specifically accepts.
Share
Share text or an image via SMS, WhatsApp, etc.
1let shareText = new MozActivity({
2 name: "share",
3 data: {
4 type: "text/plain",
5 blobs: [ "This is a message to share" ]
6 }
7});
8
9let shareImage = new MozActivity({
10 name: "share",
11 data: {
12 type: "image/png",
13 number: 1,
14 blobs: [ blob ], // i.e. Canvas.toBlob
15 filenames: [ "myimage.png" ]
16 }
17});
Pick
Launch the Gallery or Camera app to select an image.
1let pick = new MozActivity({
2 name: "pick",
3 data: {
4 type: ["image/png", "image/jpg", "image/jpeg"]
5 }
6});
Launch the Contacts app to select a contact.
1let pick = new MozActivity({
2 name: "pick",
3 data: {
4 type: ["webcontacts/contact"],
5 },
6});
Note: when more than one app is able to respond to an activity request, a list is presented to the user to select.
Dial
Launch the phone dialer to call a number (requires user confirmation).
1let dial = new MozActivity({
2 name: "dial",
3 data: {
4 number: "*345#"
5 }
6});
View
Launch a website in the Browser.
1let view = new MozActivity({
2 name: "view",
3 data: {
4 type: "url",
5 url: "https://kaios.dev"
6 }
7});
Open
The "open"
action will always launch an activity using the original content type to allow for third party applications to handle arbitrary types of content.
1let open = new MozActivity({
2 name: "open",
3 data: {
4 type: contentType,
5 blob: openBlob,
6 filename: null
7 }
8});
New
Create a new contact.
1let newActivity = new MozActivity({
2 name: "new",
3 data: {
4 type: "webcontacts/contact",
5 params: {
6 giveName: "Tom",
7 familyName: "Barrasso",
8 tel: "+1234567890",
9 email: "[email protected]",
10 address: "USA",
11 note: "Memo",
12 company: "PodLP"
13 }
14 }
15});
Create a new text message (SMS).
1let newActivity = new MozActivity({
2 name: "new",
3 data: {
4 type: "websms/sms",
5 number: "1234567890",
6 },
7});
Save
Save a website as a bookmark on the homescreen.
1let saveBookmark = new MozActivity({
2 name: "save-bookmark",
3 data: {
4 type: "url",
5 url: "https://kaios.dev",
6 name: "KaiOS.dev",
7 icon: "https://kaiostech.com/favicon.ico"
8 }
9});
Set Ringtone
Set an audio file as the system ringtone.
1let ringtoneActivity = new MozActivity({
2 name: "setringtone",
3 data: {
4 blobs: [ audioBlob ],
5 type: "audio/*",
6 metadata: [{
7 title: "Umbrella",
8 artist: "Rihanna",
9 picture: pictureBlog
10 }]
11 }
12});
Set Wallpaper
Set an image blob as the system wallpaper.
1let wallpaperActivity = new MozActivity({
2 name: "setwallpaper",
3 data: {
4 blobs: [ imageBlob ],
5 type: "image/*",
6 number: 1
7 }
8});
9
10let wallpaperActivity = new WebActivity("set-wallpaper", {
11 blobs: [ imageBlob ],
12 type: "image/*",
13 number: 1
14});
Note: this is available on KaiOS 3.0 and later KaiOS 2.5 devices. It requires the pre-installed Wallpaper (Gaia Wallpaper Picker) app, and may be named "setwallpaper"
or "set-wallpaper"
.
Cancelling and Error Handling
On both KaiOS 2.5 and 3.0, to cancel a started activity, use cancel()
. You can cancel an activity before or after it’s been loaded. On KaiOS 2.5, the pending DOMRequest
of new MozAcitivity
will trigger onerror
, and on KaiOS 3.0 the pending promise of start()
will be rejected.
The onerror
callback returns a DOMError
on KaiOS 2.5, and by far the most common error name
you’ll receive is "NO_PROVIDER"
, which means there is no available app to respond to that activity request. When an activity is cancelled the error name
will be ActivityCanceled
.
Additional Activities
Open Page
The open-page
activity is supported by the KaiStore and JioStore. It lets you launch the app store to a specific app detail page for quicker installation, and can be used with Install banners on websites. This is how the
KaiStore’s web directory prompts for app installations on KaiOS phones. Below is an example that would open
PodLP on the KaiStore.
1let openPage = new MozActivity({
2 name: "open-page",
3 data: {
4 type: "url",
5 url: "https://api.kaiostech.com/apps/manifest/UxappJMyyWGDpPORzsyl"
6 }
7});
Note: app identifiers are consistently-generated hashes between the KaiStore and JioStore, but Manifest URLs are different. For instance, PodLP’s ID is UxappJMyyWGDpPORzsyl
but the prefix for JioStore Manifest URLs is
https://api.kai.jiophone.net/v2.0/apps/manifest/.
Deep Linking
Deep linking is the ability for an app to respond to a URL loaded within the system. For instance, the KaiStore supports deep linking on KaiOS with declarations in the manifest.webapp
file.
1{
2 "deeplinks": {
3 "regex": "^(app://)(kaios-store|kaios-plus)(.kaiostech.com)($|/$|/\\?(apps|postResult)=)",
4 "action": "open-deeplink"
5 },
6 "activities": {
7 "open-deeplink": {
8 "href": "./index.html",
9 "disposition": "window",
10 "filters": {
11 "type": "url",
12 "url": {
13 "required": true,
14 "pattern": "(app|rtsp|data):.{1,16384}"
15 }
16 },
17 "returnValue": true
18 }
19 }
20}
Deep links require two parts: a deeplinks
section with a regular expression for a specific URL and an action activity that’s typically named "open-deeplink"
. This feature is supported in later versions of KaiOS 2.5, as well as all versions of KaiOS 3.0. Implementation details for deep link registration and handling can be found in
AppsServiceDelegate.jsm and
nsURILoader.cpp.
Conclusion
There are many ways to use web activities to develop high-quality user experiences on KaiOS apps and websites. Activities can increase install conversation, nudge users to easily grant permissions, and conveniently share content. If you’re looking for a partner to ensure the best possible user experience on KaiOS, you can find the author’s contact info on the About page.