Google Play Store now open for Progressive Web Apps 😱

By Maximiliano Firtman

Disclaimer: I’m not affiliated with Google Play, Chrome or any other company mentioned. This is not an official statement; usage of the logo and name is just for illustration.

Chrome 72 for Android shipped the long-awaited Trusted Web Activity feature, which means we can now distribute PWAs in the Google Play Store! I played with the feature for a while, digging into the APIs and here you have a summary of what’s going on, what to expect and how to use it today.

This article is an introduction and I’m working in a tutorial series, so if you are interested in that, follow me on Twitter or subscribe to my newsletter.

Chrome 72 for Android is now shipping from the Play Store to all users and this version included Trusted Web Activity (TWA), that in a nutshell is a way to open Chrome in standalone mode (without any toolbar or Chrome UI) within the scope of our own native Android package.

Let me start saying that the publishing process is not straightforward as it should be (such as “enter your URL” in the Play Console and it’s done). It’s also not a way to use the already available WebAPK and publish it in the store. It’s a Java API that communicates through services with Chrome and seem to be in the early stages, so there is a lot of manual work to do. But after some effort, I could make it work. I will share my experience in this article for everyone interested.

For some reason, the feature doesn’t appear as shipped in Chrome Platform Status and there is still no documentation available more than the one published 15 months ago. But that’s not a surprise seeing that the available official docs to TWAs were published in October 2017 announcing the feature for Chrome 69 with most of the links and samples now dead.

Here you can watch a video of Pete Le Page explaining TWAs a year ago.

Is the Web Platform, Max! Why do we want to use the Store?

Well, it’s a long discussion, but I’ve been doing consulting with small and big companies for years, and when talking about PWA investment, the store is a recurrent demand. “My users will look for my app in the Store”, “I already have a native app that I want to stop doing, but I don’t want to lose my users”, “I need to access a native API”, or “I want to monetize my PWA.”

From now, publishing your PWA in the Store -as well as from the browser- will be possible. Of course, it’s an opt-in operation. The Play Store is not emulating the Microsoft Store: your PWA won’t be listed in the store if you don’t compile your own APK, and publish it.

Despite the new distribution mechanism and people finding your app within the store (and even in Google’s search under “Apps”), there are some things we may be able to do with a PWA in the Store, such as:

  • Offer a Home Screen Widget
  • Offer a wear OS companion app or Android Auto extension
  • Have background services accessing native features (communication with the PWA is still limited — more on this later)
  • Monetize the App — limited today, more on this later
  • Have some native screens mixed with PWA content
  • Distribute more than one PWA icon in the Launcher and/or home screen pointing to different URLs (within the same host)
  • Better internationalization support
There were already some PWAs in the Store, but now they will be much easier to produce and publish

In Google Play Store there are already some PWAs published such as Google Maps Go, Instagram Lite or Twitter Lite. The first one is using some kind of a private pre-TWA version, and the last two are using a WebView that while not ideal, it was the only way to do something like this before TWAs. These apps are adding a lot of native code for some things such as notifications. We want to publish PWAs as web developers, we don’t want to write a lot of Java code.

TWA is a special mode on Chrome Custom Tabs, a solution available to native Android apps from Chrome 45 to open an In-App browser.

No. With Cordova or other hybrid solutions, you are typically shipping your web resources (HTML, JS, CSS, etc.) within your APK package. Also, the engine is different and isolated from the users’ browser, so no session or cache sharing.

This is how a PWA looks like with Trusted Web Activity (Starbucks actually didn’t have one yet). I don’t see the theme-color implemented yet

With Trusted Web Activity you don’t need to package any resource file from your PWA (only native components, in case you want them); all your resources will be downloaded and updated on the fly from your Service Worker. Your PWA will still be rendered with the installed Chrome version, sharing all storage, cache, and sessions with the browser. Therefore, if your user has a session on your website opened when the user installs the app from the Play Store, she will still be logged in. The user is just installing a shortcut to Chrome using a special mode.

For creating an Android Package using TWA we will first need Android Studio. Right now, the options available are kind of experimental and documented only with some open source examples.

We can develop apps with TWA:

  • Use a high-level Java Support Library provided by the Chrome team: in this case, you don’t need to write any Java or Kotlin code; you create an Android Studio project (or clone the example), set up some metadata in AndroidManifest.xml from your Web App Manifest and you are done. 
    The framework will provide the connection with TWA and optional abilities to create a Settings entry in the Android device and to make Web Push notifications available. The framework is currently available as a support library in a temporary Jitpack repository. I guess that’s not going to be the final location for this library in the future.
  • Connect to the Trusted Web Activity manually. If you have experience developing Android apps with Java or Kotlin, you can just plug your PWA manually into your app. That means you can have some native activities and at one point you can open the Trusted Web Activity with your PWA. In this case, I suggest analyzing the Support Library to understand how to connect to Chrome from your project.

