Skip to content

Commit 0d0cf10

Browse files
committed
handle WebView crashes by showing an alert page
1 parent 0b4b603 commit 0d0cf10

File tree

3 files changed

+91
-25
lines changed

3 files changed

+91
-25
lines changed

app/src/main/java/app/grapheneos/pdfviewer/PdfViewer.java

Lines changed: 61 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import android.view.View;
1717
import android.webkit.CookieManager;
1818
import android.webkit.JavascriptInterface;
19+
import android.webkit.RenderProcessGoneDetail;
1920
import android.webkit.WebResourceRequest;
2021
import android.webkit.WebResourceResponse;
2122
import android.webkit.WebSettings;
@@ -58,6 +59,7 @@
5859
public class PdfViewer extends AppCompatActivity implements LoaderManager.LoaderCallbacks<List<CharSequence>> {
5960
private static final String TAG = "PdfViewer";
6061

62+
private static final String STATE_WEBVIEW_CRASHED = "webview_crashed";
6163
private static final String STATE_URI = "uri";
6264
private static final String STATE_PAGE = "page";
6365
private static final String STATE_ZOOM_RATIO = "zoomRatio";
@@ -116,6 +118,7 @@ public class PdfViewer extends AppCompatActivity implements LoaderManager.Loader
116118
private static final int STATE_END = 2;
117119
private static final int PADDING = 10;
118120

121+
private boolean webViewCrashed;
119122
private Uri mUri;
120123
public int mPage;
121124
public int mNumPages;
@@ -264,10 +267,19 @@ public String getPassword() {
264267
}
265268
}
266269

270+
private void showWebViewCrashed() {
271+
binding.webviewAlertTitle.setText(getString(R.string.webview_crash_title));
272+
binding.webviewAlertMessage.setText(getString(R.string.webview_crash_message));
273+
binding.webviewAlertLayout.setVisibility(View.VISIBLE);
274+
binding.webviewAlertReload.setVisibility(View.VISIBLE);
275+
binding.webview.setVisibility(View.GONE);
276+
}
277+
267278
@Override
268279
@SuppressLint({"SetJavaScriptEnabled"})
269280
protected void onCreate(Bundle savedInstanceState) {
270281
super.onCreate(savedInstanceState);
282+
271283
binding = PdfviewerBinding.inflate(getLayoutInflater());
272284
setContentView(binding.getRoot());
273285
setSupportActionBar(binding.toolbar);
@@ -395,6 +407,18 @@ public void onPageFinished(WebView view, String url) {
395407
invalidateOptionsMenu();
396408
loadPdfWithPassword(mEncryptedDocumentPassword);
397409
}
410+
411+
@Override
412+
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
413+
if (detail.didCrash()) {
414+
webViewCrashed = true;
415+
showWebViewCrashed();
416+
invalidateOptionsMenu();
417+
purgeWebView();
418+
return true;
419+
}
420+
return false;
421+
}
398422
});
399423

