2 * Copyright © 2017-2019 Soren Stoutner <soren@stoutner.com>.
4 * This file is part of Privacy Browser <https://www.stoutner.com/privacy-browser>.
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.
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.
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/>.
20 package com.stoutner.privacybrowser.asynctasks;
22 import android.app.Activity;
23 import android.content.SharedPreferences;
24 import android.graphics.Typeface;
25 import android.os.AsyncTask;
26 import android.os.Build;
27 import android.os.LocaleList;
28 import android.preference.PreferenceManager;
29 import android.text.SpannableStringBuilder;
30 import android.text.Spanned;
31 import android.text.style.StyleSpan;
32 import android.view.View;
33 import android.webkit.CookieManager;
34 import android.widget.ProgressBar;
35 import android.widget.TextView;
37 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
39 import com.stoutner.privacybrowser.R;
41 import java.io.BufferedInputStream;
42 import java.io.ByteArrayOutputStream;
43 import java.io.IOException;
44 import java.io.InputStream;
45 import java.lang.ref.WeakReference;
46 import java.net.HttpURLConnection;
48 import java.util.Locale;
50 // This must run asynchronously because it involves a network request. `String` declares the parameters. `Void` does not declare progress units. `SpannableStringBuilder[]` contains the results.
51 public class GetSource extends AsyncTask<String, Void, SpannableStringBuilder[]> {
52 // Declare a weak reference to the calling activity.
53 private WeakReference<Activity> activityWeakReference;
55 // Store the user agent.
56 private String userAgent;
58 public GetSource(Activity activity, String userAgent) {
59 // Populate the weak reference to the calling activity.
60 activityWeakReference = new WeakReference<>(activity);
62 // Store the user agent.
63 this.userAgent = userAgent;
66 // `onPreExecute()` operates on the UI thread.
68 protected void onPreExecute() {
69 // Get a handle for the activity.
70 Activity viewSourceActivity = activityWeakReference.get();
72 // Abort if the activity is gone.
73 if ((viewSourceActivity == null) || viewSourceActivity.isFinishing()) {
77 // Get a handle for the progress bar.
78 ProgressBar progressBar = viewSourceActivity.findViewById(R.id.progress_bar);
80 // Make the progress bar visible.
81 progressBar.setVisibility(View.VISIBLE);
83 // Set the progress bar to be indeterminate.
84 progressBar.setIndeterminate(true);
88 protected SpannableStringBuilder[] doInBackground(String... formattedUrlString) {
89 // Initialize the response body String.
90 SpannableStringBuilder requestHeadersBuilder = new SpannableStringBuilder();
91 SpannableStringBuilder responseMessageBuilder = new SpannableStringBuilder();
92 SpannableStringBuilder responseHeadersBuilder = new SpannableStringBuilder();
93 SpannableStringBuilder responseBodyBuilder = new SpannableStringBuilder();
95 // Get a handle for the activity.
96 Activity activity = activityWeakReference.get();
98 // Abort if the activity is gone.
99 if ((activity == null) || activity.isFinishing()) {
100 return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder};
103 // Because everything relating to requesting data from a webserver can throw errors, the entire section must catch `IOExceptions`.
105 // Get the current URL from the main activity.
106 URL url = new URL(formattedUrlString[0]);
108 // Open a connection to the URL. No data is actually sent at this point.
109 HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();
111 // Define the variables necessary to build the request headers.
112 requestHeadersBuilder = new SpannableStringBuilder();
113 int oldRequestHeadersBuilderLength;
114 int newRequestHeadersBuilderLength;
117 // Set the `Host` header property.
118 httpUrlConnection.setRequestProperty("Host", url.getHost());
120 // Add the `Host` header to the string builder and format the text.
121 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
122 requestHeadersBuilder.append("Host", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
123 } else { // Older versions not so much.
124 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
125 requestHeadersBuilder.append("Host");
126 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
127 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
129 requestHeadersBuilder.append(": ");
130 requestHeadersBuilder.append(url.getHost());
133 // Set the `Connection` header property.
134 httpUrlConnection.setRequestProperty("Connection", "keep-alive");
136 // Add the `Connection` header to the string builder and format the text.
137 requestHeadersBuilder.append(System.getProperty("line.separator"));
138 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
139 requestHeadersBuilder.append("Connection", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
140 } else { // Older versions not so much.
141 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
142 requestHeadersBuilder.append("Connection");
143 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
144 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
146 requestHeadersBuilder.append(": keep-alive");
149 // Set the `Upgrade-Insecure-Requests` header property.
150 httpUrlConnection.setRequestProperty("Upgrade-Insecure-Requests", "1");
152 // Add the `Upgrade-Insecure-Requests` header to the string builder and format the text.
153 requestHeadersBuilder.append(System.getProperty("line.separator"));
154 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
155 requestHeadersBuilder.append("Upgrade-Insecure-Requests", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
156 } else { // Older versions not so much.
157 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
158 requestHeadersBuilder.append("Upgrade-Insecure_Requests");
159 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
160 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
162 requestHeadersBuilder.append(": 1");
165 // Set the `User-Agent` header property.
166 httpUrlConnection.setRequestProperty("User-Agent", userAgent);
168 // Add the `User-Agent` header to the string builder and format the text.
169 requestHeadersBuilder.append(System.getProperty("line.separator"));
170 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
171 requestHeadersBuilder.append("User-Agent", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
172 } else { // Older versions not so much.
173 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
174 requestHeadersBuilder.append("User-Agent");
175 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
176 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
178 requestHeadersBuilder.append(": ");
179 requestHeadersBuilder.append(userAgent);
182 // Set the `x-requested-with` header property.
183 httpUrlConnection.setRequestProperty("x-requested-with", "");
185 // Add the `x-requested-with` header to the string builder and format the text.
186 requestHeadersBuilder.append(System.getProperty("line.separator"));
187 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
188 requestHeadersBuilder.append("x-requested-with", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
189 } else { // Older versions not so much.
190 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
191 requestHeadersBuilder.append("x-requested-with");
192 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
193 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
195 requestHeadersBuilder.append(": ");
198 // Set the `Sec-Fetch-Site` header property.
199 httpUrlConnection.setRequestProperty("Sec-Fetch-Site", "none");
201 // Add the `Sec-Fetch-Site` header to the string builder and format the text.
202 requestHeadersBuilder.append(System.getProperty("line.separator"));
203 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
204 requestHeadersBuilder.append("Sec-Fetch-Site", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
205 } else { // Older versions not so much.
206 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
207 requestHeadersBuilder.append("Sec-Fetch-Site");
208 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
209 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
211 requestHeadersBuilder.append(": none");
214 // Set the `Sec-Fetch-Mode` header property.
215 httpUrlConnection.setRequestProperty("Sec-Fetch-Mode", "navigate");
217 // Add the `Sec-Fetch-Mode` header to the string builder and format the text.
218 requestHeadersBuilder.append(System.getProperty("line.separator"));
219 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
220 requestHeadersBuilder.append("Sec-Fetch-Mode", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
221 } else { // Older versions not so much.
222 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
223 requestHeadersBuilder.append("Sec-Fetch-Mode");
224 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
225 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
227 requestHeadersBuilder.append(": navigate");
230 // Set the `Sec-Fetch-User` header property.
231 httpUrlConnection.setRequestProperty("Sec-Fetch-User", "?1");
233 // Add the `Sec-Fetch-User` header to the string builder and format the text.
234 requestHeadersBuilder.append(System.getProperty("line.separator"));
235 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
236 requestHeadersBuilder.append("Sec-Fetch-User", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
237 } else { // Older versions not so much.
238 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
239 requestHeadersBuilder.append("Sec-Fetch-User");
240 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
241 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
243 requestHeadersBuilder.append(": ?1");
246 // Get a handle for the shared preferences.
247 SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
249 // Only populate `Do Not Track` if it is enabled.
250 if (sharedPreferences.getBoolean("do_not_track", false)) {
251 // Set the `dnt` header property.
252 httpUrlConnection.setRequestProperty("dnt", "1");
254 // Add the `dnt` header to the string builder and format the text.
255 requestHeadersBuilder.append(System.getProperty("line.separator"));
256 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
257 requestHeadersBuilder.append("dnt", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
258 } else { // Older versions not so much.
259 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
260 requestHeadersBuilder.append("dnt");
261 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
262 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
264 requestHeadersBuilder.append(": 1");
268 // Set the `Accept` header property.
269 httpUrlConnection.setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3");
271 // Add the `Accept` header to the string builder and format the text.
272 requestHeadersBuilder.append(System.getProperty("line.separator"));
273 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
274 requestHeadersBuilder.append("Accept", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
275 } else { // Older versions not so much.
276 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
277 requestHeadersBuilder.append("Accept");
278 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
279 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
281 requestHeadersBuilder.append(": ");
282 requestHeadersBuilder.append("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3");
285 // Instantiate a locale string.
288 // Populate the locale string.
289 if (Build.VERSION.SDK_INT >= 24) { // SDK >= 24 has a list of locales.
290 // Get the list of locales.
291 LocaleList localeList = activity.getResources().getConfiguration().getLocales();
293 // Initialize a string builder to extract the locales from the list.
294 StringBuilder localesStringBuilder = new StringBuilder();
296 // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages.
299 // Populate the string builder with the contents of the locales list.
300 for (int i = 0; i < localeList.size(); i++) {
301 // Append a comma if there is already an item in the string builder.
303 localesStringBuilder.append(",");
306 // Get the locale from the list.
307 Locale locale = localeList.get(i);
309 // Add the locale to the string. `locale` by default displays as `en_US`, but WebView uses the `en-US` format.
310 localesStringBuilder.append(locale.getLanguage());
311 localesStringBuilder.append("-");
312 localesStringBuilder.append(locale.getCountry());
314 // If not the first locale, append `;q=0.x`, which drops by .1 for each removal from the main locale until q=0.1.
316 localesStringBuilder.append(";q=0.");
317 localesStringBuilder.append(q);
320 // Decrement `q` if it is greater than 1.
325 // Add a second entry for the language only portion of the locale.
326 localesStringBuilder.append(",");
327 localesStringBuilder.append(locale.getLanguage());
329 // Append `1;q=0.x`, which drops by .1 for each removal form the main locale until q=0.1.
330 localesStringBuilder.append(";q=0.");
331 localesStringBuilder.append(q);
333 // Decrement `q` if it is greater than 1.
339 // Store the populated string builder in the locale string.
340 localeString = localesStringBuilder.toString();
341 } else { // SDK < 24 only has a primary locale.
342 // Store the locale in the locale string.
343 localeString = Locale.getDefault().toString();
346 // Set the `Accept-Language` header property.
347 httpUrlConnection.setRequestProperty("Accept-Language", localeString);
349 // Add the `Accept-Language` header to the string builder and format the text.
350 requestHeadersBuilder.append(System.getProperty("line.separator"));
351 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
352 requestHeadersBuilder.append("Accept-Language", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
353 } else { // Older versions not so much.
354 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
355 requestHeadersBuilder.append("Accept-Language");
356 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
357 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
359 requestHeadersBuilder.append(": ");
360 requestHeadersBuilder.append(localeString);
363 // Get the cookies for the current domain.
364 String cookiesString = CookieManager.getInstance().getCookie(url.toString());
366 // Only process the cookies if they are not null.
367 if (cookiesString != null) {
368 // Set the `Cookie` header property.
369 httpUrlConnection.setRequestProperty("Cookie", cookiesString);
371 // Add the `Cookie` header to the string builder and format the text.
372 requestHeadersBuilder.append(System.getProperty("line.separator"));
373 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
374 requestHeadersBuilder.append("Cookie", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
375 } else { // Older versions not so much.
376 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
377 requestHeadersBuilder.append("Cookie");
378 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
379 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
381 requestHeadersBuilder.append(": ");
382 requestHeadersBuilder.append(cookiesString);
386 // `HttpUrlConnection` sets `Accept-Encoding` to be `gzip` by default. If the property is manually set, than `HttpUrlConnection` does not process the decoding.
387 // Add the `Accept-Encoding` header to the string builder and format the text.
388 requestHeadersBuilder.append(System.getProperty("line.separator"));
389 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
390 requestHeadersBuilder.append("Accept-Encoding", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
391 } else { // Older versions not so much.
392 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
393 requestHeadersBuilder.append("Accept-Encoding");
394 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
395 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
397 requestHeadersBuilder.append(": gzip");
400 // The actual network request is in a `try` bracket so that `disconnect()` is run in the `finally` section even if an error is encountered in the main block.
402 // Initialize the string builders.
403 responseMessageBuilder = new SpannableStringBuilder();
404 responseHeadersBuilder = new SpannableStringBuilder();
406 // Get the response code, which causes the connection to the server to be made.
407 int responseCode = httpUrlConnection.getResponseCode();
409 // Populate the response message string builder.
410 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
411 responseMessageBuilder.append(String.valueOf(responseCode), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
412 } else { // Older versions not so much.
413 responseMessageBuilder.append(String.valueOf(responseCode));
414 int newLength = responseMessageBuilder.length();
415 responseMessageBuilder.setSpan(new StyleSpan(Typeface.BOLD), 0, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
417 responseMessageBuilder.append(": ");
418 responseMessageBuilder.append(httpUrlConnection.getResponseMessage());
420 // Initialize the iteration variable.
423 // Iterate through the received header fields.
424 while (httpUrlConnection.getHeaderField(i) != null) {
425 // Add a new line if there is already information in the string builder.
427 responseHeadersBuilder.append(System.getProperty("line.separator"));
430 // Add the header to the string builder and format the text.
431 if (Build.VERSION.SDK_INT >= 21) { // Newer versions of Android are so smart.
432 responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
433 } else { // Older versions not so much.
434 int oldLength = responseHeadersBuilder.length();
435 responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i));
436 int newLength = responseHeadersBuilder.length();
437 responseHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldLength, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
439 responseHeadersBuilder.append(": ");
440 responseHeadersBuilder.append(httpUrlConnection.getHeaderField(i));
442 // Increment the iteration variable.
446 // Instantiate an input stream for the response body.
447 InputStream inputStream;
449 // Get the correct input stream based on the response code.
450 if (responseCode == 404) { // Get the error stream.
451 inputStream = new BufferedInputStream(httpUrlConnection.getErrorStream());
452 } else { // Get the response body stream.
453 inputStream = new BufferedInputStream(httpUrlConnection.getInputStream());
456 // Initialize the byte array output stream and the conversion buffer byte array.
457 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
458 byte[] conversionBufferByteArray = new byte[1024];
460 // Instantiate the variable to track the buffer length.
464 // Attempt to read data from the input stream and store it in the conversion buffer byte array. Also store the amount of data transferred in the buffer length variable.
465 while ((bufferLength = inputStream.read(conversionBufferByteArray)) > 0) { // Proceed while the amount of data stored in the buffer is > 0.
466 // Write the contents of the conversion buffer to the byte array output stream.
467 byteArrayOutputStream.write(conversionBufferByteArray, 0, bufferLength);
469 } catch (IOException e) {
473 // Close the input stream.
476 // Populate the response body string with the contents of the byte array output stream.
477 responseBodyBuilder.append(byteArrayOutputStream.toString());
479 // Disconnect `httpUrlConnection`.
480 httpUrlConnection.disconnect();
482 } catch (IOException e) {
486 // Return the response body string as the result.
487 return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder};
490 // `onPostExecute()` operates on the UI thread.
492 protected void onPostExecute(SpannableStringBuilder[] viewSourceStringArray){
493 // Get a handle for the activity.
494 Activity activity = activityWeakReference.get();
496 // Abort if the activity is gone.
497 if ((activity == null) || activity.isFinishing()) {
501 // Get handles for the text views.
502 TextView requestHeadersTextView = activity.findViewById(R.id.request_headers);
503 TextView responseMessageTextView = activity.findViewById(R.id.response_message);
504 TextView responseHeadersTextView = activity.findViewById(R.id.response_headers);
505 TextView responseBodyTextView = activity.findViewById(R.id.response_body);
506 ProgressBar progressBar = activity.findViewById(R.id.progress_bar);
507 SwipeRefreshLayout swipeRefreshLayout = activity.findViewById(R.id.view_source_swiperefreshlayout);
509 // Populate the text views. This can take a long time, and freeze the user interface, if the response body is particularly large.
510 requestHeadersTextView.setText(viewSourceStringArray[0]);
511 responseMessageTextView.setText(viewSourceStringArray[1]);
512 responseHeadersTextView.setText(viewSourceStringArray[2]);
513 responseBodyTextView.setText(viewSourceStringArray[3]);
515 // Hide the progress bar.
516 progressBar.setIndeterminate(false);
517 progressBar.setVisibility(View.GONE);
519 //Stop the swipe to refresh indicator if it is running
520 swipeRefreshLayout.setRefreshing(false);