A PWA in the App Store won’t use the Web App Manifest for defining how your app runs; we will need to copy some of those values manually. Icons will be taken from the “res” folder like any other native Android App, orientation lock should be defined in AndroidManifest’s activity entry, etc.

If you are using the Java Support library, a splash screen might be created for you automatically but other properties from the manifest won’t be used. In fact, in my testing, I couldn’t see the theme-color being applied yet when my PWA is on the screen.

TWAs will work only if we digital handshake our domain with our app. This is a mechanism known as Digital Assets Links. That will make a trusted relationship between your host and your APK, proving that you are the owner of the PWA and that you won’t be publishing PWAs in the Play Store that you don’t own. It also makes a digital link between your website and your native app that in theory can let them share private data (but doesn’t seem to be possible with today’s TWA API).

With Digital Asset Links you must serve under your PWA’s domain a file in the URL <your-domain>/.well-known/assetlinks.json. That JSON file will contain information about your Android package (such as the package ID) and a hash of your app’s certificate that you can get running a command in the terminal. Your Android package will have a counterpart setting the host URL. There is an online validator tool available to check if everything is fine.

If you don’t do the handshake, TWAs won’t be activated and your app will just use normal Chrome Custom Tabs with a Chrome user interface similar to when your PWA has display: minimal-ui. I’m not completely sure but I guess the Play Store might reject apps that are just pointing to normal Custom Tabs and not validated TWAs. I’m not completely sure yet when Chrome is doing the Digital Asset Link check; if it’s being done on every access to the app before opening it as a TWA it might be a performance problem. I guess a cache will be possible, and also the Play Store might check this before accepting the app. We’ll see if future documentation gives us clarity on this matter.

There is a (not so simple) mechanism to bypass the Digital Asset Link certification process for development purposes, explained below in this article.

To publish your PWA shortcut using TWAs you will need to follow all the Google Play Store rules. Check the Developer Policy Center for more information. You will also need a Developer Publisher account paying a one-time fee of USD $25 and create metadata, screenshots and marketing material for your app.

To publish in the Google Play Store you must accept developer agreement and pay a one time USD $25 registration fee

When you are done creating your APK (Android Native Package) from Android Studio and you already have a Developer Console account, you must create a production APK and sign it with a self-created key that you create within Android Studio tools. You might also want to check App Signing by Google Play to simplify the process in the future.

There are no special rules or processes to upload these apps to the Play Store, but the Revision team might detect that you are using TWAs and will do a check that 1) Digital Assets Link is enabled and validated, and 2) the URL is passing the PWA criteria (mostly for a Service Worker fetch event handler).

Filling a lot of metadata and graphic assets will be mandatory for publishing your PWA in the Google Play Store

You don’t need to upload your app again if you change web content — unless you change the app completely as per the store rules. You will continue updating your app through Service Workers and new deploys to your server. You will have to create and upload another APK only if you want to change metadata, native code, or icons.

I’m seeing a list of possible limitations regarding the platform today, but it’s just a start and I hope we will see improvements over new versions.

If you publish your PWA in a subfolder of a host, it seems to be a couple of issues here.

  • Digital Asset Link connects the whole domain, not just a folder
  • The current Support Library seems to handle as an Intent (Link Capturing) the whole host, even if your PWA is in a subfolder

While this is a restriction of the Play Store itself (you can’t publish intranet apps, or apps that are just for you or your company), you might want to use TWAs and create APKs that you will deploy outside of the store.

That won’t be possible as Digital Asset Link works only with public URLs because Chrome needs to verify we own the domain, and that’s is not possible with internal URLs yet.

The first time you open the recently installed app, you don’t have any actual files from your app (Service Worker wasn’t registered yet — unless, the user visited the PWA before), so if you are offline you will end up with a blank white screen. I think trying to warm up Chrome after installation somehow will be useful in future versions. If you are using TWA APIs instead of the Support Library you might be able to detect this situation and inform the user properly using native APIs.

There is already a bi-directional channel to communicate the TWA Server (Chrome) and the TWA Client (our APK). That channel is currently being used to send Push Messages and show them as being part of our native app and not Chrome, but nothing else yet.

There is a potential here to bridge native code and JavaScript code without too much effort and let our PWAs access native code, similar to what happens when we publish an APPX with a PWA for Microsoft Store.

