Convert Progressive Web App (PWA) to Android App (APK)

Progressive Web App or PWA is an excellent way enabling your website visitors adding an icon on their mobile device desktop for quick and easy access. In addition to other benefits, PWA offers native app-like experience while still being a website just with manifest like this added. But it's much more difficult to engage user to hit Add to home while visiting your website on regular browser than to ask him/her to install an app from Play Store. Let's look into how to convert PWA to full-blown Android app that can be distributed as APK or via regular app store.

First we need to introduce ourselves to Trusted Web Activity or TWA:

Trusted Web Activity is a new way to open your web-app content such as your Progressive Web App (PWA) from your Android app using a protocol based on Custom Tabs.

So essentially whatever will be presented to user screen is website content which would look exactly the same opened in usual browser. Afterall, most apps need internet connection to work, right?

TWA can be built into APK or prepared as Android App Bundle (AAB) which is required by Play Sore following this guide or by walking this step-by-step post available to web monetization subscribers below.

Install Node

Make sure NodeJS v.16 in installed in your system. On Ubuntu it goes like this:

# Using Ubuntu
curl -fsSL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt-get install nodejs

Install npm

Node Package Manager is installed using this command:

sudo apt install npm

Make npm user directory based

To avoid permission errors such as:

Error: EACCES: permission denied, access '/usr/local/lib'

Let's setup npm in user's local directory:

mkdir ~/.npm-global

npm config set prefix '~/.npm-global'

Now edit user's shell config file ~/.profile by adding:

export PATH=~/.npm-global/bin:$PATH

And propagate these changes:

source ~/.profile

In case needed to install up-to-date npm, run:

npm install -g npm@7.20.6

Install Bubblewrap

Bubblewrap will build our PWA / TWA into APK. Install:

npm i -g @bubblewrap/cli

Build your APK: initialize project

Now it's time to build. Have your PWA manifest file URL ready – in my case it is https://inretio.dev/superpwa-manifest.json – you will be asked for it to initiate the process.

Initialize project providing URL as manifest parameter:

bubblewrap init --manifest=https://inretio.dev/superpwa-manifest.json

Script will ask to install JDK 8 and Android SDK to ~/.bubblewrap – make sure to allow it.

Here is how wizard will look like:

