Create an "Add to Home Screen" AlertDialog.
[PrivacyBrowser.git] / app / src / main / java / com / stoutner / privacybrowser / Webview.java
1 package com.stoutner.privacybrowser;
2
3 import android.annotation.SuppressLint;
4 import android.annotation.TargetApi;
5 import android.app.Activity;
6 import android.app.DownloadManager;
7 import android.content.ClipData;
8 import android.content.ClipboardManager;
9 import android.content.Context;
10 import android.content.Intent;
11 import android.graphics.Bitmap;
12 import android.net.Uri;
13 import android.os.Build;
14 import android.os.Bundle;
15 import android.support.v4.app.DialogFragment;
16 import android.support.v7.app.ActionBar;
17 import android.support.v7.app.AppCompatActivity;
18 import android.support.v7.app.AppCompatDialogFragment;
19 import android.util.Patterns;
20 import android.view.KeyEvent;
21 import android.view.Menu;
22 import android.view.MenuItem;
23 import android.view.View;
24 import android.view.inputmethod.InputMethodManager;
25 import android.webkit.DownloadListener;
26 import android.webkit.WebChromeClient;
27 import android.webkit.WebResourceError;
28 import android.webkit.WebResourceRequest;
29 import android.webkit.WebView;
30 import android.webkit.WebViewClient;
31 import android.widget.EditText;
32 import android.widget.FrameLayout;
33 import android.widget.ImageView;
34 import android.widget.ProgressBar;
35 import android.widget.Toast;
36 import java.io.UnsupportedEncodingException;
37 import java.net.MalformedURLException;
38 import java.net.URL;
39 import java.net.URLEncoder;
40
41 public class Webview extends AppCompatActivity implements CreateHomeScreenShortcut.CreateHomeScreenSchortcutListener {
42     // favoriteIcon is public static so it can be accessed from CreateHomeScreenShortcut.
43     public static Bitmap favoriteIcon;
44
45     private String formattedUrlString;
46     private String homepage = "https://www.duckduckgo.com/";
47
48     // Remove Android Studio's warning about the dangers of using SetJavaScriptEnabled.
49     @SuppressLint("SetJavaScriptEnabled")
50
51     @Override
52     protected void onCreate(Bundle savedInstanceState) {
53         super.onCreate(savedInstanceState);
54         setContentView(R.layout.activity_webview);
55
56         final WebView mainWebView = (WebView) findViewById(R.id.mainWebView);
57         final FrameLayout fullScreenVideoFrameLayout = (FrameLayout) findViewById(R.id.fullScreenVideoFrameLayout);
58         final Activity mainWebViewActivity = this;
59
60         final ActionBar actionBar = getSupportActionBar();
61         if (actionBar != null) {
62             // Remove the title from the action bar.
63             actionBar.setDisplayShowTitleEnabled(false);
64
65             // Add the custom app_bar layout, which shows the favoriteIcon, urlTextBar, and progressBar.
66             actionBar.setCustomView(R.layout.app_bar);
67             actionBar.setDisplayOptions(ActionBar.DISPLAY_SHOW_CUSTOM);
68
69             // Set the "go" button on the keyboard to load the URL in urlTextBox.
70             EditText urlTextBox = (EditText) actionBar.getCustomView().findViewById(R.id.urlTextBox);
71             urlTextBox.setOnKeyListener(new View.OnKeyListener() {
72                 public boolean onKey(View v, int keyCode, KeyEvent event) {
73                     // If the event is a key-down event on the "enter" button, load the URL.
74                     if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
75                         // Load the URL into the mainWebView and consume the event.
76                         try {
77                             loadUrlFromTextBox();
78                         } catch (UnsupportedEncodingException e) {
79                             e.printStackTrace();
80                         }
81                         // If the enter key was pressed, consume the event.
82                         return true;
83                     } else {
84                         // If any other key was pressed, do not consume the event.
85                         return false;
86                     }
87                 }
88             });
89         }
90
91         mainWebView.setWebViewClient(new WebViewClient() {
92             // shouldOverrideUrlLoading makes this WebView the default handler for URLs inside the app, so that links are not kicked out to other apps.
93             @Override
94             public boolean shouldOverrideUrlLoading(WebView view, String url) {
95                 mainWebView.loadUrl(url);
96                 return true;
97             }
98
99             public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
100                 Toast.makeText(mainWebViewActivity, "Error loading " + request + "   Error: " + error, Toast.LENGTH_LONG).show();
101             }
102
103             // Update the URL in urlTextBox when the page starts to load.
104             @Override
105             public void onPageStarted(WebView view, String url, Bitmap favicon) {
106                 if (actionBar != null) {
107                     EditText urlTextBox = (EditText) actionBar.getCustomView().findViewById(R.id.urlTextBox);
108                     urlTextBox.setText(url);
109                 }
110             }
111
112             // Update formattedUrlString and urlTextBox.  It is necessary to do this after the page finishes loading because the final URL can change during load.
113             @Override
114             public void onPageFinished(WebView view, String url) {
115                 formattedUrlString = url;
116
117                 if (actionBar != null) {
118                     EditText urlTextBox = (EditText) actionBar.getCustomView().findViewById(R.id.urlTextBox);
119                     urlTextBox.setText(formattedUrlString);
120                 }
121             }
122         });
123
124         mainWebView.setWebChromeClient(new WebChromeClient() {
125             // Update the progress bar when a page is loading.
126             @Override
127             public void onProgressChanged(WebView view, int progress) {
128                 // Make sure that actionBar is not null.
129                 if (actionBar != null) {
130                     ProgressBar progressBar = (ProgressBar) actionBar.getCustomView().findViewById(R.id.progressBar);
131                     progressBar.setProgress(progress);
132                     if (progress < 100) {
133                         progressBar.setVisibility(View.VISIBLE);
134                     } else {
135                         progressBar.setVisibility(View.GONE);
136                     }
137                 }
138             }
139
140             // Set the favorite icon when it changes.
141             @Override
142             public void onReceivedIcon(WebView view, Bitmap icon) {
143                 // Save a copy of the favorite icon for use if a shortcut is added to the home screen.
144                 favoriteIcon = icon;
145
146                 // Place the favorite icon in the actionBar if it is not null.
147                 if (actionBar != null) {
148                     ImageView imageViewFavoriteIcon = (ImageView) actionBar.getCustomView().findViewById(R.id.favoriteIcon);
149                     imageViewFavoriteIcon.setImageBitmap(Bitmap.createScaledBitmap(icon, 64, 64, true));
150                 }
151             }
152
153             // Enter full screen video
154             @Override
155             public void onShowCustomView(View view, CustomViewCallback callback) {
156                 getSupportActionBar().hide();
157
158                 fullScreenVideoFrameLayout.addView(view);
159                 fullScreenVideoFrameLayout.setVisibility(View.VISIBLE);
160
161                 mainWebView.setVisibility(View.GONE);
162
163                 /* SYSTEM_UI_FLAG_HIDE_NAVIGATION hides the navigation bars on the bottom or right of the screen.
164                 ** SYSTEM_UI_FLAG_FULLSCREEN hides the status bar across the top of the screen.
165                 ** SYSTEM_UI_FLAG_IMMERSIVE_STICKY makes the navigation and status bars ghosted overlays and automatically rehides them.
166                 */
167
168                 // Set the one flag supported by API >= 14.
169                 if (Build.VERSION.SDK_INT >= 14) {
170                     view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
171                 }
172
173                 // Set the two flags that are supported by API >= 16.
174                 if (Build.VERSION.SDK_INT >= 16) {
175                     view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN);
176                 }
177
178                 // Set all three flags that are supported by API >= 19.
179                 if (Build.VERSION.SDK_INT >= 19) {
180                     view.setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
181                 }
182             }
183
184             // Exit full screen video
185             public void onHideCustomView() {
186                 getSupportActionBar().show();
187
188                 mainWebView.setVisibility(View.VISIBLE);
189
190                 fullScreenVideoFrameLayout.removeAllViews();
191                 fullScreenVideoFrameLayout.setVisibility(View.GONE);
192             }
193         });
194
195         mainWebView.setDownloadListener(new DownloadListener() {
196             // Launch the Android download manager when a link leads to a download.
197             @Override
198             public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
199                 DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
200                 DownloadManager.Request requestUri = new DownloadManager.Request(Uri.parse(url));
201
202                 // Add the URL as the description for the download.
203                 requestUri.setDescription(url);
204
205                 // Show the download notification after the download is completed if the API is 11 or greater.
206                 if (Build.VERSION.SDK_INT >= 11) {
207                     requestUri.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
208                 }
209
210                 downloadManager.enqueue(requestUri);
211                 Toast.makeText(mainWebViewActivity, "Download started", Toast.LENGTH_SHORT).show();
212             }
213         });
214
215         // Allow pinch to zoom.
216         mainWebView.getSettings().setBuiltInZoomControls(true);
217
218         // Hide zoom controls if the API is 11 or greater.
219         if (Build.VERSION.SDK_INT >= 11) {
220             mainWebView.getSettings().setDisplayZoomControls(false);
221         }
222
223         // Enable JavaScript.
224         mainWebView.getSettings().setJavaScriptEnabled(true);
225
226         // Enable DOM Storage.
227         mainWebView.getSettings().setDomStorageEnabled(true);
228
229         // Get the intent information that started the app.
230         final Intent intent = getIntent();
231
232         if (intent.getData() != null) {
233             // Get the intent data and convert it to a string.
234             final Uri intentUriData = intent.getData();
235             formattedUrlString = intentUriData.toString();
236         }
237
238         // If formattedUrlString is null assign the homepage to it.
239         if (formattedUrlString == null) {
240             formattedUrlString = homepage;
241         }
242
243         // Load the initial website.
244         mainWebView.loadUrl(formattedUrlString);
245     }
246
247     @Override
248     public boolean onCreateOptionsMenu(Menu menu) {
249         // Inflate the menu; this adds items to the action bar if it is present.
250         getMenuInflater().inflate(R.menu.menu_webview, menu);
251         return true;
252     }
253
254     // @TargetApi(11) turns off the errors regarding copy and paste, which are removed from view in menu_webview.xml for lower version of Android.
255     @Override
256     @TargetApi(11)
257     public boolean onOptionsItemSelected(MenuItem menuItem) {
258         int menuItemId = menuItem.getItemId();
259         ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
260         ActionBar actionBar = getSupportActionBar();
261         final WebView mainWebView = (WebView) findViewById(R.id.mainWebView);
262
263         // Sets the commands that relate to the menu entries.
264         switch (menuItemId) {
265             case R.id.home:
266                 mainWebView.loadUrl(homepage);
267                 break;
268
269             case R.id.refresh:
270                 mainWebView.loadUrl(formattedUrlString);
271                 break;
272
273             case R.id.back:
274                 mainWebView.goBack();
275                 break;
276
277             case R.id.forward:
278                 mainWebView.goForward();
279                 break;
280
281             case R.id.copyURL:
282                 // Make sure that actionBar is not null.
283                 if (actionBar != null) {
284                     EditText urlTextBox = (EditText) actionBar.getCustomView().findViewById(R.id.urlTextBox);
285                     clipboard.setPrimaryClip(ClipData.newPlainText("URL", urlTextBox.getText()));
286                 }
287                 break;
288
289             case R.id.pasteURL:
290                 // Make sure that actionBar is not null.
291                 if (actionBar != null) {
292                     ClipData.Item clipboardData = clipboard.getPrimaryClip().getItemAt(0);
293                     EditText urlTextBox = (EditText) actionBar.getCustomView().findViewById(R.id.urlTextBox);
294                     urlTextBox.setText(clipboardData.coerceToText(this));
295                     try {
296                         loadUrlFromTextBox();
297                     } catch (UnsupportedEncodingException e) {
298                         e.printStackTrace();
299                     }
300                 }
301                 break;
302
303             case R.id.shareURL:
304                 // Make sure that actionBar is not null.
305                 if (actionBar != null) {
306                     EditText urlTextBox = (EditText) actionBar.getCustomView().findViewById(R.id.urlTextBox);
307                     Intent shareIntent = new Intent();
308                     shareIntent.setAction(Intent.ACTION_SEND);
309                     shareIntent.putExtra(Intent.EXTRA_TEXT, urlTextBox.getText().toString());
310                     shareIntent.setType("text/plain");
311                     startActivity(Intent.createChooser(shareIntent, "Share URL"));
312                 }
313                 break;
314
315             case R.id.addToHomescreen:
316                 // Show the CreateHomeScreenShortcut AlertDialog and name this instance createShortcut.
317                 AppCompatDialogFragment shortcutDialog = new CreateHomeScreenShortcut();
318                 shortcutDialog.show(getSupportFragmentManager(), "createShortcut");
319
320                 //Everything else will be handled by CreateHomeScreenShortcut and the associated listeners below.
321                 break;
322
323             case R.id.downloads:
324                 // Launch the system Download Manager.
325                 Intent downloadManangerIntent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS);
326
327                 // Launch as a new task so that Download Manager and Privacy Browser show as separate windows in the recent tasks list.
328                 downloadManangerIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
329
330                 startActivity(downloadManangerIntent);
331                 break;
332         }
333
334         return super.onOptionsItemSelected(menuItem);
335     }
336
337     @Override
338     public void onCreateHomeScreenShortcutCancel(DialogFragment dialog) {
339         // Do nothing because the user selected "Cancel".
340     }
341
342     @Override
343     public void onCreateHomeScreenShortcutCreate(DialogFragment dialog) {
344         // Get shortcutNameEditText from the alert dialog.
345         EditText shortcutNameEditText = (EditText) dialog.getDialog().findViewById(R.id.shortcutNameEditText);
346
347         // Create the bookmark shortcut based on formattedUrlString.
348         Intent bookmarkShortcut = new Intent();
349         bookmarkShortcut.setAction(Intent.ACTION_VIEW);
350         bookmarkShortcut.setData(Uri.parse(formattedUrlString));
351
352         // Place the bookmark shortcut on the home screen.
353         Intent placeBookmarkShortcut = new Intent();
354         placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.INTENT", bookmarkShortcut);
355         placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.NAME", shortcutNameEditText.getText().toString());
356         placeBookmarkShortcut.putExtra("android.intent.extra.shortcut.ICON", favoriteIcon);
357         placeBookmarkShortcut.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
358         sendBroadcast(placeBookmarkShortcut);
359     }
360
361     // Override onBackPressed so that if mainWebView can go back it does when the system back button is pressed.
362     @Override
363     public void onBackPressed() {
364         final WebView mainWebView = (WebView) findViewById(R.id.mainWebView);
365
366         if (mainWebView.canGoBack()) {
367             mainWebView.goBack();
368         } else {
369             super.onBackPressed();
370         }
371     }
372
373     public void loadUrlFromTextBox() throws UnsupportedEncodingException {
374         // Make sure that actionBar is not null.
375         ActionBar actionBar = getSupportActionBar();
376         if (actionBar != null) {
377             final WebView mainWebView = (WebView) findViewById(R.id.mainWebView);
378             EditText urlTextBox = (EditText) actionBar.getCustomView().findViewById(R.id.urlTextBox);
379
380             // Get the text from urlTextInput and convert it to a string.
381             String unformattedUrlString = urlTextBox.getText().toString();
382             URL unformattedUrl = null;
383             Uri.Builder formattedUri = new Uri.Builder();
384
385             // Check to see if unformattedUrlString is a valid URL.  Otherwise, convert it into a Duck Duck Go search.
386             if (Patterns.WEB_URL.matcher(unformattedUrlString).matches()) {
387
388                 // Add http:// at the beginning if it is missing.  Otherwise the app will segfault.
389                 if (!unformattedUrlString.startsWith("http")) {
390                     unformattedUrlString = "http://" + unformattedUrlString;
391                 }
392
393                 // 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.
394                 try {
395                     unformattedUrl = new URL(unformattedUrlString);
396                 } catch (MalformedURLException e) {
397                     e.printStackTrace();
398                 }
399
400                 // The ternary operator (? :) makes sure that a null pointer exception is not thrown, which would happen if .get was called on a null value.
401                 final String scheme = unformattedUrl != null ? unformattedUrl.getProtocol() : null;
402                 final String authority = unformattedUrl != null ? unformattedUrl.getAuthority() : null;
403                 final String path = unformattedUrl != null ? unformattedUrl.getPath() : null;
404                 final String query = unformattedUrl != null ? unformattedUrl.getQuery() : null;
405                 final String fragment = unformattedUrl != null ? unformattedUrl.getRef() : null;
406
407                 formattedUri.scheme(scheme).authority(authority).path(path).query(query).fragment(fragment);
408                 formattedUrlString = formattedUri.build().toString();
409
410             } else {
411                 // Sanitize the search input and convert it to a DuckDuckGo search.
412                 final String encodedUrlString = URLEncoder.encode(unformattedUrlString, "UTF-8");
413                 formattedUrlString = "https://duckduckgo.com/?q=" + encodedUrlString;
414             }
415
416             mainWebView.loadUrl(formattedUrlString);
417
418             // Hides the keyboard so we can see the webpage.
419             InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Activity.INPUT_METHOD_SERVICE);
420             inputMethodManager.hideSoftInputFromWindow(mainWebView.getWindowToken(), 0);
421         }
422     }
423 }