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