Updates about_licenses, adding the full text of the Apache License 2.0 and the 3...
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / fragments / SettingsFragment.java
1 /*
2  * Copyright © 2016-2017 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.fragments;
21
22 import android.annotation.SuppressLint;
23 import android.content.SharedPreferences;
24 import android.os.Build;
25 import android.os.Bundle;
26 import android.preference.Preference;
27 import android.preference.PreferenceFragment;
28 import android.view.LayoutInflater;
29 import android.view.View;
30 import android.webkit.WebView;
31
32 import com.stoutner.privacybrowser.R;
33
34 public class SettingsFragment extends PreferenceFragment {
35     private SharedPreferences.OnSharedPreferenceChangeListener preferencesListener;
36     private SharedPreferences savedPreferences;
37
38     @Override
39     public void onCreate(Bundle savedInstanceState) {
40         super.onCreate(savedInstanceState);
41         addPreferencesFromResource(R.xml.preferences);
42
43         // Initialize savedPreferences.
44         savedPreferences = getPreferenceScreen().getSharedPreferences();
45
46         // Get handles for the preferences we need to modify.
47         final Preference javaScriptPreference = findPreference("javascript_enabled");
48         final Preference firstPartyCookiesPreference = findPreference("first_party_cookies_enabled");
49         final Preference thirdPartyCookiesPreference = findPreference("third_party_cookies_enabled");
50         final Preference domStoragePreference = findPreference("dom_storage_enabled");
51         final Preference saveFormDataPreference = findPreference("save_form_data_enabled");
52         final Preference userAgentPreference = findPreference("user_agent");
53         final Preference customUserAgentPreference = findPreference("custom_user_agent");
54         final Preference blockAdsPreference = findPreference("block_ads");
55         final Preference incognitoModePreference = findPreference("incognito_mode");
56         final Preference doNotTrackPreference = findPreference("do_not_track");
57         final Preference proxyThroughOrbotPreference = findPreference("proxy_through_orbot");
58         final Preference torHomepagePreference = findPreference("tor_homepage");
59         final Preference torSearchPreference = findPreference("tor_search");
60         final Preference torSearchCustomURLPreference = findPreference("tor_search_custom_url");
61         final Preference searchPreference = findPreference("search");
62         final Preference searchCustomURLPreference = findPreference("search_custom_url");
63         final Preference hideSystemBarsPreference = findPreference("hide_system_bars");
64         final Preference translucentNavigationBarPreference = findPreference("translucent_navigation_bar");
65         final Preference homepagePreference = findPreference("homepage");
66         final Preference defaultFontSizePreference = findPreference("default_font_size");
67
68         // Set dependencies.
69         domStoragePreference.setDependency("javascript_enabled");
70         torHomepagePreference.setDependency("proxy_through_orbot");
71         torSearchPreference.setDependency("proxy_through_orbot");
72         hideSystemBarsPreference.setDependency("enable_full_screen_browsing_mode");
73
74         // Get strings from the preferences.
75         String torSearchString = savedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
76         String searchString = savedPreferences.getString("search", "https://duckduckgo.com/html/?q=");
77
78         // Get booleans from the preferences.
79         boolean javaScriptEnabledBoolean = savedPreferences.getBoolean("javascript_enabled", false);
80         boolean firstPartyCookiesEnabledBoolean = savedPreferences.getBoolean("first_party_cookies_enabled", false);
81         boolean thirdPartyCookiesEnabledBoolean = savedPreferences.getBoolean("third_party_cookies_enabled", false);
82         boolean proxyThroughOrbotBoolean = savedPreferences.getBoolean("proxy_through_orbot", false);
83
84         // Only enable `thirdPartyCookiesPreference` if `firstPartyCookiesEnabledBoolean` is `true` and API >= 21.
85         thirdPartyCookiesPreference.setEnabled(firstPartyCookiesEnabledBoolean && (Build.VERSION.SDK_INT >= 21));
86
87         // We need to inflated a `WebView` to get the default user agent.
88         LayoutInflater inflater = getActivity().getLayoutInflater();
89         // `@SuppressLint("InflateParams")` removes the warning about using `null` as the `ViewGroup`, which in this case makes sense because we don't want to display `bare_webview` on the screen.  `false` does not attach the view to the root.
90         @SuppressLint("InflateParams") View bareWebViewLayout = inflater.inflate(R.layout.bare_webview, null, false);
91         final WebView bareWebView = (WebView) bareWebViewLayout.findViewById(R.id.bare_webview);
92
93         // Set the current user-agent as the summary text for the "user_agent" preference when the preference screen is loaded.
94         switch (savedPreferences.getString("user_agent", "PrivacyBrowser/1.0")) {
95             case "WebView default user agent":
96                 // Get the user agent text from the webview (which changes based on the version of Android and WebView installed).
97                 userAgentPreference.setSummary(bareWebView.getSettings().getUserAgentString());
98                 break;
99
100             case "Custom user agent":
101                 // We can't use the string from the array because it is referenced in code and can't be translated.
102                 userAgentPreference.setSummary(R.string.custom_user_agent);
103                 break;
104
105             default:
106                 // Display the user agent from the preference as the summary text.
107                 userAgentPreference.setSummary(savedPreferences.getString("user_agent", "PrivacyBrowser/1.0"));
108                 break;
109         }
110
111         // Set the summary text for "customUserAgentPreference" (the default is `PrivacyBrowser/1.0`) and enable it if `userAgentPreference` it set to `Custom user agent`.
112         customUserAgentPreference.setSummary(savedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0"));
113         customUserAgentPreference.setEnabled(userAgentPreference.getSummary().equals("Custom user agent"));
114
115
116         // Set the Tor homepage URL as the summary text for the `tor_homepage` preference when the preference screen is loaded.  The default is DuckDuckGo: `https://3g2upl4pq6kufc4m.onion`.
117         torHomepagePreference.setSummary(savedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion"));
118
119
120         // Set the Tor search URL as the summary text for the Tor preference when the preference screen is loaded.  The default is `https://3g2upl4pq6kufc4m.onion/html/?q=`
121         if (torSearchString.equals("Custom URL")) {
122             // Use R.string.custom_url, which will be translated, instead of the array value, which will not.
123             torSearchPreference.setSummary(R.string.custom_url);
124         } else {
125             // Set the array value as the summary text.
126             torSearchPreference.setSummary(torSearchString);
127         }
128
129         // Set the summary text for `tor_search_custom_url`.  The default is `""`.
130         torSearchCustomURLPreference.setSummary(savedPreferences.getString("tor_search_custom_url", ""));
131
132         // Enable the Tor custom URL search options only if proxying through Orbot and the search is set to `Custom URL`.
133         torSearchCustomURLPreference.setEnabled(proxyThroughOrbotBoolean && torSearchString.equals("Custom URL"));
134
135
136         // Set the search URL as the summary text for the search preference when the preference screen is loaded.  The default is `https://duckduckgo.com/html/?q=`.
137         if (searchString.equals("Custom URL")) {
138             // Use R.string.custom_url, which will be translated, instead of the array value, which will not.
139             searchPreference.setSummary(R.string.custom_url);
140         } else {
141             // Set the array value as the summary text.
142             searchPreference.setSummary(searchString);
143         }
144
145         // Set the summary text for `search_custom_url` (the default is `""`) and enable it if `search` is set to `Custom URL`.
146         searchCustomURLPreference.setSummary(savedPreferences.getString("search_custom_url", ""));
147         searchCustomURLPreference.setEnabled(searchString.equals("Custom URL"));
148
149
150         // Enable `transparent_navigation_bar` only if full screen browsing mode is enabled and `hide_system_bars` is disabled.
151         translucentNavigationBarPreference.setEnabled(savedPreferences.getBoolean("enable_full_screen_browsing_mode", false) && !savedPreferences.getBoolean("hide_system_bars", false));
152
153
154         // Set the homepage URL as the summary text for the `Homepage` preference when the preference screen is loaded.  The default is `https://duckduckgo.com`.
155         homepagePreference.setSummary(savedPreferences.getString("homepage", "https://duckduckgo.com"));
156
157         // Set the default font size as the summary text for the `Default Font Size` preference when the preference screen is loaded.  The default is `100`.
158         defaultFontSizePreference.setSummary(savedPreferences.getString("default_font_size", "100") + "%%");
159
160
161         // Set the `javaScriptPreference` icon.
162         if (javaScriptEnabledBoolean) {
163             javaScriptPreference.setIcon(R.drawable.javascript_enabled);
164         } else {
165             javaScriptPreference.setIcon(R.drawable.privacy_mode);
166         }
167
168         // Set the `firstPartyCookiesPreference` icon.
169         if (firstPartyCookiesEnabledBoolean) {
170             firstPartyCookiesPreference.setIcon(R.drawable.cookies_enabled);
171         } else {
172             firstPartyCookiesPreference.setIcon(R.drawable.cookies_disabled);
173         }
174
175         // Set the `thirdPartyCookiesPreference` icon.
176         if (firstPartyCookiesEnabledBoolean && Build.VERSION.SDK_INT >= 21) {
177             if (thirdPartyCookiesEnabledBoolean) {
178                 thirdPartyCookiesPreference.setIcon(R.drawable.cookies_warning);
179             } else {
180                 thirdPartyCookiesPreference.setIcon(R.drawable.cookies_disabled);
181             }
182         } else {
183             thirdPartyCookiesPreference.setIcon(R.drawable.cookies_ghosted);
184         }
185
186         // Set the `domStoragePreference` icon.
187         if (javaScriptEnabledBoolean) {
188             if (savedPreferences.getBoolean("dom_storage_enabled", false)) {
189                 domStoragePreference.setIcon(R.drawable.dom_storage_enabled);
190             } else {
191                 domStoragePreference.setIcon(R.drawable.dom_storage_disabled);
192             }
193         } else {
194             domStoragePreference.setIcon(R.drawable.dom_storage_ghosted);
195         }
196
197         // Set the `saveFormDataPreference` icon.
198         if (savedPreferences.getBoolean("save_form_data_enabled", false)) {
199             saveFormDataPreference.setIcon(R.drawable.form_data_enabled);
200         } else {
201             saveFormDataPreference.setIcon(R.drawable.form_data_disabled);
202         }
203
204         // Set the `customUserAgentPreference` icon.
205         if (customUserAgentPreference.isEnabled()) {
206             customUserAgentPreference.setIcon(R.drawable.custom_user_agent_enabled);
207         } else {
208             customUserAgentPreference.setIcon(R.drawable.custom_user_agent_ghosted);
209         }
210
211         // Set the `blockAdsPreference` icon.
212         if (savedPreferences.getBoolean("block_ads", true)) {
213             blockAdsPreference.setIcon(R.drawable.block_ads_enabled);
214         } else {
215             blockAdsPreference.setIcon(R.drawable.block_ads_disabled);
216         }
217
218         // Set the `incognitoModePreference` icon.
219         if (savedPreferences.getBoolean("incognito_mode", false)) {
220             incognitoModePreference.setIcon(R.drawable.incognito_mode_enabled);
221         } else {
222             incognitoModePreference.setIcon(R.drawable.incognito_mode_disabled);
223         }
224
225         // Set the `doNotTrackPreference` icon.
226         if (savedPreferences.getBoolean("do_not_track", false)) {
227             doNotTrackPreference.setIcon(R.drawable.do_not_track_enabled);
228         } else {
229             doNotTrackPreference.setIcon(R.drawable.do_not_track_disabled);
230         }
231
232         // Set the `proxyThroughOrbotPreference` icon.
233         if (proxyThroughOrbotBoolean) {
234             proxyThroughOrbotPreference.setIcon(R.drawable.orbot_enabled);
235         } else {
236             proxyThroughOrbotPreference.setIcon(R.drawable.orbot_disabled);
237         }
238
239         // Set the `searchCustomURLPreference` icon.
240         if (searchCustomURLPreference.isEnabled()) {
241             searchCustomURLPreference.setIcon(R.drawable.search_custom_url_enabled);
242         } else {
243             searchCustomURLPreference.setIcon(R.drawable.search_custom_url_ghosted);
244         }
245
246
247         // Listen for preference changes.
248         preferencesListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
249             @Override
250             // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.  We know.
251             @SuppressLint("SetJavaScriptEnabled")
252             public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
253
254                 switch (key) {
255                     case "javascript_enabled":
256                         // Update the icons.
257                         if (sharedPreferences.getBoolean("javascript_enabled", false)) {
258                             // Update the icon for `javascript_enabled`.
259                             javaScriptPreference.setIcon(R.drawable.javascript_enabled);
260
261                             // Update the icon for `dom_storage_enabled`.
262                             if (sharedPreferences.getBoolean("dom_storage_enabled", false)) {
263                                 domStoragePreference.setIcon(R.drawable.dom_storage_enabled);
264                             } else {
265                                 domStoragePreference.setIcon(R.drawable.dom_storage_disabled);
266                             }
267                         } else {  // `javascript_enabled` is `false`.
268                             // Update the icon for `javascript_enabled`.
269                             javaScriptPreference.setIcon(R.drawable.privacy_mode);
270
271                             // Set the icon for `dom_storage_disabled` to be ghosted.
272                             domStoragePreference.setIcon(R.drawable.dom_storage_ghosted);
273                         }
274                         break;
275
276                     case "first_party_cookies_enabled":
277                         // Update the icons for `first_party_cookies_enabled` and `third_party_cookies_enabled`.
278                         if (sharedPreferences.getBoolean("first_party_cookies_enabled", false)) {
279                             // Set the icon for `first_party_cookies_enabled`.
280                             firstPartyCookiesPreference.setIcon(R.drawable.cookies_enabled);
281
282                             // Update the icon for `third_party_cookies_enabled`.
283                             if (Build.VERSION.SDK_INT >= 21) {
284                                 if (sharedPreferences.getBoolean("third_party_cookies_enabled", false)) {
285                                     thirdPartyCookiesPreference.setIcon(R.drawable.cookies_warning);
286                                 } else {
287                                     thirdPartyCookiesPreference.setIcon(R.drawable.cookies_disabled);
288                                 }
289                             } else {
290                                 thirdPartyCookiesPreference.setIcon(R.drawable.cookies_ghosted);
291                             }
292                         } else {  // `first_party_cookies_enabled` is `false`.
293                             // Update the icon for `first_party_cookies_enabled`.
294                             firstPartyCookiesPreference.setIcon(R.drawable.cookies_disabled);
295
296                             // Set the icon for `third_party_cookies_enabled` to be ghosted.
297                             thirdPartyCookiesPreference.setIcon(R.drawable.cookies_ghosted);
298                         }
299
300                         // Enable `third_party_cookies_enabled` if `first_party_cookies_enabled` is `true` and API >= 21.
301                         thirdPartyCookiesPreference.setEnabled(sharedPreferences.getBoolean("first_party_cookies_enabled", false) && (Build.VERSION.SDK_INT >= 21));
302                         break;
303
304                     case "third_party_cookies_enabled":
305                         // Update the icon.
306                         if (sharedPreferences.getBoolean("third_party_cookies_enabled", false)) {
307                             thirdPartyCookiesPreference.setIcon(R.drawable.cookies_warning);
308                         } else {
309                             thirdPartyCookiesPreference.setIcon(R.drawable.cookies_disabled);
310                         }
311                         break;
312
313                     case "dom_storage_enabled":
314                         // Update the icon.
315                         if (sharedPreferences.getBoolean("dom_storage_enabled", false)) {
316                             domStoragePreference.setIcon(R.drawable.dom_storage_enabled);
317                         } else {
318                             domStoragePreference.setIcon(R.drawable.dom_storage_disabled);
319                         }
320                         break;
321
322                     case "save_form_data_enabled":
323                         // Update the icon.
324                         if (sharedPreferences.getBoolean("save_form_data_enabled", false)) {
325                             saveFormDataPreference.setIcon(R.drawable.form_data_enabled);
326                         } else {
327                             saveFormDataPreference.setIcon(R.drawable.form_data_disabled);
328                         }
329
330                     case "user_agent":
331                         String userAgentString = sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0");
332
333                         switch (userAgentString) {
334                             case "WebView default user agent":
335                                 // Display the user agent as the summary text for `userAgentPreference`.
336                                 userAgentPreference.setSummary(bareWebView.getSettings().getUserAgentString());
337
338                                 // Update `customUserAgentPreference`.
339                                 customUserAgentPreference.setEnabled(false);
340                                 customUserAgentPreference.setIcon(R.drawable.custom_user_agent_ghosted);
341                                 break;
342
343                             case "Custom user agent":
344                                 // Display `Custom user agent` as the summary text for `userAgentPreference`.
345                                 userAgentPreference.setSummary(R.string.custom_user_agent);
346
347                                 // Update `customUserAgentPreference`.
348                                 customUserAgentPreference.setEnabled(true);
349                                 customUserAgentPreference.setIcon(R.drawable.custom_user_agent_enabled);
350                                 break;
351
352                             default:
353                                 // Display the user agent as the summary text for `userAgentPreference`.
354                                 userAgentPreference.setSummary(sharedPreferences.getString("user_agent", "PrivacyBrowser/1.0"));
355
356                                 // Update `customUserAgentPreference`.
357                                 customUserAgentPreference.setEnabled(false);
358                                 customUserAgentPreference.setIcon(R.drawable.custom_user_agent_ghosted);
359                                 break;
360                         }
361                         break;
362
363                     case "custom_user_agent":
364                         // Set the new custom user agent as the summary text for `custom_user_agent`.  The default is `PrivacyBrowser/1.0`.
365                         customUserAgentPreference.setSummary(sharedPreferences.getString("custom_user_agent", "PrivacyBrowser/1.0"));
366                         break;
367
368                     case "block_ads":
369                         // Update the icon.
370                         if (sharedPreferences.getBoolean("block_ads", true)) {
371                             blockAdsPreference.setIcon(R.drawable.block_ads_enabled);
372                         } else {
373                             blockAdsPreference.setIcon(R.drawable.block_ads_disabled);
374                         }
375                         break;
376
377                     case "incognito_mode":
378                         // Update the icon.
379                         if (sharedPreferences.getBoolean("incognito_mode", false)) {
380                             incognitoModePreference.setIcon(R.drawable.incognito_mode_enabled);
381                         } else {
382                             incognitoModePreference.setIcon(R.drawable.incognito_mode_disabled);
383                         }
384                         break;
385
386                     case "do_not_track":
387                         // Update the icon.
388                         if (sharedPreferences.getBoolean("do_not_track", false)) {
389                             doNotTrackPreference.setIcon(R.drawable.do_not_track_enabled);
390                         } else {
391                             doNotTrackPreference.setIcon(R.drawable.do_not_track_disabled);
392                         }
393
394                         break;
395
396                     case "proxy_through_orbot":
397                         // Get current settings.
398                         boolean currentProxyThroughOrbot = sharedPreferences.getBoolean("proxy_through_orbot", false);
399                         String currentTorSearchString = sharedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
400
401                         // Enable the Tor custom URL search option only if `currentProxyThroughOrbot` is true and the search is set to `Custom URL`.
402                         torSearchCustomURLPreference.setEnabled(currentProxyThroughOrbot && currentTorSearchString.equals("Custom URL"));
403
404                         // Update the icon.
405                         if (currentProxyThroughOrbot) {
406                             proxyThroughOrbotPreference.setIcon(R.drawable.orbot_enabled);
407                         } else {
408                             proxyThroughOrbotPreference.setIcon(R.drawable.orbot_disabled);
409                         }
410                         break;
411
412                     case "tor_homepage":
413                         // Set the new tor homepage URL as the summary text for the `tor_homepage` preference.  The default is DuckDuckGo:  `https://3g2upl4pq6kufc4m.onion`.
414                         torHomepagePreference.setSummary(sharedPreferences.getString("tor_homepage", "https://3g2upl4pq6kufc4m.onion"));
415                         break;
416
417                     case "tor_search":
418                         // Get the present search string.
419                         String presentTorSearchString = sharedPreferences.getString("tor_search", "https://3g2upl4pq6kufc4m.onion/html/?q=");
420
421                         // Set the summary text for `tor_search`.
422                         if (presentTorSearchString.equals("Custom URL")) {
423                             // Use R.string.custom_url, which is translated, instead of the array value, which isn't.
424                             torSearchPreference.setSummary(R.string.custom_url);
425                         } else {
426                             // Set the array value as the summary text.
427                             torSearchPreference.setSummary(presentTorSearchString);
428                         }
429
430                         // Set the status of `torJavaScriptDisabledSearchCustomURLPreference`.
431                         torSearchCustomURLPreference.setEnabled(presentTorSearchString.equals("Custom URL"));
432                         break;
433
434                     case "tor_search_custom_url":
435                         // Set the summary text for `tor_search_custom_url`.
436                         torSearchCustomURLPreference.setSummary(sharedPreferences.getString("tor_search_custom_url", ""));
437                         break;
438
439                     case "search":
440                         // Store the new search string.
441                         String newSearchString = sharedPreferences.getString("search", "https://duckduckgo.com/html/?q=");
442
443                         // Update `searchPreference` and `searchCustomURLPreference`.
444                         if (newSearchString.equals("Custom URL")) {  // `Custom URL` is selected.
445                             // Set the summary text to `R.string.custom_url`, which is translated.
446                             searchPreference.setSummary(R.string.custom_url);
447
448                             // Update `searchCustomURLPreference`.
449                             searchCustomURLPreference.setEnabled(true);
450                             searchCustomURLPreference.setIcon(R.drawable.search_custom_url_enabled);
451                         } else {  // `Custom URL` is not selected.
452                             // Set the summary text to `newSearchString`.
453                             searchPreference.setSummary(newSearchString);
454
455                             // Update `searchCustomURLPreference`.
456                             searchCustomURLPreference.setEnabled(false);
457                             searchCustomURLPreference.setIcon(R.drawable.search_custom_url_ghosted);
458                         }
459                         break;
460
461                     case "search_custom_url":
462                         // Set the new custom search URL as the summary text for `search_custom_url`.  The default is `""`.
463                         searchCustomURLPreference.setSummary(sharedPreferences.getString("search_custom_url", ""));
464                         break;
465
466                     case "enable_full_screen_browsing_mode":
467                         // Enable `transparent_navigation_bar` only if full screen browsing mode is enabled and `hide_system_bars` is disabled.
468                         translucentNavigationBarPreference.setEnabled(sharedPreferences.getBoolean("enable_full_screen_browsing_mode", false) && !sharedPreferences.getBoolean("hide_system_bars", false));
469                         break;
470
471                     case "hide_system_bars":
472                         // Enable `translucentNavigationBarPreference` if `hide_system_bars` is disabled.
473                         translucentNavigationBarPreference.setEnabled(!sharedPreferences.getBoolean("hide_system_bars", false));
474                         break;
475
476                     case "homepage":
477                         // Set the new homepage URL as the summary text for the Homepage preference.  The default is `https://www.duckduckgo.com`.
478                         homepagePreference.setSummary(sharedPreferences.getString("homepage", "https://www.duckduckgo.com"));
479                         break;
480
481                     case "default_font_size":
482                         // Update the summary text of `default_font_size`.
483                         defaultFontSizePreference.setSummary(sharedPreferences.getString("default_font_size", "100") + "%%");
484                         break;
485
486                     default:
487                         // If no match, do nothing.
488                         break;
489                 }
490             }
491         };
492
493         // Register the listener.
494         savedPreferences.registerOnSharedPreferenceChangeListener(preferencesListener);
495     }
496
497     // It is necessary to re-register the listener on every resume or it will randomly stop working because apps can be paused and resumed at any time
498     // even while running in the foreground.
499     @Override
500     public void onPause() {
501         super.onPause();
502         savedPreferences.unregisterOnSharedPreferenceChangeListener(preferencesListener);
503     }
504
505     @Override
506     public void onResume() {
507         super.onResume();
508         savedPreferences.registerOnSharedPreferenceChangeListener(preferencesListener);
509     }
510 }