Skip to content

Commit fa73571

Browse files
Offline-First TWA doc (GoogleChrome#762)
* Offline-First TWA doc * Adding line break at the end of author's file. * Wrapping the text to 80 chars. * Code review suggestions by @jpmedley Co-authored-by: Joe Medley <jmedley@google.com> Co-authored-by: Joe Medley <jmedley@google.com>
1 parent f1c6f72 commit fa73571

File tree

3 files changed

+214
-1
lines changed

3 files changed

+214
-1
lines changed

site/_data/authorsData.json

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -619,5 +619,23 @@
619619
"en": "Googler working on Chrome DevTools."
620620
},
621621
"image": "image/dPDCek3EhZgLQPGtEG3y0fTn4v82/XQZ4QLusg8Iq5kP4s04d.jpg"
622+
},
623+
"demianrenzulli": {
624+
"name": {
625+
"given": "Demian",
626+
"family": "Renzulli"
627+
},
628+
"org": {
629+
"name": "Google",
630+
"unit": "Web Ecosystems Consultant"
631+
},
632+
"descriptions": {
633+
"en": "Demián is a Web Ecosystems Consultant working at Google New York."
634+
},
635+
"country": "US",
636+
"twitter": "drenzulli",
637+
"github": "demianrenzulli",
638+
"glitch": "demianrenzulli",
639+
"image": "image/26V1DWN36MZr3mUo8ChSBlCpzp43/Jsv3fxDmq9T74z5q7zzU.jpg"
622640
}
623-
}
641+
}

