Migrate five dialogs to Kotlin. https://redmine.stoutner.com/issues/604
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / dialogs / SslCertificateErrorDialog.java
1 /*
2  * Copyright © 2016-2020 Soren Stoutner <soren@stoutner.com>.
3  *
4  * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
5  *
6  * Privacy Browser is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Privacy Browser is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Privacy Browser.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 package com.stoutner.privacybrowser.dialogs;
21
22 import android.annotation.SuppressLint;
23 import android.app.Activity;
24 import android.app.Dialog;
25 import android.content.DialogInterface;
26 import android.content.SharedPreferences;
27 import android.content.res.Configuration;
28 import android.net.Uri;
29 import android.net.http.SslCertificate;
30 import android.net.http.SslError;
31 import android.os.AsyncTask;
32 import android.os.Bundle;
33 import android.preference.PreferenceManager;
34 import android.text.SpannableStringBuilder;
35 import android.text.Spanned;
36 import android.text.style.ForegroundColorSpan;
37 import android.view.LayoutInflater;
38 import android.view.View;
39 import android.view.WindowManager;
40 import android.webkit.SslErrorHandler;
41 import android.widget.TextView;
42
43 import androidx.annotation.NonNull;
44 import androidx.appcompat.app.AlertDialog;
45 import androidx.fragment.app.DialogFragment;
46
47 import com.stoutner.privacybrowser.R;
48 import com.stoutner.privacybrowser.activities.MainWebViewActivity;
49 import com.stoutner.privacybrowser.fragments.WebViewTabFragment;
50 import com.stoutner.privacybrowser.views.NestedScrollWebView;
51
52 import java.lang.ref.WeakReference;
53 import java.net.InetAddress;
54 import java.net.UnknownHostException;
55 import java.text.DateFormat;
56 import java.util.Date;
57
58 public class SslCertificateErrorDialog extends DialogFragment {
59     public static SslCertificateErrorDialog displayDialog(SslError error, long webViewFragmentId) {
60         // Get the various components of the SSL error message.
61         int primaryErrorIntForBundle = error.getPrimaryError();
62         String urlWithErrorForBundle = error.getUrl();
63         SslCertificate sslCertificate = error.getCertificate();
64         String issuedToCNameForBundle = sslCertificate.getIssuedTo().getCName();
65         String issuedToONameForBundle = sslCertificate.getIssuedTo().getOName();
66         String issuedToUNameForBundle = sslCertificate.getIssuedTo().getUName();
67         String issuedByCNameForBundle = sslCertificate.getIssuedBy().getCName();
68         String issuedByONameForBundle = sslCertificate.getIssuedBy().getOName();
69         String issuedByUNameForBundle = sslCertificate.getIssuedBy().getUName();
70         Date startDateForBundle = sslCertificate.getValidNotBeforeDate();
71         Date endDateForBundle = sslCertificate.getValidNotAfterDate();
72
73         // Create an arguments bundle.
74         Bundle argumentsBundle = new Bundle();
75
76         // Store the SSL error message components in a `Bundle`.
77         argumentsBundle.putInt("primary_error_int", primaryErrorIntForBundle);
78         argumentsBundle.putString("url_with_error", urlWithErrorForBundle);
79         argumentsBundle.putString("issued_to_cname", issuedToCNameForBundle);
80         argumentsBundle.putString("issued_to_oname", issuedToONameForBundle);
81         argumentsBundle.putString("issued_to_uname", issuedToUNameForBundle);
82         argumentsBundle.putString("issued_by_cname", issuedByCNameForBundle);
83         argumentsBundle.putString("issued_by_oname", issuedByONameForBundle);
84         argumentsBundle.putString("issued_by_uname", issuedByUNameForBundle);
85         argumentsBundle.putString("start_date", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(startDateForBundle));
86         argumentsBundle.putString("end_date", DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.LONG).format(endDateForBundle));
87         argumentsBundle.putLong("webview_fragment_id", webViewFragmentId);
88
89         // Create a new instance of the SSL certificate error dialog.
90         SslCertificateErrorDialog thisSslCertificateErrorDialog = new SslCertificateErrorDialog();
91
92         // Add the arguments bundle to the new dialog.
93         thisSslCertificateErrorDialog.setArguments(argumentsBundle);
94
95         // Return the new dialog.
96         return thisSslCertificateErrorDialog;
97     }
98
99     // `@SuppressLint("InflateParams")` removes the warning about using `null` as the parent view group when inflating the `AlertDialog`.
100     @SuppressLint("InflateParams")
101     @Override
102     @NonNull
103     public Dialog onCreateDialog(Bundle savedInstanceState) {
104         // Get a handle for the arguments.
105         Bundle arguments = getArguments();
106
107         // Remove the incorrect lint warning that the arguments might be null.
108         assert arguments != null;
109
110         // Get the variables from the bundle.
111         int primaryErrorInt = arguments.getInt("primary_error_int");
112         String urlWithErrors = arguments.getString("url_with_error");
113         String issuedToCName = arguments.getString("issued_to_cname");
114         String issuedToOName = arguments.getString("issued_to_oname");
115         String issuedToUName = arguments.getString("issued_to_uname");
116         String issuedByCName = arguments.getString("issued_by_cname");
117         String issuedByOName = arguments.getString("issued_by_oname");
118         String issuedByUName = arguments.getString("issued_by_uname");
119         String startDate = arguments.getString("start_date");
120         String endDate = arguments.getString("end_date");
121         long webViewFragmentId = arguments.getLong("webview_fragment_id");
122
123         // Get the current position of this WebView fragment.
124         int webViewPosition = MainWebViewActivity.webViewPagerAdapter.getPositionForId(webViewFragmentId);
125
126         // Get the WebView tab fragment.
127         WebViewTabFragment webViewTabFragment = MainWebViewActivity.webViewPagerAdapter.getPageFragment(webViewPosition);
128
129         // Get the fragment view.
130         View fragmentView = webViewTabFragment.getView();
131
132         // Remove the incorrect lint warning below that the fragment view might be null.
133         assert fragmentView != null;
134
135         // Get a handle for the current WebView.
136         NestedScrollWebView nestedScrollWebView = fragmentView.findViewById(R.id.nestedscroll_webview);
137
138         // Get a handle for the SSL error handler.
139         SslErrorHandler sslErrorHandler = nestedScrollWebView.getSslErrorHandler();
140
141         // Get the activity's layout inflater.
142         LayoutInflater layoutInflater = requireActivity().getLayoutInflater();
143
144         // Use an alert dialog builder to create the alert dialog.
145         AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(requireContext(), R.style.PrivacyBrowserAlertDialog);
146
147         // Get the current theme status.
148         int currentThemeStatus = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
149
150         // Set the icon according to the theme.
151         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
152             dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_night);
153         } else {
154             dialogBuilder.setIcon(R.drawable.ssl_certificate_enabled_day);
155         }
156
157         // Set the title.
158         dialogBuilder.setTitle(R.string.ssl_certificate_error);
159
160         // Set the view.  The parent view is `null` because it will be assigned by `AlertDialog`.
161         dialogBuilder.setView(layoutInflater.inflate(R.layout.ssl_certificate_error, null));
162
163         // Set a listener on the cancel button.
164         dialogBuilder.setNegativeButton(R.string.cancel, (DialogInterface dialog, int which) -> {
165             // Check to make sure the SSL error handler is not null.  This might happen if multiple dialogs are displayed at once.
166             if (sslErrorHandler != null) {
167                 // Cancel the request.
168                 sslErrorHandler.cancel();
169
170                 // Reset the SSL error handler.
171                 nestedScrollWebView.resetSslErrorHandler();
172             }
173         });
174
175         // Set a listener on the proceed button.
176         dialogBuilder.setPositiveButton(R.string.proceed, (DialogInterface dialog, int which) -> {
177             // Check to make sure the SSL error handler is not null.  This might happen if multiple dialogs are displayed at once.
178             if (sslErrorHandler != null) {
179                 // Cancel the request.
180                 sslErrorHandler.proceed();
181
182                 // Reset the SSL error handler.
183                 nestedScrollWebView.resetSslErrorHandler();
184             }
185         });
186
187
188         // Create an alert dialog from the alert dialog builder.
189         AlertDialog alertDialog = dialogBuilder.create();
190
191         // Get a handle for the shared preferences.
192         SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
193
194         // Get the screenshot preference.
195         boolean allowScreenshots = sharedPreferences.getBoolean("allow_screenshots", false);
196
197         // Disable screenshots if not allowed.
198         if (!allowScreenshots) {
199             // Remove the warning below that `getWindow()` might be null.
200             assert alertDialog.getWindow() != null;
201
202             // Disable screenshots.
203             alertDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
204         }
205
206         // Get a URI for the URL with errors.
207         Uri uriWithErrors = Uri.parse(urlWithErrors);
208
209         // Get the IP addresses for the URI.
210         new GetIpAddresses(getActivity(), alertDialog).execute(uriWithErrors.getHost());
211
212         // The alert dialog must be shown before the contents can be modified.
213         alertDialog.show();
214
215         // Get handles for the `TextViews`
216         TextView primaryErrorTextView = alertDialog.findViewById(R.id.primary_error);
217         TextView urlTextView = alertDialog.findViewById(R.id.url);
218         TextView issuedToCNameTextView = alertDialog.findViewById(R.id.issued_to_cname);
219         TextView issuedToONameTextView = alertDialog.findViewById(R.id.issued_to_oname);
220         TextView issuedToUNameTextView = alertDialog.findViewById(R.id.issued_to_uname);
221         TextView issuedByTextView = alertDialog.findViewById(R.id.issued_by_textview);
222         TextView issuedByCNameTextView = alertDialog.findViewById(R.id.issued_by_cname);
223         TextView issuedByONameTextView = alertDialog.findViewById(R.id.issued_by_oname);
224         TextView issuedByUNameTextView = alertDialog.findViewById(R.id.issued_by_uname);
225         TextView validDatesTextView = alertDialog.findViewById(R.id.valid_dates_textview);
226         TextView startDateTextView = alertDialog.findViewById(R.id.start_date);
227         TextView endDateTextView = alertDialog.findViewById(R.id.end_date);
228
229         // Remove the incorrect lint warnings below that the views might be null.
230         assert primaryErrorTextView != null;
231         assert urlTextView != null;
232         assert issuedToCNameTextView != null;
233         assert issuedToONameTextView != null;
234         assert issuedToUNameTextView != null;
235         assert issuedByTextView != null;
236         assert issuedByCNameTextView != null;
237         assert issuedByONameTextView != null;
238         assert issuedByUNameTextView != null;
239         assert validDatesTextView != null;
240         assert startDateTextView != null;
241         assert endDateTextView != null;
242
243         // Setup the common strings.
244         String urlLabel = getString(R.string.url_label) + "  ";
245         String cNameLabel = getString(R.string.common_name) + "  ";
246         String oNameLabel = getString(R.string.organization) + "  ";
247         String uNameLabel = getString(R.string.organizational_unit) + "  ";
248         String startDateLabel = getString(R.string.start_date) + "  ";
249         String endDateLabel = getString(R.string.end_date) + "  ";
250
251         // Create a spannable string builder for each text view that needs multiple colors of text.
252         SpannableStringBuilder urlStringBuilder = new SpannableStringBuilder(urlLabel + urlWithErrors);
253         SpannableStringBuilder issuedToCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedToCName);
254         SpannableStringBuilder issuedToONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedToOName);
255         SpannableStringBuilder issuedToUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedToUName);
256         SpannableStringBuilder issuedByCNameStringBuilder = new SpannableStringBuilder(cNameLabel + issuedByCName);
257         SpannableStringBuilder issuedByONameStringBuilder = new SpannableStringBuilder(oNameLabel + issuedByOName);
258         SpannableStringBuilder issuedByUNameStringBuilder = new SpannableStringBuilder(uNameLabel + issuedByUName);
259         SpannableStringBuilder startDateStringBuilder = new SpannableStringBuilder(startDateLabel + startDate);
260         SpannableStringBuilder endDateStringBuilder = new SpannableStringBuilder((endDateLabel + endDate));
261
262         // Define the color spans.
263         ForegroundColorSpan blueColorSpan;
264         ForegroundColorSpan redColorSpan;
265
266         // Set the color spans according to the theme.  The deprecated `getResources()` must be used until the minimum API >= 23.
267         if (currentThemeStatus == Configuration.UI_MODE_NIGHT_NO) {
268             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.blue_700));
269             redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_a700));
270         } else {
271             blueColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.violet_500));
272             redColorSpan = new ForegroundColorSpan(getResources().getColor(R.color.red_900));
273         }
274
275         // Setup the spans to display the certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
276         urlStringBuilder.setSpan(blueColorSpan, urlLabel.length(), urlStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
277         issuedToCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
278         issuedToONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedToONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
279         issuedToUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedToUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
280         issuedByCNameStringBuilder.setSpan(blueColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
281         issuedByONameStringBuilder.setSpan(blueColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
282         issuedByUNameStringBuilder.setSpan(blueColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
283         startDateStringBuilder.setSpan(blueColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
284         endDateStringBuilder.setSpan(blueColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
285
286         // Initialize `primaryErrorString`.
287         String primaryErrorString = "";
288
289         // Highlight the primary error in red and store the primary error string in `primaryErrorString`.
290         switch (primaryErrorInt) {
291             case SslError.SSL_IDMISMATCH:
292                 // Change the URL span colors to red.
293                 urlStringBuilder.setSpan(redColorSpan, urlLabel.length(), urlStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
294                 issuedToCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedToCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
295
296                 // Store the primary error string.
297                 primaryErrorString = getString(R.string.cn_mismatch);
298                 break;
299
300             case SslError.SSL_UNTRUSTED:
301                 // Change the issued by text view text to red.  The deprecated `getResources().getColor` must be used until the minimum API >= 23.
302                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
303                     issuedByTextView.setTextColor(getResources().getColor(R.color.red_900));
304                 } else {
305                     issuedByTextView.setTextColor(getResources().getColor(R.color.red_a700));
306                 }
307
308                 // Change the issued by span color to red.
309                 issuedByCNameStringBuilder.setSpan(redColorSpan, cNameLabel.length(), issuedByCNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
310                 issuedByONameStringBuilder.setSpan(redColorSpan, oNameLabel.length(), issuedByONameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
311                 issuedByUNameStringBuilder.setSpan(redColorSpan, uNameLabel.length(), issuedByUNameStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
312
313                 // Store the primary error string.
314                 primaryErrorString = getString(R.string.untrusted);
315                 break;
316
317             case SslError.SSL_DATE_INVALID:
318                 // Change the valid dates text view text to red.  The deprecated `getResources().getColor` must be used until the minimum API >= 23.
319                 if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
320                     validDatesTextView.setTextColor(getResources().getColor(R.color.red_900));
321                 } else {
322                     validDatesTextView.setTextColor(getResources().getColor(R.color.red_a700));
323                 }
324
325                 // Change the date span colors to red.
326                 startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
327                 endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
328
329                 // Store the primary error string.
330                 primaryErrorString = getString(R.string.invalid_date);
331                 break;
332
333             case SslError.SSL_NOTYETVALID:
334                 // Change the start date span color to red.
335                 startDateStringBuilder.setSpan(redColorSpan, startDateLabel.length(), startDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
336
337                 // Store the primary error string.
338                 primaryErrorString = getString(R.string.future_certificate);
339                 break;
340
341             case SslError.SSL_EXPIRED:
342                 // Change the end date span color to red.
343                 endDateStringBuilder.setSpan(redColorSpan, endDateLabel.length(), endDateStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
344
345                 // Store the primary error string.
346                 primaryErrorString = getString(R.string.expired_certificate);
347                 break;
348
349             case SslError.SSL_INVALID:
350                 // Store the primary error string.
351                 primaryErrorString = getString(R.string.invalid_certificate);
352                 break;
353         }
354
355
356         // Display the strings.
357         primaryErrorTextView.setText(primaryErrorString);
358         urlTextView.setText(urlStringBuilder);
359         issuedToCNameTextView.setText(issuedToCNameStringBuilder);
360         issuedToONameTextView.setText(issuedToONameStringBuilder);
361         issuedToUNameTextView.setText(issuedToUNameStringBuilder);
362         issuedByCNameTextView.setText(issuedByCNameStringBuilder);
363         issuedByONameTextView.setText(issuedByONameStringBuilder);
364         issuedByUNameTextView.setText(issuedByUNameStringBuilder);
365         startDateTextView.setText(startDateStringBuilder);
366         endDateTextView.setText(endDateStringBuilder);
367
368         // `onCreateDialog` requires the return of an alert dialog.
369         return alertDialog;
370     }
371
372
373     // This must run asynchronously because it involves a network request.  `String` declares the parameters.  `Void` does not declare progress units.  `SpannableStringBuilder` contains the results.
374     private static class GetIpAddresses extends AsyncTask<String, Void, SpannableStringBuilder> {
375         // The weak references are used to determine if the activity or the alert dialog have disappeared while the AsyncTask is running.
376         private WeakReference<Activity> activityWeakReference;
377         private WeakReference<AlertDialog> alertDialogWeakReference;
378
379         GetIpAddresses(Activity activity, AlertDialog alertDialog) {
380             // Populate the weak references.
381             activityWeakReference = new WeakReference<>(activity);
382             alertDialogWeakReference = new WeakReference<>(alertDialog);
383         }
384
385         @Override
386         protected SpannableStringBuilder doInBackground(String... domainName) {
387             // Get handles for the activity and the alert dialog.
388             Activity activity = activityWeakReference.get();
389             AlertDialog alertDialog = alertDialogWeakReference.get();
390
391             // Abort if the activity or the dialog is gone.
392             if ((activity == null) || (activity.isFinishing()) || (alertDialog == null)) {
393                 return new SpannableStringBuilder();
394             }
395
396             // Initialize an IP address string builder.
397             StringBuilder ipAddresses = new StringBuilder();
398
399             // Get an array with the IP addresses for the host.
400             try {
401                 // Get an array with all the IP addresses for the domain.
402                 InetAddress[] inetAddressesArray = InetAddress.getAllByName(domainName[0]);
403
404                 // Add each IP address to the string builder.
405                 for (InetAddress inetAddress : inetAddressesArray) {
406                     // Check to see if this is not the first IP address.
407                     if (ipAddresses.length() > 0) {
408                         // Add a line break to the string builder first.
409                         ipAddresses.append("\n");
410                     }
411
412                     // Add the IP Address to the string builder.
413                     ipAddresses.append(inetAddress.getHostAddress());
414                 }
415             } catch (UnknownHostException exception) {
416                 // Do nothing.
417             }
418
419             // Set the label.
420             String ipAddressesLabel = activity.getString(R.string.ip_addresses) + "  ";
421
422             // Create a spannable string builder.
423             SpannableStringBuilder ipAddressesStringBuilder = new SpannableStringBuilder(ipAddressesLabel + ipAddresses);
424
425             // Create a blue foreground color span.
426             ForegroundColorSpan blueColorSpan;
427
428             // Get the current theme status.
429             int currentThemeStatus = activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
430
431             // Set the blue color span according to the theme.  The deprecated `getColor()` must be used until the minimum API >= 23.
432             if (currentThemeStatus == Configuration.UI_MODE_NIGHT_YES) {
433                 blueColorSpan = new ForegroundColorSpan(activity.getResources().getColor(R.color.violet_500));
434             } else {
435                 blueColorSpan = new ForegroundColorSpan(activity.getResources().getColor(R.color.blue_700));
436             }
437
438             // Set the string builder to display the certificate information in blue.  `SPAN_INCLUSIVE_INCLUSIVE` allows the span to grow in either direction.
439             ipAddressesStringBuilder.setSpan(blueColorSpan, ipAddressesLabel.length(), ipAddressesStringBuilder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
440
441             // Return the formatted string.
442             return ipAddressesStringBuilder;
443         }
444
445         // `onPostExecute()` operates on the UI thread.
446         @Override
447         protected void onPostExecute(SpannableStringBuilder ipAddresses) {
448             // Get handles for the activity and the alert dialog.
449             Activity activity = activityWeakReference.get();
450             AlertDialog alertDialog = alertDialogWeakReference.get();
451
452             // Abort if the activity or the alert dialog is gone.
453             if ((activity == null) || (activity.isFinishing()) || (alertDialog == null)) {
454                 return;
455             }
456
457             // Get a handle for the IP addresses text view.
458             TextView ipAddressesTextView = alertDialog.findViewById(R.id.ip_addresses);
459
460             // Remove the incorrect lint warning below that the view might be null.
461             assert ipAddressesTextView != null;
462
463             // Populate the IP addresses text view.
464             ipAddressesTextView.setText(ipAddresses);
465         }
466     }
467 }