400424
GestureHelper.attach(PdfViewer.this, binding.webview,
@@ -455,6 +479,7 @@ public void onZoomEnd() {
455479
}
456480

457481
if (savedInstanceState != null) {
482+
webViewCrashed = savedInstanceState.getBoolean(STATE_WEBVIEW_CRASHED);
458483
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
459484
mUri = savedInstanceState.getParcelable(STATE_URI, Uri.class);
460485
} else {
@@ -468,7 +493,14 @@ public void onZoomEnd() {
468493
mEncryptedDocumentPassword = savedInstanceState.getString(STATE_ENCRYPTED_DOCUMENT_PASSWORD);
469494
}
470495

471-
if (mUri != null) {
496+
binding.webviewAlertReload.setOnClickListener(v -> {
497+
webViewCrashed = false;
498+
recreate();
499+
});
500+
501+
if (webViewCrashed) {
502+
showWebViewCrashed();
503+
} else if (mUri != null) {
472504
if ("file".equals(mUri.getScheme())) {
473505
snackbar.setText(R.string.legacy_file_uri).show();
474506
return;
@@ -478,14 +510,16 @@ public void onZoomEnd() {
478510
}
479511
}
480512

481-
513+
private void purgeWebView() {
514+
binding.webview.removeJavascriptInterface("channel");
515+
binding.getRoot().removeView(binding.webview);
516+
binding.webview.destroy();
517+
}
482518

483519
@Override
484520
protected void onDestroy() {
485521
super.onDestroy();
486-
binding.webview.removeJavascriptInterface("channel");
487-
binding.getRoot().removeView(binding.webview);
488-
binding.webview.destroy();
522+
purgeWebView();
489523
maybeCloseInputStream();
490524
}
491525

@@ -525,15 +559,18 @@ private void setToolbarTitleWithDocumentName() {
525559
protected void onResume() {
526560
super.onResume();
527561

528-
// The user could have left the activity to update the WebView
529-
invalidateOptionsMenu();
530-
if (getWebViewRelease() >= MIN_WEBVIEW_RELEASE) {
531-
binding.webviewOutOfDateLayout.setVisibility(View.GONE);
532-
binding.webview.setVisibility(View.VISIBLE);
533-
} else {
534-
binding.webview.setVisibility(View.GONE);
535-
binding.webviewOutOfDateMessage.setText(getString(R.string.webview_out_of_date_message, getWebViewRelease(), MIN_WEBVIEW_RELEASE));
536-
binding.webviewOutOfDateLayout.setVisibility(View.VISIBLE);
562+
if (!webViewCrashed) {
563+
// The user could have left the activity to update the WebView
564+
invalidateOptionsMenu();
565+
if (getWebViewRelease() >= MIN_WEBVIEW_RELEASE) {
566+
binding.webviewAlertLayout.setVisibility(View.GONE);
567+
binding.webview.setVisibility(View.VISIBLE);
568+
} else {
569+
binding.webview.setVisibility(View.GONE);
570+
binding.webviewAlertTitle.setText(getString(R.string.webview_out_of_date_title));
571+
binding.webviewAlertMessage.setText(getString(R.string.webview_out_of_date_message, getWebViewRelease(), MIN_WEBVIEW_RELEASE));
572+
binding.webviewAlertLayout.setVisibility(View.VISIBLE);
573+
}
537574
}
538575
}
539576

@@ -645,6 +682,7 @@ private void hideSystemUi() {
645682
@Override
646683
public void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
647684
super.onSaveInstanceState(savedInstanceState);
685+
savedInstanceState.putBoolean(STATE_WEBVIEW_CRASHED, webViewCrashed);
648686
savedInstanceState.putParcelable(STATE_URI, mUri);
649687
savedInstanceState.putInt(STATE_PAGE, mPage);
650688
savedInstanceState.putFloat(STATE_ZOOM_RATIO, mZoomRatio);
@@ -703,14 +741,22 @@ public boolean onPrepareOptionsMenu(@NonNull Menu menu) {
703741
mDocumentState = STATE_END;
704742
}
705743

706-
enableDisableMenuItem(menu.findItem(R.id.action_open), getWebViewRelease() >= MIN_WEBVIEW_RELEASE);
744+
745+
enableDisableMenuItem(menu.findItem(R.id.action_open),
746+
!webViewCrashed && getWebViewRelease() >= MIN_WEBVIEW_RELEASE);
707747
enableDisableMenuItem(menu.findItem(R.id.action_share), mUri != null);
708748
enableDisableMenuItem(menu.findItem(R.id.action_next), mPage < mNumPages);
709749
enableDisableMenuItem(menu.findItem(R.id.action_previous), mPage > 1);
710750
enableDisableMenuItem(menu.findItem(R.id.action_save_as), mUri != null);
711751

712752
menu.findItem(R.id.action_outline).setVisible(viewModel.hasOutline());
713753

754+
if (webViewCrashed) {
755+
for (final int id : ids) {
756+
enableDisableMenuItem(menu.findItem(id), false);
757+
}
758+
}
759+
714760
return true;
715761
}
716762

app/src/main/res/layout/pdfviewer.xml

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
2626

2727
<ScrollView
28-
android:id="@+id/webview_out_of_date_layout"
28+
android:id="@+id/webview_alert_layout"
2929
android:layout_width="match_parent"
3030
android:layout_height="match_parent"
3131
android:fillViewport="true"
@@ -43,15 +43,15 @@
4343
android:layout_marginTop="32dp"
4444
android:importantForAccessibility="no"
4545
android:src="@drawable/ic_error_outline_24dp"
46-
app:layout_constraintBottom_toTopOf="@id/webview_out_of_date_title"
46+
app:layout_constraintBottom_toTopOf="@id/webview_alert_title"
4747
app:layout_constraintEnd_toEndOf="parent"
4848
app:layout_constraintStart_toStartOf="parent"
4949
app:layout_constraintTop_toTopOf="parent"
5050
app:layout_constraintVertical_chainStyle="packed"
5151
app:tint="?attr/colorPrimary" />
5252

5353
<TextView
54-
android:id="@+id/webview_out_of_date_title"
54+
android:id="@+id/webview_alert_title"
5555
style="@style/TextAppearance.Material3.TitleLarge"
5656
android:layout_width="match_parent"
5757
android:layout_height="wrap_content"
@@ -60,15 +60,13 @@
6060
android:layout_marginEnd="32dp"
6161
android:elegantTextHeight="true"
6262
android:gravity="center"
63-
android:text="@string/webview_out_of_date_title"
64-
app:layout_constraintBottom_toTopOf="@id/webview_out_of_date_message"
63+
app:layout_constraintBottom_toTopOf="@id/webview_alert_message"
6564
app:layout_constraintEnd_toEndOf="parent"
6665
app:layout_constraintStart_toStartOf="parent"
6766
app:layout_constraintTop_toBottomOf="@id/error_image_view" />
6867

69-
7068
<TextView
71-
android:id="@+id/webview_out_of_date_message"
69+
android:id="@+id/webview_alert_message"
7270
style="@style/TextAppearance.Material3.BodyMedium"
7371
android:layout_width="match_parent"
7472
android:layout_height="wrap_content"
@@ -79,14 +77,31 @@
7977
android:elegantTextHeight="true"
8078
android:gravity="center"
8179
android:lineSpacingMultiplier="1.2"
82-
android:text="@string/webview_out_of_date_message"
80+
app:layout_constraintBottom_toBottomOf="@id/webview_alert_reload"
81+
app:layout_constraintEnd_toEndOf="parent"
82+
app:layout_constraintStart_toStartOf="parent"
83+
app:layout_constraintTop_toBottomOf="@id/webview_alert_title" />
84+
85+
<Button
86+
android:id="@+id/webview_alert_reload"
87+
android:layout_width="match_parent"
88+
android:layout_height="wrap_content"
89+
android:layout_marginStart="32dp"
90+
android:layout_marginTop="8dp"
91+
android:layout_marginEnd="32dp"
92+
android:layout_marginBottom="32dp"
93+
android:elegantTextHeight="true"
94+
android:gravity="center"
95+
android:lineSpacingMultiplier="1.2"
96+
android:text="@string/reload"
97+
android:visibility="gone"
8398
app:layout_constraintBottom_toBottomOf="parent"
8499
app:layout_constraintEnd_toEndOf="parent"
85100
app:layout_constraintStart_toStartOf="parent"
86-
app:layout_constraintTop_toBottomOf="@id/webview_out_of_date_title" />
101+
app:layout_constraintTop_toBottomOf="@id/webview_alert_message" />
87102

88103
</androidx.constraintlayout.widget.ConstraintLayout>
89104

90105
</ScrollView>
91106

92-
</androidx.coordinatorlayout.widget.CoordinatorLayout>
107+
</androidx.coordinatorlayout.widget.CoordinatorLayout>

app/src/main/res/values/strings.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,11 @@
3333
<string name="webview_out_of_date_title">WebView out-of-date</string>
3434
<string name="webview_out_of_date_message" tools:ignore="PluralsCandidate">Your current WebView version is %1$d. The WebView should be at least version %2$d for the PDF Viewer to work.</string>
3535

36+
<string name="webview_crash_title">WebView crashed</string>
37+
<string name="webview_crash_message">Android WebView render process crashed.</string>
38+
39+
<string name="reload">Reload</string>
40+
3641
<string name="password_prompt_hint">Password</string>
3742
<string name="password_prompt_description">Enter the password to decrypt this PDF file</string>
3843
<string name="open">Open</string>

0 commit comments

Comments
 (0)