]> gitweb.stoutner.com Git - PrivacyBrowserAndroid.git/blob - app/src/main/java/com/stoutner/privacybrowser/MainWebViewActivity.java
User Preeminence: only update the urlTextBox if the user is not typing in it.
[PrivacyBrowserAndroid.git] / app / src / main / java / com / stoutner / privacybrowser / MainWebViewActivity.java
1 /**
2  * Copyright 2015-2016 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;
21
22 import android.annotation.SuppressLint;
23 import android.annotation.TargetApi;
24 import android.app.Activity;
25 import android.app.DownloadManager;
26 import android.content.Intent;
27 import android.content.SharedPreferences;
28 import android.graphics.Bitmap;
29 import android.net.Uri;
30 import android.os.Build;
31 import android.os.Bundle;
32 import android.preference.PreferenceManager;
33 import android.support.design.widget.Snackbar;
34 import android.support.v4.app.DialogFragment;
35 import android.support.v4.widget.SwipeRefreshLayout;
36 import android.support.v7.app.ActionBar;
37 import android.support.v7.app.AppCompatActivity;
38 import android.support.v7.app.AppCompatDialogFragment;
39 import android.support.v7.widget.Toolbar;
40 import android.util.Patterns;
41 import android.view.KeyEvent;
42 import android.view.Menu;
43 import android.view.MenuItem;
44 import android.view.View;
45 import android.view.inputmethod.InputMethodManager;
46 import android.webkit.CookieManager;
47 import android.webkit.DownloadListener;
48 import android.webkit.WebChromeClient;
49 import android.webkit.WebStorage;
50 import android.webkit.WebView;
51 import android.webkit.WebViewClient;
52 import android.widget.EditText;
53 import android.widget.FrameLayout;
54 import android.widget.ImageView;
55 import android.widget.ProgressBar;
56 import android.widget.Toast;
57
58 import java.io.UnsupportedEncodingException;
59 import java.net.MalformedURLException;
60 import java.net.URL;
61 import java.net.URLEncoder;
62
63 // We need to use AppCompatActivity from android.support.v7.app.AppCompatActivity to have access to the SupportActionBar until the minimum API is >= 21.
64 public class MainWebViewActivity extends AppCompatActivity implements CreateHomeScreenShortcut.CreateHomeScreenSchortcutListener {
65     // favoriteIcon is public static so it can be accessed from CreateHomeScreenShortcut.
66     public static Bitmap favoriteIcon;
67     // mainWebView is public static so it can be accessed from AboutDialog.  It is also used in onCreate(), onOptionsItemSelected(), and loadUrlFromTextBox().
68     public static WebView mainWebView;
69
70     // mainMenu is used in onCreateOptionsMenu() and onOptionsItemSelected().
71     private Menu mainMenu;
72     // formattedUrlString is used in onCreate(), onOptionsItemSelected(), onCreateHomeScreenShortcutCreate(), and loadUrlFromTextBox().
73     private String formattedUrlString;
74     // homepage is used in onCreate() and onOptionsItemSelected().
75     private String homepage;
76     // javaScriptEnabled is used in onCreate(), onCreateOptionsMenu(), onOptionsItemSelected(), and loadUrlFromTextBox().
77     private boolean javaScriptEnabled;
78     // domStorageEnabled is used in onCreate(), onCreateOptionsMenu(), and onOptionsItemSelected().
79     private boolean domStorageEnabled;
80
81     /* saveFormDataEnabled does nothing until database storage is implemented.
82     // saveFormDataEnabled is used in onCreate(), onCreateOptionsMenu(), and onOptionsItemSelected().
83     private boolean saveFormDataEnabled;
84     */
85
86     // cookieManager is used in onCreate() and onOptionsItemSelected().
87     private CookieManager cookieManager;
88     // cookiesEnabled is used in onCreate(), onCreateOptionsMenu(), and onOptionsItemSelected().
89     private boolean cookiesEnabled;
90
91     // urlTextBox is used in onCreate(), onOptionsItemSelected(), and loadUrlFromTextBox().
92     private EditText urlTextBox;
93
94     @Override
95     // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.  The whole premise of Privacy Browser is built around an understanding of these dangers.
96     @SuppressLint("SetJavaScriptEnabled")
97     protected void onCreate(Bundle savedInstanceState) {
98         super.onCreate(savedInstanceState);
99         setContentView(R.layout.activity_webview);
100
101         // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21.
102         Toolbar supportAppBar = (Toolbar) findViewById(R.id.appBar);
103         setSupportActionBar(supportAppBar);
104
105         final FrameLayout fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.fullScreenVideoFrameLayout);
106
107         // We need to use the SupportActionBar from android.support.v7.app.ActionBar until the minimum API is >= 21.
108         final ActionBar appBar = getSupportActionBar();
109
110         // Setup AdView for the free flavor.
111         final View adView = findViewById(R.id.adView);
112
113         // Implement swipe to refresh
114         final SwipeRefreshLayout swipeToRefresh = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
115         swipeToRefresh.setColorSchemeResources(R.color.blue);
116         swipeToRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
117             @Override
118             public void onRefresh() {
119                 mainWebView.reload();
120             }
121         });
122
123         mainWebView = (WebView) findViewById(R.id.mainWebView);
124
125         if (appBar != null) {
126             // Add the custom url_bar layout, which shows the favoriteIcon, urlTextBar, and progressBar.
127             appBar.setCustomView(R.layout.url_bar);
128             appBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
129
130             // Set the "go" button on the keyboard to load the URL in urlTextBox.
131             urlTextBox = (EditText) appBar.getCustomView().findViewById(R.id.urlTextBox);
132             urlTextBox.setOnKeyListener(new View.OnKeyListener() {
133                 public boolean onKey(View v, int keyCode, KeyEvent event) {
134                     // If the event is a key-down event on the "enter" button, load the URL.
135                     if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
136                         // Load the URL into the mainWebView and consume the event.
137                         try {
138                             loadUrlFromTextBox();
139                         } catch (UnsupportedEncodingException e) {
140                             e.printStackTrace();
141                         }
142                         // If the enter key was pressed, consume the event.
143                         return true;
144                     } else {
145                         // If any other key was pressed, do not consume the event.
146                         return false;
147                     }
148                 }
149             });
150         }
151
152         mainWebView.setWebViewClient(new WebViewClient() {
153             // shouldOverrideUrlLoading makes this WebView the default handler for URLs inside the app, so that links are not kicked out to other apps.
154             @Override
155             public boolean shouldOverrideUrlLoading(WebView view, String url) {
156                 mainWebView.loadUrl(url);
157                 return true;
158             }
159
160             // Update the URL in urlTextBox when the page starts to load.
161             @Override
162             public void onPageStarted(WebView view, String url, Bitmap favicon) {
163                 urlTextBox.setText(url);
164             }
165
166             // Update formattedUrlString and urlTextBox.  It is necessary to do this after the page finishes loading because the final URL can change during load.
167             @Override
168             public void onPageFinished(WebView view, String url) {
169                 formattedUrlString = url;
170
171                 // Only update urlTextBox if the user is not typing in it.
172                 if (!urlTextBox.hasFocus()) {
173                     urlTextBox.setText(formattedUrlString);
174                 }
175             }
176         });
177
178         mainWebView.setWebChromeClient(new WebChromeClient() {
179             // Update the progress bar when a page is loading.
180             @Override
181             public void onProgressChanged(WebView view, int progress) {
182                 // Make sure that appBar is not null.
183                 if (appBar != null) {
184                     ProgressBar progressBar = (ProgressBar) appBar.getCustomView().findViewById(R.id.progressBar);
185                     progressBar.setProgress(progress);
186                     if (progress < 100) {
187                         progressBar.setVisibility(View.VISIBLE);
188                     } else {
189                         progressBar.setVisibility(View.GONE);
190
191                         //Stop the SwipeToRefresh indicator if it is running
192                         swipeToRefresh.setRefreshing(false);
193                     }
194                 }
195             }
196
197             // Set the favorite icon when it changes.
198             @Override
199             public void onReceivedIcon(WebView view, Bitmap icon) {
200                 // Save a copy of the favorite icon for use if a shortcut is added to the home screen.
201                 favoriteIcon = icon;
202
203                 // Place the favorite icon in the appBar if it is not null.
204                 if (appBar != null) {
205                     ImageView imageViewFavoriteIcon = (ImageView) appBar.getCustomView().findViewById(R.id.favoriteIcon);
206                     imageViewFavoriteIcon.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
207                 }
208             }
209
210             // Enter full screen video
211             @Override
212             public void onShowCustomView(View view, CustomViewCallback callback) {
213                 if (appBar != null) {
214                     appBar.hide();
215                 }
216
217                 // Show the fullScreenVideoFrameLayout.
218                 fullScreenVideoFrameLayout.addView(view);
219                 fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
220
221                 // Hide the mainWebView.
222                 mainWebView.setVisibility(View.GONE);
223
224                 // Hide the ad if this is the free flavor.
225                 BannerAd.hideAd(adView);
226
227                 /* SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bars on the bottom or right of the screen.
228                 ** SYSTEM_UI_FLAG_FULLSCREEN hides the status bar across the top of the screen.
229                 ** SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the navigation and status bars ghosted overlays and automatically rehides them.
230                 */
231
232                 // Set the one flag supported by API >= 14.
233                 view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
234
235                 // Set the two flags that are supported by API >= 16.
236                 if (Build.VERSION.SDK_INT >= 16) {
237                     view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
238                 }
239
240                 // Set all three flags that are supported by API >= 19.
241                 if (Build.VERSION.SDK_INT >= 19) {
242                     view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
243                 }
244             }
245
246             // Exit full screen video
247             public void onHideCustomView() {
248                 if (appBar != null) {
249                     appBar.show();
250                 }
251
252                 // Show the mainWebView.
253                 mainWebView.setVisibility(View.VISIBLE);
254
255                 // Show the ad if this is the free flavor.
256                 BannerAd.showAd(adView);
257
258                 // Hide the fullScreenVideoFrameLayout.
259                 fullScreenVideoFrameLayout.removeAllViews();
260                 fullScreenVideoFrameLayout.setVisibility(View.GONE);
261             }
262         });
263
264         // Allow the downloading of files.
265         mainWebView.setDownloadListener(new DownloadListener() {
266             // Launch the Android download manager when a link leads to a download.
267             @Override
268             public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
269                 DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
270                 DownloadManager.Request requestUri = new DownloadManager.Request(Uri.parse(url));
271
272                 // Add the URL as the description for the download.
273                 requestUri.setDescription(url);
274
275                 // Show the download notification after the download is completed.
276                 requestUri.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
277
278                 // Initiate the download and display a Snackbar.
279                 downloadManager.enqueue(requestUri);
280                 Snackbar.make(findViewById(R.id.mainWebView), R.string.download_started, Snackbar.LENGTH_SHORT).show();
281             }
282         });
283
284         // Allow pinch to zoom.
285         mainWebView.getSettings().setBuiltInZoomControls(true);
286
287         // Hide zoom controls.
288         mainWebView.getSettings().setDisplayZoomControls(false);
289
290         // Initialize the default preference values the first time the program is run.
291         PreferenceManager.setDefaultValues(this, R.xml.preferences, false);
292
293         // Get the shared preference values.
294         SharedPreferences savedPreferences = PreferenceManager.getDefaultSharedPreferences(this);
295
296         // Set JavaScript initial status.
297         javaScriptEnabled = savedPreferences.getBoolean("javascript_enabled", false);
298         mainWebView.getSettings().setJavaScriptEnabled(javaScriptEnabled);
299
300         // Set DOM storage initial status.
301         domStorageEnabled = savedPreferences.getBoolean("dom_storage_enabled", false);
302         mainWebView.getSettings().setDomStorageEnabled(domStorageEnabled);
303
304         /* Save Form Data does nothing until database storage is implemented.
305         // Set Save Form Data initial status.
306         saveFormDataEnabled = true;
307         mainWebView.getSettings().setSaveFormData(saveFormDataEnabled);
308         */
309
310         // Set cookies initial status.
311         cookiesEnabled = savedPreferences.getBoolean("cookies_enabled", false);
312         cookieManager = CookieManager.getInstance();
313         cookieManager.setAcceptCookie(cookiesEnabled);
314
315         // Set homepage initial status.
316         homepage = savedPreferences.getString("homepage", "https://www.duckduckgo.com");
317
318         // Get the intent information that started the app.
319         final Intent intent = getIntent();
320
321         if (intent.getData() != null) {
322             // Get the intent data and convert it to a string.
323             final Uri intentUriData = intent.getData();
324             formattedUrlString = intentUriData.toString();
325         }
326
327         // If formattedUrlString is null assign the homepage to it.
328         if (formattedUrlString == null) {
329             formattedUrlString = homepage;
330         }
331
332         // Load the initial website.
333         mainWebView.loadUrl(formattedUrlString);
334
335         // Load the ad if this is the free flavor.
336         BannerAd.requestAd(adView);
337     }
338
339     @Override
340     protected void onNewIntent(Intent intent) {
341         // Sets the new intent as the activity intent, so that any future getIntent()s pick up this one instead of creating a new activity.
342         setIntent(intent);
343
344         if (intent.getData() != null) {
345             // Get the intent data and convert it to a string.
346             final Uri intentUriData = intent.getData();
347             formattedUrlString = intentUriData.toString();
348         }
349
350         // Load the website.
351         mainWebView.loadUrl(formattedUrlString);
352
353         // Clear the keyboard if displayed and remove the focus on the urlTextBar if it has it.
354         mainWebView.requestFocus();
355     }
356
357     @Override
358     public boolean onCreateOptionsMenu(Menu menu) {
359         // Inflate the menu; this adds items to the action bar if it is present.
360         getMenuInflater().inflate(R.menu.menu_webview, menu);
361
362         // Set mainMenu so it can be used by onOptionsItemSelected.
363         mainMenu = menu;
364
365         // Get MenuItems for checkable menu items.
366         MenuItem toggleJavaScript = menu.findItem(R.id.toggleJavaScript);
367         MenuItem toggleDomStorage = menu.findItem(R.id.toggleDomStorage);
368         /* toggleSaveFormData does nothing until database storage is implemented.
369         MenuItem toggleSaveFormData = menu.findItem(R.id.toggleSaveFormData);
370         */
371         MenuItem toggleCookies = menu.findItem(R.id.toggleCookies);
372
373         // Set the initial icon for toggleJavaScript
374         if (javaScriptEnabled) {
375             toggleJavaScript.setIcon(R.drawable.javascript_enabled);
376         } else {
377             if (domStorageEnabled || cookiesEnabled) {
378                 toggleJavaScript.setIcon(R.drawable.warning);
379             } else {
380                 toggleJavaScript.setIcon(R.drawable.privacy_mode);
381             }
382         }
383
384         // Set the initial status of the menu item checkboxes.
385         toggleDomStorage.setChecked(domStorageEnabled);
386         /* toggleSaveFormData does nothing until database storage is implemented.
387         toggleSaveFormData.setChecked(saveFormDataEnabled);
388         */
389         toggleCookies.setChecked(cookiesEnabled);
390
391         return true;
392     }
393
394     @Override
395     public boolean onPrepareOptionsMenu(Menu menu) {
396         // Enable Clear Cookies if there are any.
397         MenuItem clearCookies = menu.findItem(R.id.clearCookies);
398         clearCookies.setEnabled(cookieManager.hasCookies());
399
400         // Enable Back if canGoBack().
401         MenuItem back = menu.findItem(R.id.back);
402         back.setEnabled(mainWebView.canGoBack());
403
404         // Enable forward if canGoForward().
405         MenuItem forward = menu.findItem(R.id.forward);
406         forward.setEnabled(mainWebView.canGoForward());
407
408         // Run all the other default commands.
409         super.onPrepareOptionsMenu(menu);
410
411         // return true displays the menu.
412         return true;
413     }
414
415     @Override
416     // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.
417     @SuppressLint("SetJavaScriptEnabled")
418     // removeAllCookies is deprecated, but it is required for API < 21.
419     @SuppressWarnings("deprecation")
420     public boolean onOptionsItemSelected(MenuItem menuItem) {
421         int menuItemId = menuItem.getItemId();
422
423         // Some options need to update the drawable for toggleJavaScript.
424         MenuItem toggleJavaScript = mainMenu.findItem(R.id.toggleJavaScript);
425
426         // Set the commands that relate to the menu entries.
427         switch (menuItemId) {
428             case R.id.toggleJavaScript:
429                 if (javaScriptEnabled) {
430                     javaScriptEnabled = false;
431                     mainWebView.getSettings().setJavaScriptEnabled(false);
432                     mainWebView.reload();
433
434                     // Update the toggleJavaScript icon and display a snackbar.
435                     if (domStorageEnabled || cookiesEnabled) {
436                         menuItem.setIcon(R.drawable.warning);
437                         if (domStorageEnabled && cookiesEnabled) {
438                             Snackbar.make(findViewById(R.id.mainWebView), R.string.both_still_enabled, Snackbar.LENGTH_SHORT).show();
439                         } else {
440                             if (domStorageEnabled) {
441                                 Snackbar.make(findViewById(R.id.mainWebView), R.string.dom_storage_still_enabled, Snackbar.LENGTH_SHORT).show();
442                             } else {
443                                 Snackbar.make(findViewById(R.id.mainWebView), R.string.cookies_still_enabled, Snackbar.LENGTH_SHORT).show();
444                             }
445                         }
446                     } else {
447                         menuItem.setIcon(R.drawable.privacy_mode);
448                         Snackbar.make(findViewById(R.id.mainWebView), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
449                     }
450                 } else {
451                     javaScriptEnabled = true;
452                     menuItem.setIcon(R.drawable.javascript_enabled);
453                     mainWebView.getSettings().setJavaScriptEnabled(true);
454                     mainWebView.reload();
455                     Snackbar.make(findViewById(R.id.mainWebView), R.string.javascript_enabled, Snackbar.LENGTH_SHORT).show();
456                 }
457                 return true;
458
459             case R.id.toggleDomStorage:
460                 if (domStorageEnabled) {
461                     domStorageEnabled = false;
462                     menuItem.setChecked(false);
463                     mainWebView.getSettings().setDomStorageEnabled(false);
464                     mainWebView.reload();
465
466                     // Update the toggleJavaScript icon and display a snackbar if appropriate.
467                     if (!javaScriptEnabled && !cookiesEnabled) {
468                         toggleJavaScript.setIcon(R.drawable.privacy_mode);
469                         Snackbar.make(findViewById(R.id.mainWebView), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
470                     } else {
471                         if (cookiesEnabled) {
472                             toggleJavaScript.setIcon(R.drawable.warning);
473                             Snackbar.make(findViewById(R.id.mainWebView), R.string.cookies_still_enabled, Snackbar.LENGTH_SHORT).show();
474                         } // Else Do nothing because JavaScript is enabled.
475                     }
476                 } else {
477                     domStorageEnabled = true;
478                     menuItem.setChecked(true);
479                     mainWebView.getSettings().setDomStorageEnabled(true);
480                     mainWebView.reload();
481
482                     // Update the toggleJavaScript icon if appropriate.
483                     if (!javaScriptEnabled) {
484                         toggleJavaScript.setIcon(R.drawable.warning);
485                     } // Else Do nothing because JavaScript is enabled.
486
487                     Snackbar.make(findViewById(R.id.mainWebView), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
488                 }
489                 return true;
490
491             /* toggleSaveFormData does nothing until database storage is implemented.
492             case R.id.toggleSaveFormData:
493                 if (saveFormDataEnabled) {
494                     saveFormDataEnabled = false;
495                     menuItem.setChecked(false);
496                     mainWebView.getSettings().setSaveFormData(false);
497                     mainWebView.reload();
498                 } else {
499                     saveFormDataEnabled = true;
500                     menuItem.setChecked(true);
501                     mainWebView.getSettings().setSaveFormData(true);
502                     mainWebView.reload();
503                 }
504                 return true;
505             */
506
507             case R.id.toggleCookies:
508                 if (cookiesEnabled) {
509                     cookiesEnabled = false;
510                     menuItem.setChecked(false);
511                     cookieManager.setAcceptCookie(false);
512                     mainWebView.reload();
513
514                     // Update the toggleJavaScript icon and display a snackbar if appropriate.
515                     if (!javaScriptEnabled && !domStorageEnabled) {
516                         toggleJavaScript.setIcon(R.drawable.privacy_mode);
517                         Snackbar.make(findViewById(R.id.mainWebView), R.string.privacy_mode, Snackbar.LENGTH_SHORT).show();
518                     } else {
519                         if (domStorageEnabled) {
520                             toggleJavaScript.setIcon(R.drawable.warning);
521                             Snackbar.make(findViewById(R.id.mainWebView), R.string.dom_storage_enabled, Snackbar.LENGTH_SHORT).show();
522                         } // Else Do nothing because JavaScript is enabled.
523                     }
524                 } else {
525                     cookiesEnabled = true;
526                     menuItem.setChecked(true);
527                     cookieManager.setAcceptCookie(true);
528                     mainWebView.reload();
529
530                     // Update the toggleJavaScript icon if appropriate.
531                     if (!javaScriptEnabled) {
532                         toggleJavaScript.setIcon(R.drawable.warning);
533                     } // Else Do nothing because JavaScript is enabled.
534
535                     Snackbar.make(findViewById(R.id.mainWebView), R.string.cookies_enabled, Snackbar.LENGTH_SHORT).show();
536                 }
537                 return true;
538
539             case R.id.clearDomStorage:
540                 WebStorage webStorage = WebStorage.getInstance();
541                 webStorage.deleteAllData();
542                 Snackbar.make(findViewById(R.id.mainWebView), R.string.dom_storage_deleted, Snackbar.LENGTH_SHORT).show();
543                 return true;
544
545             case R.id.clearCookies:
546                 if (Build.VERSION.SDK_INT < 21) {
547                     cookieManager.removeAllCookie();
548                 } else {
549                     cookieManager.removeAllCookies(null);
550                 }
551                 Snackbar.make(findViewById(R.id.mainWebView), R.string.cookies_deleted, Snackbar.LENGTH_SHORT).show();
552                 return true;
553
554             case R.id.addToHomescreen:
555                 // Show the CreateHomeScreenShortcut AlertDialog and name this instance createShortcut.
556                 AppCompatDialogFragment shortcutDialog = new CreateHomeScreenShortcut();
557                 shortcutDialog.show(getSupportFragmentManager(), "createShortcut");
558
559                 //Everything else will be handled by CreateHomeScreenShortcut and the associated listeners below.
560                 return true;
561
562             case R.id.downloads:
563                 // Launch the system Download Manager.
564                 Intent downloadManagerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
565
566                 // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list.
567                 downloadManagerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
568
569                 startActivity(downloadManagerIntent);
570                 return true;
571
572             case R.id.home:
573                 mainWebView.loadUrl(homepage);
574                 return true;
575
576             case R.id.back:
577                 mainWebView.goBack();
578                 return true;
579
580             case R.id.forward:
581                 mainWebView.goForward();
582                 return true;
583
584             case R.id.share:
585                 Intent shareIntent = new Intent();
586                 shareIntent.setAction(Intent.ACTION_SEND);
587                 shareIntent.putExtra(Intent.EXTRA_TEXT, urlTextBox.getText().toString());
588                 shareIntent.setType("text/plain");
589                 startActivity(Intent.createChooser(shareIntent, "Share URL"));
590                 return true;
591
592             case R.id.settings:
593                 // Launch SettingsActivity.
594                 Intent intent = new Intent(this, SettingsActivity.class);
595                 startActivity(intent);
596                 return true;
597
598             case R.id.about:
599                 // Show the AboutDialog AlertDialog and name this instance aboutDialog.
600                 AppCompatDialogFragment aboutDialog = new AboutDialog();
601                 aboutDialog.show(getSupportFragmentManager(), "aboutDialog");
602                 return true;
603
604             case R.id.clearAndExit:
605                 // Clear DOM storage.
606                 WebStorage domStorage = WebStorage.getInstance();
607                 domStorage.deleteAllData();
608
609                 // Clear cookies.  The commands changed slightly in API 21.
610                 if (Build.VERSION.SDK_INT >= 21) {
611                     cookieManager.removeAllCookies(null);
612                 } else {
613                     cookieManager.removeAllCookie();
614                 }
615
616                 // Destroy the internal state of the webview.
617                 mainWebView.destroy();
618
619                 // Close Privacy Browser.  finishAndRemoveTask also removes Privacy Browser from the recent app list.
620                 if (Build.VERSION.SDK_INT >= 21) {
621                     finishAndRemoveTask();
622                 } else {
623                     finish();
624                 }
625                 return true;
626
627             default:
628                 return super.onOptionsItemSelected(menuItem);
629         }
630     }
631
632     @Override
633     public void onCreateHomeScreenShortcutCancel(DialogFragment dialog) {
634         // Do nothing because the user selected "Cancel".
635     }
636
637     @Override
638     public void onCreateHomeScreenShortcutCreate(DialogFragment dialog) {
639         // Get shortcutNameEditText from the alert dialog.
640         EditText shortcutNameEditText = (EditText) dialog.getDialog().findViewById(R.id.shortcutNameEditText);
641
642         // Create the bookmark shortcut based on formattedUrlString.
643         Intent bookmarkShortcut = new Intent();
644         bookmarkShortcut.setAction(Intent.ACTION_VIEW);
645         bookmarkShortcut.setData(Uri.parse(formattedUrlString));
646
647         // Place the bookmark shortcut on the home screen.
648         Intent placeBookmarkShortcut = new Intent();
649         placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.INTENT", bookmarkShortcut);
650         placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.NAME", shortcutNameEditText.getText().toString());
651         placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.ICON", favoriteIcon);
652         placeBookmarkShortcut.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
653         sendBroadcast(placeBookmarkShortcut);
654     }
655
656     // Override onBackPressed so that if mainWebView can go back it does when the system back button is pressed.
657     @Override
658     public void onBackPressed() {
659         final WebView mainWebView = (WebView) findViewById(R.id.mainWebView);
660
661         if (mainWebView.canGoBack()) {
662             mainWebView.goBack();
663         } else {
664             super.onBackPressed();
665         }
666     }
667
668     private void loadUrlFromTextBox() throws UnsupportedEncodingException {
669         // Get the text from urlTextBox and convert it to a string.
670         String unformattedUrlString = urlTextBox.getText().toString();
671         URL unformattedUrl = null;
672         Uri.Builder formattedUri = new Uri.Builder();
673
674         // Check to see if unformattedUrlString is a valid URL.  Otherwise, convert it into a Duck Duck Go search.
675         if (Patterns.WEB_URL.matcher(unformattedUrlString).matches()) {
676             // Add http:// at the beginning if it is missing.  Otherwise the app will segfault.
677             if (!unformattedUrlString.startsWith("http")) {
678                 unformattedUrlString = "http://" + unformattedUrlString;
679             }
680
681             // Convert unformattedUrlString to a URL, then to a URI, and then back to a string, which sanitizes the input and adds in any missing components.
682             try {
683                 unformattedUrl = new URL(unformattedUrlString);
684             } catch (MalformedURLException e) {
685                 e.printStackTrace();
686             }
687
688             // The ternary operator (? :) makes sure that a null pointer exception is not thrown, which would happen if .get was called on a null value.
689             final String scheme = unformattedUrl != null ? unformattedUrl.getProtocol() : null;
690             final String authority = unformattedUrl != null ? unformattedUrl.getAuthority() : null;
691             final String path = unformattedUrl != null ? unformattedUrl.getPath() : null;
692             final String query = unformattedUrl != null ? unformattedUrl.getQuery() : null;
693             final String fragment = unformattedUrl != null ? unformattedUrl.getRef() : null;
694
695             formattedUri.scheme(scheme).authority(authority).path(path).query(query).fragment(fragment);
696             formattedUrlString = formattedUri.build().toString();
697         } else {
698             // Sanitize the search input and convert it to a DuckDuckGo search.
699             final String encodedUrlString = URLEncoder.encode(unformattedUrlString, "UTF-8");
700
701             // Use the correct search URL based on javaScriptEnabled.
702             if (javaScriptEnabled) {
703                 formattedUrlString = "https://duckduckgo.com/?q=" + encodedUrlString;
704             } else {
705                 formattedUrlString = "https://duckduckgo.com/html/?q=" + encodedUrlString;
706             }
707         }
708
709         mainWebView.loadUrl(formattedUrlString);
710
711         // Hides the keyboard so we can see the webpage.
712         InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
713         inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
714     }
715 }