2a320c39bdb64f65af7209a64069cfda8d0edfde
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / asynctasks / GetSource.java
1 /*
2  * Copyright © 2017-2019 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.asynctasks;
21
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;
36
37 import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
38
39 import com.stoutner.privacybrowser.R;
40
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;
47 import java.net.URL;
48 import java.util.Locale;
49
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;
54
55     // Store the user agent.
56     private String userAgent;
57
58     public GetSource(Activity activity, String userAgent) {
59         // Populate the weak reference to the calling activity.
60         activityWeakReference = new WeakReference<>(activity);
61
62         // Store the user agent.
63         this.userAgent = userAgent;
64     }
65
66     // `onPreExecute()` operates on the UI thread.
67     @Override
68     protected void onPreExecute() {
69         // Get a handle for the activity.
70         Activity viewSourceActivity = activityWeakReference.get();
71
72         // Abort if the activity is gone.
73         if ((viewSourceActivity == null) || viewSourceActivity.isFinishing()) {
74             return;
75         }
76
77         // Get a handle for the progress bar.
78         ProgressBar progressBar = viewSourceActivity.findViewById(R.id.progress_bar);
79
80         // Make the progress bar visible.
81         progressBar.setVisibility(View.VISIBLE);
82
83         // Set the progress bar to be indeterminate.
84         progressBar.setIndeterminate(true);
85     }
86
87     @Override
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();
94
95         // Get a handle for the activity.
96         Activity activity = activityWeakReference.get();
97
98         // Abort if the activity is gone.
99         if ((activity == null) || activity.isFinishing()) {
100             return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder};
101         }
102
103         // Because everything relating to requesting data from a webserver can throw errors, the entire section must catch `IOExceptions`.
104         try {
105             // Get the current URL from the main activity.
106             URL url = new URL(formattedUrlString[0]);
107
108             // Open a connection to the URL.  No data is actually sent at this point.
109             HttpURLConnection httpUrlConnection = (HttpURLConnection) url.openConnection();
110
111             // Define the variables necessary to build the request headers.
112             requestHeadersBuilder = new SpannableStringBuilder();
113             int oldRequestHeadersBuilderLength;
114             int newRequestHeadersBuilderLength;
115
116
117             // Set the `Host` header property.
118             httpUrlConnection.setRequestProperty("Host", url.getHost());
119
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 + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
128             }
129             requestHeadersBuilder.append(":  ");
130             requestHeadersBuilder.append(url.getHost());
131
132
133             // Set the `Connection` header property.
134             httpUrlConnection.setRequestProperty("Connection", "keep-alive");
135
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 + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
145             }
146             requestHeadersBuilder.append(":  keep-alive");
147
148
149             // Set the `Upgrade-Insecure-Requests` header property.
150             httpUrlConnection.setRequestProperty("Upgrade-Insecure-Requests", "1");
151
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 + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
161             }
162             requestHeadersBuilder.append(":  1");
163
164
165             // Set the `User-Agent` header property.
166             httpUrlConnection.setRequestProperty("User-Agent", userAgent);
167
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 + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
177             }
178             requestHeadersBuilder.append(":  ");
179             requestHeadersBuilder.append(userAgent);
180
181
182             // Set the `x-requested-with` header property.
183             httpUrlConnection.setRequestProperty("x-requested-with", "");
184
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 + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
194             }
195             requestHeadersBuilder.append(":  ");
196
197
198             // Get a handle for the shared preferences.
199             SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(activity.getApplicationContext());
200
201             // Only populate `Do Not Track` if it is enabled.
202             if (sharedPreferences.getBoolean("do_not_track", false)) {
203                 // Set the `dnt` header property.
204                 httpUrlConnection.setRequestProperty("dnt", "1");
205
206                 // Add the `dnt` header to the string builder and format the text.
207                 requestHeadersBuilder.append(System.getProperty("line.separator"));
208                 if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
209                     requestHeadersBuilder.append("dnt", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
210                 } else {  // Older versions not so much.
211                     oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
212                     requestHeadersBuilder.append("dnt");
213                     newRequestHeadersBuilderLength = requestHeadersBuilder.length();
214                     requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
215                 }
216                 requestHeadersBuilder.append(":  1");
217             }
218
219
220             // Set the `Accept` header property.
221             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");
222
223             // Add the `Accept` header to the string builder and format the text.
224             requestHeadersBuilder.append(System.getProperty("line.separator"));
225             if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
226                 requestHeadersBuilder.append("Accept", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
227             } else {  // Older versions not so much.
228                 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
229                 requestHeadersBuilder.append("Accept");
230                 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
231                 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
232             }
233             requestHeadersBuilder.append(":  ");
234             requestHeadersBuilder.append("text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3");
235
236
237             // Instantiate a locale string.
238             String localeString;
239
240             // Populate the locale string.
241             if (Build.VERSION.SDK_INT >= 24) {  // SDK >= 24 has a list of locales.
242                 // Get the list of locales.
243                 LocaleList localeList = activity.getResources().getConfiguration().getLocales();
244
245                 // Initialize a string builder to extract the locales from the list.
246                 StringBuilder localesStringBuilder = new StringBuilder();
247
248                 // Initialize a `q` value, which is used by `WebView` to indicate the order of importance of the languages.
249                 int q = 10;
250
251                 // Populate the string builder with the contents of the locales list.
252                 for (int i = 0; i < localeList.size(); i++) {
253                     // Append a comma if there is already an item in the string builder.
254                     if (i > 0) {
255                         localesStringBuilder.append(",");
256                     }
257
258                     // Get the locale from the list.
259                     Locale locale = localeList.get(i);
260
261                     // Add the locale to the string.  `locale` by default displays as `en_US`, but WebView uses the `en-US` format.
262                     localesStringBuilder.append(locale.getLanguage());
263                     localesStringBuilder.append("-");
264                     localesStringBuilder.append(locale.getCountry());
265
266                     // If not the first locale, append `;q=0.x`, which drops by .1 for each removal from the main locale until q=0.1.
267                     if (q < 10) {
268                         localesStringBuilder.append(";q=0.");
269                         localesStringBuilder.append(q);
270                     }
271
272                     // Decrement `q` if it is greater than 1.
273                     if (q > 1) {
274                         q--;
275                     }
276
277                     // Add a second entry for the language only portion of the locale.
278                     localesStringBuilder.append(",");
279                     localesStringBuilder.append(locale.getLanguage());
280
281                     // Append `1;q=0.x`, which drops by .1 for each removal form the main locale until q=0.1.
282                     localesStringBuilder.append(";q=0.");
283                     localesStringBuilder.append(q);
284
285                     // Decrement `q` if it is greater than 1.
286                     if (q > 1) {
287                         q--;
288                     }
289                 }
290
291                 // Store the populated string builder in the locale string.
292                 localeString = localesStringBuilder.toString();
293             } else {  // SDK < 24 only has a primary locale.
294                 // Store the locale in the locale string.
295                 localeString = Locale.getDefault().toString();
296             }
297
298             // Set the `Accept-Language` header property.
299             httpUrlConnection.setRequestProperty("Accept-Language", localeString);
300
301             // Add the `Accept-Language` header to the string builder and format the text.
302             requestHeadersBuilder.append(System.getProperty("line.separator"));
303             if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
304                 requestHeadersBuilder.append("Accept-Language", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
305             } else {  // Older versions not so much.
306                 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
307                 requestHeadersBuilder.append("Accept-Language");
308                 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
309                 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
310             }
311             requestHeadersBuilder.append(":  ");
312             requestHeadersBuilder.append(localeString);
313
314
315             // Get the cookies for the current domain.
316             String cookiesString = CookieManager.getInstance().getCookie(url.toString());
317
318             // Only process the cookies if they are not null.
319             if (cookiesString != null) {
320                 // Set the `Cookie` header property.
321                 httpUrlConnection.setRequestProperty("Cookie", cookiesString);
322
323                 // Add the `Cookie` header to the string builder and format the text.
324                 requestHeadersBuilder.append(System.getProperty("line.separator"));
325                 if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
326                     requestHeadersBuilder.append("Cookie", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
327                 } else {  // Older versions not so much.
328                     oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
329                     requestHeadersBuilder.append("Cookie");
330                     newRequestHeadersBuilderLength = requestHeadersBuilder.length();
331                     requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
332                 }
333                 requestHeadersBuilder.append(":  ");
334                 requestHeadersBuilder.append(cookiesString);
335             }
336
337
338             // `HttpUrlConnection` sets `Accept-Encoding` to be `gzip` by default.  If the property is manually set, than `HttpUrlConnection` does not process the decoding.
339             // Add the `Accept-Encoding` header to the string builder and format the text.
340             requestHeadersBuilder.append(System.getProperty("line.separator"));
341             if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
342                 requestHeadersBuilder.append("Accept-Encoding", new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
343             } else {  // Older versions not so much.
344                 oldRequestHeadersBuilderLength = requestHeadersBuilder.length();
345                 requestHeadersBuilder.append("Accept-Encoding");
346                 newRequestHeadersBuilderLength = requestHeadersBuilder.length();
347                 requestHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldRequestHeadersBuilderLength + 1, newRequestHeadersBuilderLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
348             }
349             requestHeadersBuilder.append(":  gzip");
350
351
352             // 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.
353             try {
354                 // Initialize the string builders.
355                 responseMessageBuilder = new SpannableStringBuilder();
356                 responseHeadersBuilder = new SpannableStringBuilder();
357
358                 // Get the response code, which causes the connection to the server to be made.
359                 int responseCode = httpUrlConnection.getResponseCode();
360
361                 // Populate the response message string builder.
362                 if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
363                     responseMessageBuilder.append(String.valueOf(responseCode), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
364                 } else {  // Older versions not so much.
365                     responseMessageBuilder.append(String.valueOf(responseCode));
366                     int newLength = responseMessageBuilder.length();
367                     responseMessageBuilder.setSpan(new StyleSpan(Typeface.BOLD), 0, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
368                 }
369                 responseMessageBuilder.append(":  ");
370                 responseMessageBuilder.append(httpUrlConnection.getResponseMessage());
371
372                 // Initialize the iteration variable.
373                 int i = 0;
374
375                 // Iterate through the received header fields.
376                 while (httpUrlConnection.getHeaderField(i) != null) {
377                     // Add a new line if there is already information in the string builder.
378                     if (i > 0) {
379                         responseHeadersBuilder.append(System.getProperty("line.separator"));
380                     }
381
382                     // Add the header to the string builder and format the text.
383                     if (Build.VERSION.SDK_INT >= 21) {  // Newer versions of Android are so smart.
384                         responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i), new StyleSpan(Typeface.BOLD), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
385                     } else {  // Older versions not so much.
386                         int oldLength = responseHeadersBuilder.length();
387                         responseHeadersBuilder.append(httpUrlConnection.getHeaderFieldKey(i));
388                         int newLength = responseHeadersBuilder.length();
389                         responseHeadersBuilder.setSpan(new StyleSpan(Typeface.BOLD), oldLength + 1, newLength, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
390                     }
391                     responseHeadersBuilder.append(":  ");
392                     responseHeadersBuilder.append(httpUrlConnection.getHeaderField(i));
393
394                     // Increment the iteration variable.
395                     i++;
396                 }
397
398                 // Instantiate an input stream for the response body.
399                 InputStream inputStream;
400
401                 // Get the correct input stream based on the response code.
402                 if (responseCode == 404) {  // Get the error stream.
403                     inputStream = new BufferedInputStream(httpUrlConnection.getErrorStream());
404                 } else {  // Get the response body stream.
405                     inputStream = new BufferedInputStream(httpUrlConnection.getInputStream());
406                 }
407
408                 // Initialize the byte array output stream and the conversion buffer byte array.
409                 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
410                 byte[] conversionBufferByteArray = new byte[1024];
411
412                 // Instantiate the variable to track the buffer length.
413                 int bufferLength;
414
415                 try {
416                     // 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.
417                     while ((bufferLength = inputStream.read(conversionBufferByteArray)) > 0) {  // Proceed while the amount of data stored in the buffer is > 0.
418                         // Write the contents of the conversion buffer to the byte array output stream.
419                         byteArrayOutputStream.write(conversionBufferByteArray, 0, bufferLength);
420                     }
421                 } catch (IOException e) {
422                     e.printStackTrace();
423                 }
424
425                 // Close the input stream.
426                 inputStream.close();
427
428                 // Populate the response body string with the contents of the byte array output stream.
429                 responseBodyBuilder.append(byteArrayOutputStream.toString());
430             } finally {
431                 // Disconnect `httpUrlConnection`.
432                 httpUrlConnection.disconnect();
433             }
434         } catch (IOException e) {
435             e.printStackTrace();
436         }
437
438         // Return the response body string as the result.
439         return new SpannableStringBuilder[] {requestHeadersBuilder, responseMessageBuilder, responseHeadersBuilder, responseBodyBuilder};
440     }
441
442     // `onPostExecute()` operates on the UI thread.
443     @Override
444     protected void onPostExecute(SpannableStringBuilder[] viewSourceStringArray){
445         // Get a handle for the activity.
446         Activity activity = activityWeakReference.get();
447
448         // Abort if the activity is gone.
449         if ((activity == null) || activity.isFinishing()) {
450             return;
451         }
452
453         // Get handles for the text views.
454         TextView requestHeadersTextView = activity.findViewById(R.id.request_headers);
455         TextView responseMessageTextView = activity.findViewById(R.id.response_message);
456         TextView responseHeadersTextView = activity.findViewById(R.id.response_headers);
457         TextView responseBodyTextView = activity.findViewById(R.id.response_body);
458         ProgressBar progressBar = activity.findViewById(R.id.progress_bar);
459         SwipeRefreshLayout swipeRefreshLayout = activity.findViewById(R.id.view_source_swiperefreshlayout);
460
461         // Populate the text views.  This can take a long time, and freeze the user interface, if the response body is particularly large.
462         requestHeadersTextView.setText(viewSourceStringArray[0]);
463         responseMessageTextView.setText(viewSourceStringArray[1]);
464         responseHeadersTextView.setText(viewSourceStringArray[2]);
465         responseBodyTextView.setText(viewSourceStringArray[3]);
466
467         // Hide the progress bar.
468         progressBar.setIndeterminate(false);
469         progressBar.setVisibility(View.GONE);
470
471         //Stop the swipe to refresh indicator if it is running
472         swipeRefreshLayout.setRefreshing(false);
473     }
474 }