bubblewrap init --manifest=https://inretio.dev/superpwa-manifest.json
,-----.        ,--.  ,--.  ,--.
|  |) /_,--.,--|  |-.|  |-.|  |,---.,--.   ,--,--.--.,--,--.,---.
|  .-.  |  ||  | .-. | .-. |  | .-. |  |.'.|  |  .--' ,-.  | .-. |
|  '--' '  ''  | `-' | `-' |  \   --|   .'.   |  |  \ '-'  | '-' '
`------' `----' `---' `---'`--'`----'--'   '--`--'   `--`--|  |-'
                                                           `--'    
Initializing application from Web Manifest:
	-  https://inretio.dev/superpwa-manifest.json
WARNING: Trusted Web Activities are currently incompatible with applications
targeting children under the age of 13. Check out the Play for Families
policies to learn more.
https://play.google.com/console/about/families/

Web app details (1/5)

The application generated by Bubblewrap will open a Progressive Web App when
started from the Android launcher. Please enter the following details about
the PWA:
  
	- Domain: the domain / origin where the PWA is hosted. 
	  Example: example.com

	- URL path: an URL path relative to the root of the origin,
	  opened when the application is started from the home screen.
	  Examples:

		- To open https://example.com/: /
		- To open https://example.com/path-to-pwa/: /path-to-pwa/

? Domain: inretio.dev
? URL path: /

Android app details (2/5)

Please, enter details regarding how the Android app will look when installed
into a device:

	- Application name: the name used in most places,
	  including the App information screen and on the Play Store.

	- Short name: an alternate name for the app, limited to
	  12 characters, used on a device launch screen.

	- Application ID: also known as Package Name, this is
	  the unique identifier for the application on an Android device or
	  the Play Store. The name must contain at least two segments,
	  separated by dots, each segment must start with a letter and all
	  characters must be alphanumeric or an underscore (_).

	- Display mode: how the app will be displayed on the
	  device screen when started. The default mode, used by most apps,
	  is standalone. fullscreen causes the device status bar and
	  navigation bars to be removed and is suitable for games or media
	  players. For more information on the status bars and navigation
	  bar on Android, go to:
	   - https://material.io/design/platform-guidance/android-bars.html.

	- Status bar color: sets the status bar color used when the
	  application is in foreground. Example: #7CC0FF

? Application name: Inretio Services
? Short name: Inretio
? Application ID: dev.inretio.twa
? Starting version code for the new app version: 1.0.0
? Display mode: standalone
? Orientation: any
? Status bar color: #2D5016

Launcher icons and splash screen (3/5)

The Android app requires an image for the launcher icon. It also displays a
splash screen while the web content is loading, to avoid displaying a flash of
a blank white page to users. 

	- Splash screen color: sets the background colour used for the
	  splash screen. Example: #7CC0FF

	- Icon URL: URL to an image that is at least 512x512px. Used to
	  generate the launcher icon for the application and the image for
	  the splash screen.

	- Maskable Icon URL (Optional): URL to an image that is at least
	  512x512px to be used when generating maskable icons. Maskable
	  icons should look good when their edges are removed by an icon
	  mask. They will be used to display adaptive launcher icons on the
	  Android home screen.

? Splash screen color: #CAE5C7
? Icon URL: https://inretio.dev/wp-content/uploads/2021/08/inretio_chip_512x512.png
? Maskable icon URL: https://inretio.dev/wp-content/uploads/2021/08/inretio_chip_512x512.png

Optional Features (4/5)

	- Include app shortcuts: This question is only prompted if a
	  'shortcuts' section is available on the input Web Manifest. When
	  answered “yes”, Bubblewrap uses the information to generate
	  shortcuts on the Android app. Read more about app shortcuts at
	  https://web.dev/app-shortcuts/.

	- Monochrome icon URL: URL to an image that is at least 48x48px to
	  be used when generating monochrome icons. Monochrome icons should
	  look good when displayed with a single color, the PWA's
	  theme_color. They will be used for notification icons.

? Monochrome icon URL: https://inretio.dev/wp-content/uploads/2020/04/inretio-chip_48x48.png
? Include support for Play Billing (this relies on alpha dependencies)? No
? Request geolocation permission? No

Signing key information (5/5)

Please, enter information about the key store containing the keys that will be used
to sign the application. If a key store does not exist on the provided path,
Bubblewrap will prompt for the creation of a new keystore.

	- Key store location: The location of the key store in the file
	  system.

	- Key name: The alias used on the key.

Read more about Android signing keys at:
	 https://developer.android.com/studio/publish/app-signing

? Key store location: /home/gytis/inretio_pwa_app/android.keystore
? Key name: android
Generating Android Project.
 >> [████████████████████████████████████████] 100%

Signing key creation

An existing key store could not be found at "/home/gytis/inretio_pwa_app/android.keystore".

? Do you want to create one now? Yes
? First and Last names (eg: John Doe): Gytis Repecka
? Organizational Unit (eg: Engineering Dept): IT
? Organization (eg: Company Name): Inretio, MB
? Country (2 letter code): LT
? Password for the Key Store: ******
? Password for the Key: ******
keytool Signing Key created successfully

Project generated successfully. Build it by running bubblewrap build

Notice signing key is greated in android.keystore – this will be important later.

Build app

Now build app by running bubblewrap build – process will look like this:

bubblewrap build
,-----.        ,--.  ,--.  ,--.
|  |) /_,--.,--|  |-.|  |-.|  |,---.,--.   ,--,--.--.,--,--.,---.
|  .-.  |  ||  | .-. | .-. |  | .-. |  |.'.|  |  .--' ,-.  | .-. |
|  '--' '  ''  | `-' | `-' |  \   --|   .'.   |  |  \ '-'  | '-' '
`------' `----' `---' `---'`--'`----'--'   '--`--'   `--`--|  |-'
                                                           `--'    
Please, enter passwords for the keystore /home/gytis/inretio_pwa_app/android.keystore and alias android.

? Password for the Key Store: ******
? Password for the Key: ******

Building the Android App...
	- Generated Android APK at ./app-release-signed.apk
	- Generated Android App Bundle at ./app-release-bundle.aab

Failed to run the PWA Quality Criteria checks. Skipping.

Install app

To check if APK works, install it onto Android device using adb:

adb devices
List of devices attached
xxxxxxxxxxxxxxxxxxxx	device

adb install app-release-signed.apk
Success

Now app should be available on the phone. Note: to make sure it does not display browser bar, set up Digital Assets Links.

Publish to Play Store

To publish app on Play Store you will need to setup developer account, pay $25 and submit your app-release-bundle.aab file via Play Console.

Few words about app signing:

If you opt in to Play App Signing, then the signing key that Bubblewrap generated and used to sign your app becomes the “upload key”. Whether you choose to use the Bubblewrap generated key as your signing or upload key, you should guard and keep the key private. We don't recommend committing it to version control. Instead, limit the number of individuals with access to it.

Have fun!

#development #android #app #apk #play #aab #pwa #twa


My name is Gytis Repečka, I am Data Engineer currently working with PostgreSQL and Linux infrastructure on AWS cloud. Writing code in SQL, Go and Bash. Experienced data professional (Teradata, Informatica) with focus on Inmon's data warehousing architecture (10 years). I enjoy using, promoting and contributing to open source software and love communicating about tech to both advanced and non-tech people. Mention me @gytis@mastodon.lt on Mastodon.