site/_data/docs/android/toc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,4 @@
2020
- url: /docs/android/trusted-web-activity/web-share-target/
2121
- url: /docs/android/trusted-web-activity/play-billing/
2222
- url: /docs/android/trusted-web-activity/receive-payments-play-billing/
23+
- url: /docs/android/trusted-web-activity/offline-first/
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
---
2+
layout: "layouts/doc-post.njk"
3+
title: Offline-First Trusted Web Activities
4+
date: 2021-05-11
5+
description: How to display a fallback offline screen, if the first time the user opens the app, there's no connectivity.
6+
authors:
7+
- demianrenzulli
8+
---
9+
10+
The first time a user launches a Progressive Web App (PWA) via Trusted Web
11+
Activity, the service worker won't yet be available since [the registration
12+
process](https://developers.google.com/web/fundamentals/primers/service-workers/registration)
13+
hasn't taken place yet. Aditionally, if the user doesn't have connectivity during the first app
14+
launch, instead of the custom offline experience, the network error page is
15+
shown.
16+
17+
An example of this scenario might take place after the user downloads the PWA
18+
from the Play Store. If the user doesn't have connectivity when trying to open
19+
the app for the first time, the service worker won't yet be available to show
20+
the offline fallback page, therefore. The standard error page will be shown,
21+
leading to a bad experience.
22+
23+
{% Img src="image/26V1DWN36MZr3mUo8ChSBlCpzp43/SixNJuHH01eY6u96Owo1.png",
24+
alt="TWA offline: the standard offline page", width="533", height="372" %}
25+
26+
This guide explains how to display your own activity in this situation by
27+
checking the status of the network before launching the Trusted Web Activity.
28+
29+
{% Aside %} A prerequisite of this guide is to have a Trusted Web Activity app
30+
running. Follow the steps in the [Integration
31+
Guide](https://developer.chrome.com/docs/android/trusted-web-activity/integration-guide/)
32+
to create a basic Trusted Web Activity project. {% endAside%}
33+
34+
## Create a custom LauncherActivity
35+
36+
The first step is to create a custom launcher activity. This `Activity`
37+
that will contain the offline screen to show if there's no connectivity
38+
the first time a user opens the app.
39+
40+
Call the Activity `OfflineFirstTWALauncherActivity`, and make it extend:
41+
`com.google.androidbrowserhelper.trusted.LauncherActivity`.
42+
43+
```java
44+
import com.google.androidbrowserhelper.trusted.LauncherActivity;
45+
46+
public class OfflineFirstTWALauncherActivity extends LauncherActivity {
47+
48+
}
49+
```
50+
51+
Next, register the Activity in `AndroidManifest.xml`:
52+
53+
```xml
54+
<activity android:name=".OfflineFirstTWALauncherActivity" android:theme="@style/Theme.Design.NoActionBar">
55+
<intent-filter>
56+
<action android:name="android.intent.action.MAIN" />
57+
<category android:name="android.intent.category.LAUNCHER" />
58+
</intent-filter>
59+
<!-- Edit android:value to change the url opened by the Trusted Web Activity -->
60+
<meta-data android:name="android.support.customtabs.trusted.DEFAULT_URL" android:value="https://airhorner.com" />
61+
<!-- This intent-filter adds the Trusted Web Activity to the Android Launcher -->
62+
<intent-filter>
63+
<action android:name="android.intent.action.VIEW" />
64+
<category android:name="android.intent.category.DEFAULT" />
65+
<category android:name="android.intent.category.BROWSABLE" />
66+
<!-- Edit android:host to handle links to the target URL -->
67+
<data android:host="airhorner.com" android:scheme="https" />
68+
</intent-filter>
69+
</activity>
70+
```
71+
72+
The previous code registers `OfflineFirstTWALauncherActivity` as a launcher
73+
activity and defines [https://airhorner.com](https://airhorner.com) as the URL
74+
to open when the TWA launches.
75+
76+
## Handle offline scenarios
77+
78+
First, inside the Activity, override the `shouldLaunchImmediately()` method and
79+
make it return `false`, so that the Trusted Web Activity won't launch
80+
immediately. You can also add extra checks before the initial launch:
81+
82+
```java
83+
@Override
84+
protected boolean shouldLaunchImmediately() {
85+
// launchImmediately() returns `false` so we can check connection
86+
// and then render a fallback page or launch the Trusted Web Activity with `launchTwa()`.
87+
return false;
88+
}
89+
```
90+
91+
Override the `onCreate()` method to check the network status before the TWA
92+
launches. Add a call to `tryLaunchTwa()`, a helper method that will contain that
93+
logic:
94+
95+
```java
96+
@Override
97+
protected void onCreate(Bundle savedInstanceState) {
98+
super.onCreate(savedInstanceState);
99+
tryLaunchTwa();
100+
}
101+
```
102+
103+
Next, implement `tryLaunchTwa()`:
104+
105+
```java
106+
private void tryLaunchTwa() {
107+
// If TWA has already launched successfully, launch TWA immediately.
108+
// Otherwise, check connection status. If online, launch the Trusted Web Activity with `launchTwa()`.
109+
// Otherwise, if offline, render the offline fallback screen.
110+
if (hasTwaLaunchedSuccessfully()) {
111+
launchTwa();
112+
} else if (isOnline()) {
113+
firstTimeLaunchTwa();
114+
} else {
115+
renderOfflineFallback();
116+
}
117+
}
118+
```
119+
120+
The previous code handles three scenarios:
121+
122+
- If the TWA has launched previously, the service worker has
123+
been registered, and the PWA will be able to respond offline. In that case,
124+
call `launchTwa()`, defined in the parent class, to launch the
125+
Trusted Web Activity directly.
126+
- If the TWA hasn't launched previously and the user is online, launch the
127+
Trusted Web Activity for the first time using the `firstTimeLaunchTwa()`
128+
method that you'll implement later.
129+
- If the TWA hasn't already launched and the user is offline, render the native
130+
offline fallback screen.
131+
132+
## Implement helper methods
133+
134+
The final step is to implement the helper methods called by the previous code.
135+
Here's the code for checking offline state `isOnline()`:
136+
137+
```java
138+
private boolean isOnline() {
139+
ConnectivityManager connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
140+
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
141+
return activeNetworkInfo != null && activeNetworkInfo.isConnected();
142+
}
143+
```
144+
145+
Next, implement `hasTwaLaunchedSuccessfully()`, which checks if the TWA has
146+
launched at least once:
147+
148+
```java
149+
private boolean hasTwaLaunchedSuccessfully() {
150+
// Return `true` if the preference "twa_launched_successfully" has already been set.
151+
SharedPreferences sharedPref = getSharedPreferences(getString(R.string.twa_offline_first_preferences_file_key), Context.MODE_PRIVATE);
152+
return sharedPref.getBoolean(getString(R.string.twa_launched_successfully), false);
153+
}
154+
```
155+
156+
The previous code calls the `launchTWA()` from the parent class, and saves the
157+
`twa_launched_successfully` flag in shared preferences. This indeicates that the TWA
158+
has launched successfully, at least once.
159+
160+
The remaining helper method, `renderOfflineFallback()` renders an Android
161+
offline screen.
162+
163+
```java
164+
private void renderOfflineFallback() {
165+
setContentView(R.layout.activity_offline_first_twa);
166+
167+
Button retryBtn = this.findViewById(R.id.retry_btn);
168+
retryBtn.setOnClickListener(new View.OnClickListener() {
169+
public void onClick(View v) {
170+
// Check connection status. If online, launch the Trusted Web Activity for the first time.
171+
if (isOnline()) firstTimeLaunchTwa();
172+
}
173+
});
174+
}
175+
```
176+
177+
Fort his demo, we have defined the `activity_offline_first_twa` layout, which
178+
contains a button to retry, which will, in time, to execute
179+
`firstTimeLaunchTwa()` after checking the connection.
180+
181+
{% Img src="image/26V1DWN36MZr3mUo8ChSBlCpzp43/pzkHBtYxsTXIdsfjFfYF.png",
182+
alt="twa offline - custom offline screen", width="533", height="394" %}
183+
184+
## Conclusion
185+
186+
- The first time a user launches a Progressive Web App (PWA) via Trusted Web
187+
Activity, the service worker won't yet be available.
188+
- To avoid showing the standard offline screen if the user has no connectivity,
189+
you can detect the offline condition and show a fallback offline screen
190+
instead.
191+
- In this guide you learned how to implement that strategy. If you are
192+
interested in checking the code we used throughout this guide, you can find
193+
the complete solution in the [Offline First TWA
194+
Demo](https://github.com/GoogleChrome/android-browser-helper/tree/main/demos/twa-offline-first).

0 commit comments

Comments
 (0)