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