I’m seeing a future version with a way to register Java/Kotlin classes to the TWA Client so we can actually call them with a JavaScript API when our PWA is rendered in TWA mode.

Today, the only way to execute native code is to use Intents to open other native Activities and back opening TWA sending and receiving arguments through URIs parameters.

Also, you can create some kind of web server, or WebSocket server in native code running on a Service and let the PWA talk to it, but it’s kind of weird, complicated and maybe battery consuming. But there is a whole new world that might open now. Let’s see what the community creates!

If your app is not free to download, there will be no easy way to validate that the user has actually paid for it (at the end, your content is just a URL); also if you have digital assets or subscriptions that you’d like to sell using the Play Store wallet, it will be a challenge to make it work without having an actual bridge to native code.

I’m not sure if it’s a bug or something on my development environment but remote debugging the Service Worker from the TWA is not working. I can inspect the window’s context, but not the Service Worker.

TWAs work only with Chrome today, but the API might be also cloned by other browsers, such as Samsung Internet, Edge or Firefox in the future.

What happens if the user has an older version of Chrome and installs the app from the Play Store? In this case, your PWA will appear as a Chrome Custom Tab, not in a completely standalone mode.

Google Maps Go in the Play Store was already using something similar to TWA specifying Chrome as a requirement

What happens if the user doesn’t have Chrome at all? As of today using the Support Library, your app won’t work at all. If you are using the TWA API on your own Java/Kotlin code you might be able to detect browser availability and load an alternate solution, such as a WebView or opening the browser.

While it’s not so common to see Android devices without Chrome, there are some devices without it by default, including new devices in Europe shipping without Chrome by default.

PWAs won’t work on wear OS (watches), but I’m not completely sure what happens on other Android platforms. I will guess a probably not yet, but I’ll test it for a next article. I’m talking about Android TV or even Chromebooks with Play Store. In the meantime, if you didn’t test it, it might be a good idea to disable those platforms in your store listing.

If you already installed the PWA from Chrome, you already have installed an APK for that URL signed by the Play Store, but that won’t stop the store to list your App and let the user also install it. The same on the other way: having the app installed from the store won’t stop Chrome to offer the user to “Add” it from the browser. I think this might be avoided in the future if the WebAPK also has a Digital Asset Link somehow, or if we can match WebAPK’s app’s id, but I don’t see this happening soon. We’ll see.

Two Starbucks Apps running at the same time: WebAPK and our own APK

You can stop Chrome to offer WebAPK and offer your store listing app instead by using the related_applications attribute and prefer_related_applications Web App Manifest’s attributes. Get Installed Related Apps API might help in the future with this conflict.

Yes, I know, I’ve just invented the PWApk word, but it doesn’t sound too bad, right?

This article is an introduction and I’m working in a tutorial series, so if you are interested in that, follow me on Twitter or subscribe to my newsletter.

A few years ago I did a video course on Native Web Apps for Android, that while not exactly what you need to do for PWAs, it will help web developers understand the Android ecosystem.

You can clone the git sample or start a new project. Let’s start a new one, just to understand what’s going on. Create a new Project in Android Studio and select “No Activity” as we will use just a Trusted Web Activity provided by the Support Library.

We start with an empty project

Fill the details, picking a name (we will override this later), and a Package name. The package name is important as it will be the ID of our app in the Android OS and also within the store. I recommend using your PWA’s host in reserve order and an optional name after, such as: com.mypwa.calculator if your PWA is https://mypwa.com/calculator

API 19 (Android 4.4) as a base looks good. Chrome is currently compatible only with Chrome as it seems it’s going to be the minimum version required for Chrome soon. Some things on TWA will work only from API 23 (Android 6.0) but the Support Library will take care of that.

Picking the minimum API level will limit the devices that will see our PWA in the Google Play Store

The next step is to add the TWA Support Library as a dependency, so we will go and open two files with the name build.gradle

There are two gradle configuration files, one for the project and one for the Android app

Starting with the Project build.gradle, we will add under allprojects > repositories, the following line:

maven { url "https://jitpack.io" }

Next step, we open the Module build.gradle and we add under dependencies:

implementation 'com.github.GoogleChrome:custom-tabs-client:e446d08014'

The next step is to stay under the Module’s build.gradle file and setup the PWA settings for the Trusted Web Activity, under defaultConfig we will add:

manifestPlaceholders = [
hostName: "app.starbucks.com",
defaultUrl: "https://app.starbucks.com",
launcherName: "Starbucks",
assetStatements: '[{ "relation": ["delegate_permission/common.handle_all_urls"], ' +
'"target": {"namespace": "web", "site": "https://app.starbucks.com"}}]'
]

