Convert your website into an Android app in 3 easy steps



What are we trying to do?

  • Let your website work offline
  • Make your website installable
  • Enable users to share data to your app using the Android share action

Your website, installed like an app. Accessible offline. PWA installed

Your website listed in the Android share option along with other apps PWA installed

Advantages:

  • Works offline
  • Fast due to improved caching
  • No need to copy-paste stuff across apps while on the mobile app

The 3 easy steps

Add a manifest

The manifest is a file that defines how the your website should be treated as an installed application, with details like app name, icons, theme color etc.

Generate the manifest file

Create a file called manifest.json in the root folder. You can also use the .webmanifest extension, like app.webmanifest.
The content has details like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
{
  "short_name": "The Wallet", //Used in the home screen, launcher etc
  "name": "The Wallet: Save links and text with ease!", //Shown during installation
  //Icons with multiple sizes if you want
  "icons": [
    {
      "src": "/images/icons-192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "/images/icons-512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  //URL loaded by the app when launched from the home screen
  "start_url": "/?source=mobile-app",
  //Identifier for your app, just use the start_url
  "id": "/?source=mobile-app",
  //Color for the splash screen
  "background_color": "#3367D6",
  //"fullscreen", "standalone" (makes it look like an app), "minimal-ui" (similar to "standalone" but with refresh and back buttons), "browser" (looks like a normal browser) 
  "display": "standalone",
  //When a URL outside this scope is opened, it will be considered to be outside your app. Useful when you have multiple apps on the same domain, like https://dvsj.in/chat and https://dvsj.in/calendar
  "scope": "/",
  //Color of the toolbar, usually matches <meta> theme color in your website's <head>
  "theme_color": "#3367D6",
  //Options shown for the app
  "shortcuts": [
    {
      "name": "View top 10 bookmarks",
      "short_name": "Top Bookmarks",
      "description": "View your most popular bookmarks",
      "url": "/bookmarks?sort=popular",
      "icons": [{ "src": "/images/popular.png", "sizes": "192x192" }]
    }
  ],
  //Describes your app, usually shown during installation
  "description": "Save links and text with ease!",
  //Shown during installation
  "screenshots": [
    {
      "src": "/images/books-clay.png",
      "type": "image/png",
      "sizes": "540x720",
      "form_factor": "narrow"
    },
    {
      "src": "/images/jar-of-notes.jpg",
      "type": "image/jpg",
      "sizes": "720x540",
      "form_factor": "wide"
    }
  ]
}

Add manifest file to your website

Add this to HTML pages in your website’s <head> section:

1
<link rel="manifest" href="/manifest.json">

You can now test it in devTools!

Add Service Workers

Unlike websites, apps are expected to work well even without an internet connection. Service workers are the magic that make this possible - they sit between your app and the website, caching and serving files from memory even when there’s no internet access, making it feel like an app.

The Service Worker is just a js file that specifies how requests from the app should be handled.
We can use Workbox - a library that takes a general configuration and generates a Service Worker for us.

Add the Service Worker configuration

Add the configuration for the service worker called workbox-config.js in the root folder:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
module.exports = {
	globDirectory: 'public/',
	globPatterns: [
		'**/*.{css,js,png,svg,html,json}'
	],
	swDest: 'public/sw.js', //This is our output serviceWorker JS file
	ignoreURLParametersMatching: [
		/^utm_/,
		/^fbclid$/
	]
};

Auto-generate the Service Worker JS based on the configuration

The Service Worker needs to be generated everytime there are changes to the website. This can be automated by adding your entries to the build tool’s config. The widely used build tools are webpack and rollup.

  1. Install the rollup plugin:
1
npm i rollup-plugin-workbox
  1. Add this to your rollup.config.js file:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
//Add these to the top
const { generateSW } = require('rollup-plugin-workbox');
const workboxConfig = require('./workbox-config.js');

module.exports = {
  // ...
  plugins: [
    generateSW(workboxConfig), //Add this to the plugins array
  ], 
};

That’s it, you have it all setup. Everytime you build your website, the Service Worker JS will be generated automatically.

The steps are similar for webpack - install the webpack plugin and add entries to webpack.config.js.

If you aren’t using a bundler and want to roll it yourself, you can use the Workbox CLI to generate the Service Worker - the steps are similar.

The 2 types of Service Workers offered by Webpack are generateSW and InjectManifest. generateSW is powerful with good default features, injectManifest is more customizable (and needs us to do a lot more work). If you want to go down that route, take a look at Google’s which plugin to use section

Add the Service Worker JS to your website

Add this to your main JS file. It’s usually index.js or main.js.

1
2
3
4
5
if ("serviceWorker" in navigator) {
	// register service worker
	navigator.serviceWorker.register("sw.js");
	console.debug('ServiceWorker registered');
}  

🏆 We can now “install” our website as an app
Open your website in the browser - you should see the install option. PWA install shown

The screenshots specified in manifest.json will be shown here PWA install images shown

Add the native share action to your app

Make your app receive shared data

Android can share text content (like selected text, links) as well as files. The changes needed for each type are explained in the official docs.
Add the share_target part to your manifest.json file to make your app receive shared data:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "short_name": "Weather", 
  //...
  //...
  "share_target": {
    "action": "/share-action", //Data will be sent to this path in your app 
    "method": "GET",
    "enctype": "application/x-www-form-urlencoded",
    "params": //The data will be sent as query params
    {
      "title": "title",
      "text": "text",
      "url": "url"
    }
  }
}

🏆 App should now be listed as share target Your website listed in the Android share option along with other apps PWA installed

Handle the shared data in your app

The data from the share action is sent to the path specified in the action field of the share_target object, usually as query params.
For example, sharing the URL hello! would result in a GET request to https://dvsj.in/share-action?text=hello!.

We can take the values from the query params and use them in our app.

1
2
3
4
5
const urlParams = new URLSearchParams(window.location.search);
const text = urlParams.get('text');
const title = urlParams.get('title');
const url = urlParams.get('url');
console.log({text, title, url});

Different apps send the same data differently. Some send data in the title query param, some in the text param.
Test your share action with different apps and handle them accordingly!

Some data from tested applications, assuming the website is https://dvsj.in and text is xyz:

App Content shared text value title value
Firefox browser Link https://dvsj.in
Firefox browser Text xyz Share via
Chrome browser Link https://dvsj.in <title>
Chrome browser Text "xyz" https://dvsj.in <title>
Materialistic, a HN client Link <title> - https://dvsj.in <title>
Materialistic, a HN client Text xyz

References:

https://microsoft.github.io/win-student-devs/#/30DaysOfPWA/kickoff
https://web.dev/learn/pwa/service-workers/
https://web.dev/service-worker-mindset/
https://serviceworkies.com/
https://chodounsky.com/2019/03/24/progressive-web-application-as-a-share-option-in-android/
https://web.dev/web-share-target/
https://web.dev/app-shortcuts/
https://web.dev/add-manifest/
https://web.dev/learn/pwa/web-app-manifest/