In this case, I’ll use the Starbucks PWA as an example. The assetStatements key is the one that will need information from the Digital Asset Link process. We’ll skip that part for development purposes. The property with the name launcherName should match short_name in the Web App Manifest.

Android apps have their own manifest and they won’t use our Web App Manifest, that file is under app > manifests in your Android project explorer as is called AndroidManifest.xml. You will find a self-closed Application XML element there.

The default Android Manifest file we have now

There, we can change the value of android:label with ${launcherName} to use the launcherName we set before in the metadata, so we will have one single source of truth for the app’s name.

The next step is to setup this file, starting with opening the <application> tag so we can add some children inside that will look like the following code:

<meta-data
android:name="asset_statements"
android:value="${assetStatements}" />

<activity android:name="android.support.customtabs.trusted.LauncherActivity"
android:label="${launcherName}">
<meta-data android:name="android.support.customtabs.trusted.DEFAULT_URL"
android:value="${defaultUrl}" />

<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="https"
android:host="${hostName}"/>
</intent-filter>
</activity>

The code below will set up the TWA using the Support Library, the Intent Filter so your app will capture links to your PWA and the Digital Asset Link. I will skip the details on what’s going on there from an Android app’s point of view.

It’s time to Sync

At this point, you will have to make Android Studio take all your changes by clicking on “Sync Now.” If everything is correct, you won’t get any errors at this point.

Now our app uses just a default android icon, we should replace all the files available in app>res>mipmap in different subfolders for different pixel densities. There are two versions, square and rounded icons. Rounded icons are new in Android 7.1 and if you want to skip them remove the android:roundIcon reference in the AndroidManifest.xml.

We need to manually take icons from our Manifest and copy them in the mipmap subfolders with the right name

Finally, we should open app/res/values/styles.xml and make some changes to the theme so it will look like a PWA:

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:windowNoTitle">true</item>
<item name="android:backgroundDimEnabled">false</item>
</style>

You can change the colors of the app from app/res/values/colors.xml but I didn’t see them yet in the TWA.

If you are currently restricting orientation in your manifest, you might also want to add that restriction in the <activity> element in the AndroidManifest.xml.

We are ready to test our PWApk, but before that, we need to set up development TWA mode for Chrome in our Android testing device or emulator (with Chrome 72+).

Check that you have Chrome stable version 72 and open chrome://flags. Search for “Enable command line on non-rooted devices,” and enable the flag. You will have to restart Chrome.

Next, we need to set up Chrome to bypass Digital Asset Link for the host we want to test, in our example: app.starbucks.com. (I’m sorry Starbucks, but we love you, you know that LOL)

To change command line arguments for Chrome on Android we need to write a text file in the Android’s file system. The simplest way to do it is through adb (android debug bridge) that has to be in the path (google that if it’s not there) and execute:

adb shell cat /data/local/tmp/chrome-command-line _ - disable-digital-asset-link-verification-for-url="https://app.starbucks.com"

There is a simple bash script available in the Chrome’s sample to use if you want to.

We need to stop Chrome to take our new dev setting

And now, we need to restart Chrome. But not just killing the app from the multitask window. Go the Settings / Apps / Chrome and Terminate the whole process (Force Stop). I had to do this a couple of times before having it ready.

The warning stating that the flag is enabled

If it’s done, then every time you open chrome you will get a warning now about that flag we enabled and if you run your app from Android Studio you will finally get your PWA up and running in standalone more under your APK’s icon and name.

The idea that we can now publish PWAs in the Google Play Store is really a game changer. It seems we are in the early stages of the API right now and we definitely need a higher level solution for this. I really want to see a tool to enter our PWA’s URL and get an APK from it. It won’t be simply because of the Digital Asset Link verification system. Only the Play Store can do that just by using the same WebAPK they are generating.

I was questioning if Google was approving or not TWAs, so they are removing the suspicious at least of the TWA part of the article.

Let’s take Chrome 72 TWA as the first step of a long journey!

I want to thank Paul Kinlan for his support regarding TWAs, my questions and because I’ve been pinging him almost every month about this :P

Maximiliano Firtman is an independent mobile + web developer, trainer, speaker, and author. He has authored many books, including High-Performance Mobile Web published by O’Reilly Media. He is a frequent speaker at conferences worldwide and he has been widely recognized for his work in the mobile-web community. He teaches mobile, JavaScript, PWA and web performance trainingsfor top companies around the world. He is an independent consultant and he has delivered several PWAs workshops and courses at many companies and at online publishers, such as Linked Learning/Lynda and Safari. Twitter: @firt