mFileUploadCallbackSecond;
87 | protected long mLastError;
88 | protected String mLanguageIso3;
89 | protected int mRequestCodeFilePicker = REQUEST_CODE_FILE_PICKER;
90 | protected WebViewClient mCustomWebViewClient;
91 | protected WebChromeClient mCustomWebChromeClient;
92 | protected boolean mGeolocationEnabled;
93 | protected String mUploadableFileTypes = "*/*";
94 |
95 | public AdvancedWebView(Context context) {
96 | super(context);
97 | init(context);
98 | }
99 |
100 | public AdvancedWebView(Context context, AttributeSet attrs) {
101 | super(context, attrs);
102 | init(context);
103 | }
104 |
105 | public AdvancedWebView(Context context, AttributeSet attrs, int defStyleAttr) {
106 | super(context, attrs, defStyleAttr);
107 | init(context);
108 | }
109 |
110 | protected static void setAllowAccessFromFileUrls(final WebSettings webSettings) {
111 | if (Build.VERSION.SDK_INT >= 16) {
112 | webSettings.setAllowFileAccessFromFileURLs(false);
113 | webSettings.setAllowUniversalAccessFromFileURLs(false);
114 | }
115 | }
116 |
117 | protected static String makeUrlUnique(final String url) {
118 | StringBuilder unique = new StringBuilder();
119 | unique.append(url);
120 |
121 | if (url.contains("?")) {
122 | unique.append('&');
123 | } else {
124 | if (url.lastIndexOf('/') <= 7) {
125 | unique.append('/');
126 | }
127 | unique.append('?');
128 | }
129 |
130 | unique.append(System.currentTimeMillis());
131 | unique.append('=');
132 | unique.append(1);
133 |
134 | return unique.toString();
135 | }
136 |
137 | protected static String getLanguageIso3() {
138 | try {
139 | return Locale.getDefault().getISO3Language().toLowerCase(Locale.US);
140 | } catch (MissingResourceException e) {
141 | return LANGUAGE_DEFAULT_ISO3;
142 | }
143 | }
144 |
145 | protected static String decodeBase64(final String base64) throws IllegalArgumentException, UnsupportedEncodingException {
146 | final byte[] bytes = Base64.decode(base64, Base64.DEFAULT);
147 | return new String(bytes, CHARSET_DEFAULT);
148 | }
149 |
150 | /**
151 | * Returns whether file uploads can be used on the current device (generally all platform versions except for 4.4)
152 | *
153 | * @return whether file uploads can be used
154 | */
155 | public static boolean isFileUploadAvailable() {
156 | return isFileUploadAvailable(false);
157 | }
158 |
159 | /**
160 | * Returns whether file uploads can be used on the current device (generally all platform versions except for 4.4)
161 | *
162 | * On Android 4.4.3/4.4.4, file uploads may be possible but will come with a wrong MIME type
163 | *
164 | * @param needsCorrectMimeType whether a correct MIME type is required for file uploads or `application/octet-stream` is acceptable
165 | * @return whether file uploads can be used
166 | */
167 | public static boolean isFileUploadAvailable(final boolean needsCorrectMimeType) {
168 | if (Build.VERSION.SDK_INT == 19) {
169 | final String platformVersion = (Build.VERSION.RELEASE == null) ? "" : Build.VERSION.RELEASE;
170 |
171 | return !needsCorrectMimeType && (platformVersion.startsWith("4.4.3") || platformVersion.startsWith("4.4.4"));
172 | } else {
173 | return true;
174 | }
175 | }
176 |
177 | /**
178 | * Handles a download by loading the file from `fromUrl` and saving it to `toFilename` on the external storage
179 | *
180 | * This requires the two permissions `android.permission.INTERNET` and `android.permission.WRITE_EXTERNAL_STORAGE`
181 | *
182 | * Only supported on API level 9 (Android 2.3) and above
183 | *
184 | * @param context a valid `Context` reference
185 | * @param fromUrl the URL of the file to download, e.g. the one from `AdvancedWebView.onDownloadRequested(...)`
186 | * @param toFilename the name of the destination file where the download should be saved, e.g. `myImage.jpg`
187 | * @return whether the download has been successfully handled or not
188 | * @throws IllegalStateException if the storage or the target directory could not be found or accessed
189 | */
190 | public static boolean handleDownload(final Context context, final String fromUrl, final String toFilename) {
191 |
192 | final Request request = new Request(Uri.parse(fromUrl));
193 | request.allowScanningByMediaScanner();
194 | request.setNotificationVisibility(Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
195 | request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, toFilename);
196 |
197 | final DownloadManager dm = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
198 | try {
199 | try {
200 | dm.enqueue(request);
201 | } catch (SecurityException e) {
202 | request.setNotificationVisibility(Request.VISIBILITY_VISIBLE);
203 | dm.enqueue(request);
204 | }
205 |
206 | return true;
207 | } catch (SecurityException e) {
208 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
209 | Toast.makeText(context, "Please enable STORAGE permission!",
210 | Toast.LENGTH_LONG).show();
211 | openAppSettings(context);
212 | }
213 | return false;
214 | }
215 | // if the download manager app has been disabled on the device
216 | catch (IllegalArgumentException e) {
217 | // show the settings screen where the user can enable the download manager app again
218 | openAppSettings(context);
219 |
220 | return false;
221 | }
222 | }
223 |
224 | private static void openAppSettings(final Context context) {
225 | try {
226 | final Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
227 | intent.setData(Uri.parse("package:" + AdvancedWebView.PACKAGE_NAME_DOWNLOAD_MANAGER));
228 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
229 |
230 | context.startActivity(intent);
231 |
232 | } catch (Exception ignored) {
233 | }
234 | }
235 |
236 | public void setListener(final Activity activity, final Listener listener) {
237 | setListener(activity, listener, REQUEST_CODE_FILE_PICKER);
238 | }
239 |
240 | public void setListener(final Activity activity, final Listener listener, final int requestCodeFilePicker) {
241 | if (activity != null) {
242 | mActivity = new WeakReference<>(activity);
243 | } else {
244 | mActivity = null;
245 | }
246 |
247 | setListener(listener, requestCodeFilePicker);
248 | }
249 |
250 | protected void setListener(final Listener listener, final int requestCodeFilePicker) {
251 | mListener = listener;
252 | mRequestCodeFilePicker = requestCodeFilePicker;
253 | }
254 |
255 | @Override
256 | public void setWebViewClient(final WebViewClient client) {
257 | mCustomWebViewClient = client;
258 | }
259 |
260 | @Override
261 | public void setWebChromeClient(final WebChromeClient client) {
262 | mCustomWebChromeClient = client;
263 | }
264 |
265 | public void setGeolocationEnabled(final boolean enabled) {
266 | if (enabled) {
267 | getSettings().setJavaScriptEnabled(true);
268 | getSettings().setGeolocationEnabled(true);
269 | setGeolocationDatabasePath();
270 | }
271 |
272 | mGeolocationEnabled = enabled;
273 | }
274 |
275 | protected void setGeolocationDatabasePath() {
276 | final Activity activity;
277 |
278 | if (mFragment != null && mFragment.get() != null && mFragment.get().getActivity() != null) {
279 | activity = mFragment.get().getActivity();
280 | } else if (mActivity != null && mActivity.get() != null) {
281 | activity = mActivity.get();
282 | } else {
283 | return;
284 | }
285 |
286 | if (activity != null) {
287 | getSettings().setGeolocationDatabasePath(activity.getFilesDir().getPath());
288 | }
289 | }
290 |
291 | public void setUploadableFileTypes(final String mimeType) {
292 | mUploadableFileTypes = mimeType;
293 | }
294 |
295 | /**
296 | * Loads and displays the provided HTML source text
297 | *
298 | * @param html the HTML source text to load
299 | */
300 | public void loadHtml(final String html) {
301 | loadHtml(html, null);
302 | }
303 |
304 | /**
305 | * Loads and displays the provided HTML source text
306 | *
307 | * @param html the HTML source text to load
308 | * @param baseUrl the URL to use as the page's base URL
309 | */
310 | public void loadHtml(final String html, final String baseUrl) {
311 | loadHtml(html, baseUrl, null);
312 | }
313 |
314 | /**
315 | * Loads and displays the provided HTML source text
316 | *
317 | * @param html the HTML source text to load
318 | * @param baseUrl the URL to use as the page's base URL
319 | * @param historyUrl the URL to use for the page's history entry
320 | */
321 | public void loadHtml(final String html, final String baseUrl, final String historyUrl) {
322 | loadHtml(html, baseUrl, historyUrl, "utf-8");
323 | }
324 |
325 | /**
326 | * Loads and displays the provided HTML source text
327 | *
328 | * @param html the HTML source text to load
329 | * @param baseUrl the URL to use as the page's base URL
330 | * @param historyUrl the URL to use for the page's history entry
331 | * @param encoding the encoding or charset of the HTML source text
332 | */
333 | public void loadHtml(final String html, final String baseUrl, final String historyUrl, final String encoding) {
334 | loadDataWithBaseURL(baseUrl, html, "text/html", encoding, historyUrl);
335 | }
336 |
337 | public void onResume() {
338 | super.onResume();
339 | resumeTimers();
340 | }
341 |
342 | public void onPause() {
343 | pauseTimers();
344 | super.onPause();
345 | }
346 |
347 | public void onDestroy() {
348 | // try to remove this view from its parent first
349 | try {
350 | ((ViewGroup) getParent()).removeView(this);
351 | } catch (Exception ignored) {
352 | }
353 |
354 | // then try to remove all child views from this view
355 | try {
356 | removeAllViews();
357 | } catch (Exception ignored) {
358 | }
359 |
360 | // and finally destroy this view
361 | destroy();
362 | }
363 |
364 | public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
365 | if (requestCode == mRequestCodeFilePicker) {
366 | if (resultCode == Activity.RESULT_OK) {
367 | if (intent != null) {
368 | if (mFileUploadCallbackFirst != null) {
369 | mFileUploadCallbackFirst.onReceiveValue(intent.getData());
370 | mFileUploadCallbackFirst = null;
371 | } else if (mFileUploadCallbackSecond != null) {
372 | Uri[] dataUris = null;
373 |
374 | try {
375 | if (intent.getDataString() != null) {
376 | dataUris = new Uri[]{Uri.parse(intent.getDataString())};
377 | } else {
378 | if (Build.VERSION.SDK_INT >= 16) {
379 | if (intent.getClipData() != null) {
380 | final int numSelectedFiles = intent.getClipData().getItemCount();
381 |
382 | dataUris = new Uri[numSelectedFiles];
383 |
384 | for (int i = 0; i < numSelectedFiles; i++) {
385 | dataUris[i] = intent.getClipData().getItemAt(i).getUri();
386 | }
387 | }
388 | }
389 | }
390 | } catch (Exception ignored) {
391 | }
392 |
393 | mFileUploadCallbackSecond.onReceiveValue(dataUris);
394 | mFileUploadCallbackSecond = null;
395 | }
396 | }
397 | } else {
398 | if (mFileUploadCallbackFirst != null) {
399 | mFileUploadCallbackFirst.onReceiveValue(null);
400 | mFileUploadCallbackFirst = null;
401 | } else if (mFileUploadCallbackSecond != null) {
402 | mFileUploadCallbackSecond.onReceiveValue(null);
403 | mFileUploadCallbackSecond = null;
404 | }
405 | }
406 | }
407 | }
408 |
409 | /**
410 | * Adds an additional HTTP header that will be sent along with every HTTP `GET` request
411 | *
412 | * This does only affect the main requests, not the requests to included resources (e.g. images)
413 | *
414 | * If you later want to delete an HTTP header that was previously added this way, call `removeHttpHeader()`
415 | *
416 | * The `WebView` implementation may in some cases overwrite headers that you set or unset
417 | *
418 | * @param name the name of the HTTP header to add
419 | * @param value the value of the HTTP header to send
420 | */
421 | public void addHttpHeader(final String name, final String value) {
422 | mHttpHeaders.put(name, value);
423 | }
424 |
425 | /**
426 | * Removes one of the HTTP headers that have previously been added via `addHttpHeader()`
427 | *
428 | * If you want to unset a pre-defined header, set it to an empty string with `addHttpHeader()` instead
429 | *
430 | * The `WebView` implementation may in some cases overwrite headers that you set or unset
431 | *
432 | * @param name the name of the HTTP header to remove
433 | */
434 | public void removeHttpHeader(final String name) {
435 | mHttpHeaders.remove(name);
436 | }
437 |
438 | public void addPermittedHostname(String hostname) {
439 | mPermittedHostnames.add(hostname);
440 | }
441 |
442 | public void addPermittedHostnames(Collection extends String> collection) {
443 | mPermittedHostnames.addAll(collection);
444 | }
445 |
446 | public List getPermittedHostnames() {
447 | return mPermittedHostnames;
448 | }
449 |
450 | public void removePermittedHostname(String hostname) {
451 | mPermittedHostnames.remove(hostname);
452 | }
453 |
454 | public void clearPermittedHostnames() {
455 | mPermittedHostnames.clear();
456 | }
457 |
458 | public boolean onBackPressed() {
459 | if (canGoBack()) {
460 | goBack();
461 | return false;
462 | } else {
463 | return true;
464 | }
465 | }
466 |
467 | public void setCookiesEnabled(final boolean enabled) {
468 | CookieManager.getInstance().setAcceptCookie(enabled);
469 | }
470 |
471 | public void setThirdPartyCookiesEnabled(final boolean enabled) {
472 | if (Build.VERSION.SDK_INT >= 21) {
473 | CookieManager.getInstance().setAcceptThirdPartyCookies(this, enabled);
474 | }
475 | }
476 |
477 | public void setMixedContentAllowed(final boolean allowed) {
478 | setMixedContentAllowed(getSettings(), allowed);
479 | }
480 |
481 | protected void setMixedContentAllowed(final WebSettings webSettings, final boolean allowed) {
482 | if (Build.VERSION.SDK_INT >= 21) {
483 | webSettings.setMixedContentMode(allowed ? WebSettings.MIXED_CONTENT_ALWAYS_ALLOW : WebSettings.MIXED_CONTENT_NEVER_ALLOW);
484 | }
485 | }
486 |
487 | public void setDesktopMode(final boolean enabled) {
488 | final WebSettings webSettings = getSettings();
489 |
490 | final String newUserAgent;
491 | if (enabled) {
492 | newUserAgent = webSettings.getUserAgentString().replace("Mobile", "eliboM").replace("Android", "diordnA");
493 | } else {
494 | newUserAgent = webSettings.getUserAgentString().replace("eliboM", "Mobile").replace("diordnA", "Android");
495 | }
496 |
497 | webSettings.setUserAgentString(newUserAgent);
498 | webSettings.setUseWideViewPort(enabled);
499 | webSettings.setLoadWithOverviewMode(enabled);
500 | webSettings.setSupportZoom(enabled);
501 | webSettings.setBuiltInZoomControls(enabled);
502 | }
503 |
504 | protected void init(Context context) {
505 | // in IDE's preview mode
506 | if (isInEditMode()) {
507 | // do not run the code from this method
508 | return;
509 | }
510 |
511 | if (context instanceof Activity) {
512 | mActivity = new WeakReference<>((Activity) context);
513 | }
514 |
515 | mLanguageIso3 = getLanguageIso3();
516 |
517 | setFocusable(true);
518 | setFocusableInTouchMode(true);
519 |
520 | setSaveEnabled(true);
521 |
522 |
523 | final WebSettings webSettings = getSettings();
524 | webSettings.setAllowFileAccess(false);
525 | setAllowAccessFromFileUrls(webSettings);
526 | webSettings.setBuiltInZoomControls(false);
527 | webSettings.setJavaScriptEnabled(true);
528 | webSettings.setDomStorageEnabled(true);
529 |
530 | if (Build.VERSION.SDK_INT < 18) {
531 | webSettings.setRenderPriority(WebSettings.RenderPriority.HIGH);
532 | }
533 | webSettings.setDatabaseEnabled(true);
534 |
535 | if (Build.VERSION.SDK_INT < 19) {
536 | final String filesDir = context.getFilesDir().getPath();
537 | final String databaseDir = filesDir.substring(0, filesDir.lastIndexOf("/")) + DATABASES_SUB_FOLDER;
538 | webSettings.setDatabasePath(databaseDir);
539 | }
540 |
541 | if (Build.VERSION.SDK_INT >= 26) {
542 | // this should make render a bit faster
543 | this.setRendererPriorityPolicy(RENDERER_PRIORITY_IMPORTANT, false);
544 | }
545 |
546 | if (Build.VERSION.SDK_INT >= 21) {
547 | webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
548 | }
549 |
550 | setThirdPartyCookiesEnabled(true);
551 |
552 | super.setWebViewClient(new WebViewClient() {
553 |
554 | @Override
555 | public void onPageStarted(WebView view, String url, Bitmap favicon) {
556 | if (!hasError()) {
557 | if (mListener != null) {
558 | mListener.onPageStarted(url, favicon);
559 | }
560 | }
561 |
562 | if (mCustomWebViewClient != null) {
563 | mCustomWebViewClient.onPageStarted(view, url, favicon);
564 | }
565 | }
566 |
567 | @Override
568 | public void onPageFinished(WebView view, String url) {
569 | if (!hasError()) {
570 | if (mListener != null) {
571 | mListener.onPageFinished(url);
572 | }
573 | }
574 |
575 | if (mCustomWebViewClient != null) {
576 | mCustomWebViewClient.onPageFinished(view, url);
577 | }
578 | }
579 |
580 | @Override
581 | public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
582 | setLastError();
583 |
584 | if (mListener != null) {
585 | mListener.onPageError(errorCode, description, failingUrl);
586 | }
587 |
588 | if (mCustomWebViewClient != null) {
589 | mCustomWebViewClient.onReceivedError(view, errorCode, description, failingUrl);
590 | }
591 | }
592 |
593 | @Override
594 | public boolean shouldOverrideUrlLoading(final WebView view, final String url) {
595 | if (!isPermittedUrl(url)) {
596 | // if a listener is available
597 | if (mListener != null) {
598 | // inform the listener about the request
599 | mListener.onExternalPageRequest(url);
600 | }
601 |
602 | // cancel the original request
603 | return true;
604 | }
605 |
606 | // if there is a user-specified handler available
607 | if (mCustomWebViewClient != null) {
608 | // if the user-specified handler asks to override the request
609 | if (mCustomWebViewClient.shouldOverrideUrlLoading(view, url)) {
610 | // cancel the original request
611 | return true;
612 | }
613 | }
614 |
615 | final Uri uri = Uri.parse(url);
616 | final String scheme = uri.getScheme();
617 |
618 | if (scheme != null) {
619 | final Intent externalSchemeIntent;
620 |
621 | switch (scheme) {
622 | case "tel":
623 | externalSchemeIntent = new Intent(Intent.ACTION_DIAL, uri);
624 | break;
625 | case "sms":
626 | case "mailto":
627 | externalSchemeIntent = new Intent(Intent.ACTION_SENDTO, uri);
628 | break;
629 | case "whatsapp":
630 | externalSchemeIntent = new Intent(Intent.ACTION_SENDTO, uri);
631 | externalSchemeIntent.setPackage("com.whatsapp");
632 | break;
633 | default:
634 | externalSchemeIntent = null;
635 | break;
636 | }
637 |
638 | if (externalSchemeIntent != null) {
639 | externalSchemeIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
640 |
641 | try {
642 | if (mActivity != null && mActivity.get() != null) {
643 | mActivity.get().startActivity(externalSchemeIntent);
644 | } else {
645 | getContext().startActivity(externalSchemeIntent);
646 | }
647 | } catch (ActivityNotFoundException ignored) {
648 | }
649 |
650 | // cancel the original request
651 | return true;
652 | }
653 | }
654 |
655 | // route the request through the custom URL loading method
656 | view.loadUrl(url);
657 |
658 | // cancel the original request
659 | return true;
660 | }
661 |
662 | @Override
663 | public void onLoadResource(WebView view, String url) {
664 | if (mCustomWebViewClient != null) {
665 | mCustomWebViewClient.onLoadResource(view, url);
666 | } else {
667 | super.onLoadResource(view, url);
668 | }
669 | }
670 |
671 | public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
672 | if (mCustomWebViewClient != null) {
673 | return mCustomWebViewClient.shouldInterceptRequest(view, url);
674 | } else {
675 | return super.shouldInterceptRequest(view, url);
676 | }
677 | }
678 |
679 | public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
680 | if (Build.VERSION.SDK_INT >= 21) {
681 | if (mCustomWebViewClient != null) {
682 | return mCustomWebViewClient.shouldInterceptRequest(view, request);
683 | } else {
684 | return super.shouldInterceptRequest(view, request);
685 | }
686 | } else {
687 | return null;
688 | }
689 | }
690 |
691 | @Override
692 | public void onFormResubmission(WebView view, Message dontResend, Message resend) {
693 | if (mCustomWebViewClient != null) {
694 | mCustomWebViewClient.onFormResubmission(view, dontResend, resend);
695 | } else {
696 | super.onFormResubmission(view, dontResend, resend);
697 | }
698 | }
699 |
700 | @Override
701 | public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
702 | if (mCustomWebViewClient != null) {
703 | mCustomWebViewClient.doUpdateVisitedHistory(view, url, isReload);
704 | } else {
705 | super.doUpdateVisitedHistory(view, url, isReload);
706 | }
707 | }
708 |
709 | @Override
710 | public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
711 | if (mCustomWebViewClient != null) {
712 | mCustomWebViewClient.onReceivedSslError(view, handler, error);
713 | } else {
714 | super.onReceivedSslError(view, handler, error);
715 | }
716 | }
717 |
718 | public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
719 | if (Build.VERSION.SDK_INT >= 21) {
720 | if (mCustomWebViewClient != null) {
721 | mCustomWebViewClient.onReceivedClientCertRequest(view, request);
722 | } else {
723 | super.onReceivedClientCertRequest(view, request);
724 | }
725 | }
726 | }
727 |
728 | @Override
729 | public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) {
730 | if (mCustomWebViewClient != null) {
731 | mCustomWebViewClient.onReceivedHttpAuthRequest(view, handler, host, realm);
732 | } else {
733 | super.onReceivedHttpAuthRequest(view, handler, host, realm);
734 | }
735 | }
736 |
737 | @Override
738 | public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
739 | if (mCustomWebViewClient != null) {
740 | return mCustomWebViewClient.shouldOverrideKeyEvent(view, event);
741 | } else {
742 | return super.shouldOverrideKeyEvent(view, event);
743 | }
744 | }
745 |
746 | @Override
747 | public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
748 | if (mCustomWebViewClient != null) {
749 | mCustomWebViewClient.onUnhandledKeyEvent(view, event);
750 | } else {
751 | super.onUnhandledKeyEvent(view, event);
752 | }
753 | }
754 |
755 | @Override
756 | public void onScaleChanged(WebView view, float oldScale, float newScale) {
757 | if (mCustomWebViewClient != null) {
758 | mCustomWebViewClient.onScaleChanged(view, oldScale, newScale);
759 | } else {
760 | super.onScaleChanged(view, oldScale, newScale);
761 | }
762 | }
763 |
764 | public void onReceivedLoginRequest(WebView view, String realm, String account, String args) {
765 | if (mCustomWebViewClient != null) {
766 | mCustomWebViewClient.onReceivedLoginRequest(view, realm, account, args);
767 | } else {
768 | super.onReceivedLoginRequest(view, realm, account, args);
769 | }
770 | }
771 |
772 | });
773 |
774 | super.setWebChromeClient(new WebChromeClient() {
775 |
776 | // file upload callback (Android 5.0 (API level 21) -- current) (public method)
777 | public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
778 | if (Build.VERSION.SDK_INT >= 21) {
779 | final boolean allowMultiple = fileChooserParams.getMode() == FileChooserParams.MODE_OPEN_MULTIPLE;
780 |
781 | openFileInput(filePathCallback, allowMultiple);
782 |
783 | return true;
784 | } else {
785 | return false;
786 | }
787 | }
788 |
789 | @Override
790 | public void onProgressChanged(WebView view, int newProgress) {
791 | if (mCustomWebChromeClient != null) {
792 | mCustomWebChromeClient.onProgressChanged(view, newProgress);
793 | } else {
794 | super.onProgressChanged(view, newProgress);
795 | }
796 | }
797 |
798 | @Override
799 | public void onReceivedTitle(WebView view, String title) {
800 | if (mCustomWebChromeClient != null) {
801 | mCustomWebChromeClient.onReceivedTitle(view, title);
802 | } else {
803 | super.onReceivedTitle(view, title);
804 | }
805 | }
806 |
807 | @Override
808 | public void onReceivedIcon(WebView view, Bitmap icon) {
809 | if (mCustomWebChromeClient != null) {
810 | mCustomWebChromeClient.onReceivedIcon(view, icon);
811 | } else {
812 | super.onReceivedIcon(view, icon);
813 | }
814 | }
815 |
816 | @Override
817 | public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) {
818 | if (mCustomWebChromeClient != null) {
819 | mCustomWebChromeClient.onReceivedTouchIconUrl(view, url, precomposed);
820 | } else {
821 | super.onReceivedTouchIconUrl(view, url, precomposed);
822 | }
823 | }
824 |
825 | @Override
826 | public void onShowCustomView(View view, CustomViewCallback callback) {
827 | if (mCustomWebChromeClient != null) {
828 | mCustomWebChromeClient.onShowCustomView(view, callback);
829 | } else {
830 | super.onShowCustomView(view, callback);
831 | }
832 | }
833 |
834 | public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) {
835 | if (Build.VERSION.SDK_INT >= 14) {
836 | if (mCustomWebChromeClient != null) {
837 | mCustomWebChromeClient.onShowCustomView(view, requestedOrientation, callback);
838 | } else {
839 | super.onShowCustomView(view, requestedOrientation, callback);
840 | }
841 | }
842 | }
843 |
844 | @Override
845 | public void onHideCustomView() {
846 | if (mCustomWebChromeClient != null) {
847 | mCustomWebChromeClient.onHideCustomView();
848 | } else {
849 | super.onHideCustomView();
850 | }
851 | }
852 |
853 | @Override
854 | public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
855 | if (mCustomWebChromeClient != null) {
856 | return mCustomWebChromeClient.onCreateWindow(view, isDialog, isUserGesture, resultMsg);
857 | } else {
858 | return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg);
859 | }
860 | }
861 |
862 | @Override
863 | public void onRequestFocus(WebView view) {
864 | if (mCustomWebChromeClient != null) {
865 | mCustomWebChromeClient.onRequestFocus(view);
866 | } else {
867 | super.onRequestFocus(view);
868 | }
869 | }
870 |
871 | @Override
872 | public void onCloseWindow(WebView window) {
873 | if (mCustomWebChromeClient != null) {
874 | mCustomWebChromeClient.onCloseWindow(window);
875 | } else {
876 | super.onCloseWindow(window);
877 | }
878 | }
879 |
880 | @Override
881 | public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
882 | if (mCustomWebChromeClient != null) {
883 | return mCustomWebChromeClient.onJsAlert(view, url, message, result);
884 | } else {
885 | return super.onJsAlert(view, url, message, result);
886 | }
887 | }
888 |
889 | @Override
890 | public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
891 | if (mCustomWebChromeClient != null) {
892 | return mCustomWebChromeClient.onJsConfirm(view, url, message, result);
893 | } else {
894 | return super.onJsConfirm(view, url, message, result);
895 | }
896 | }
897 |
898 | @Override
899 | public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
900 | if (mCustomWebChromeClient != null) {
901 | return mCustomWebChromeClient.onJsPrompt(view, url, message, defaultValue, result);
902 | } else {
903 | return super.onJsPrompt(view, url, message, defaultValue, result);
904 | }
905 | }
906 |
907 | @Override
908 | public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
909 | if (mCustomWebChromeClient != null) {
910 | return mCustomWebChromeClient.onJsBeforeUnload(view, url, message, result);
911 | } else {
912 | return super.onJsBeforeUnload(view, url, message, result);
913 | }
914 | }
915 |
916 | @Override
917 | public void onGeolocationPermissionsShowPrompt(String origin, Callback callback) {
918 | if (mGeolocationEnabled) {
919 | callback.invoke(origin, true, false);
920 | } else {
921 | if (mCustomWebChromeClient != null) {
922 | mCustomWebChromeClient.onGeolocationPermissionsShowPrompt(origin, callback);
923 | } else {
924 | super.onGeolocationPermissionsShowPrompt(origin, callback);
925 | }
926 | }
927 | }
928 |
929 | @Override
930 | public void onGeolocationPermissionsHidePrompt() {
931 | if (mCustomWebChromeClient != null) {
932 | mCustomWebChromeClient.onGeolocationPermissionsHidePrompt();
933 | } else {
934 | super.onGeolocationPermissionsHidePrompt();
935 | }
936 | }
937 |
938 | public void onPermissionRequest(PermissionRequest request) {
939 | if (Build.VERSION.SDK_INT >= 21) {
940 | if (mCustomWebChromeClient != null) {
941 | mCustomWebChromeClient.onPermissionRequest(request);
942 | } else {
943 | super.onPermissionRequest(request);
944 | }
945 | }
946 | }
947 |
948 | public void onPermissionRequestCanceled(PermissionRequest request) {
949 | if (Build.VERSION.SDK_INT >= 21) {
950 | if (mCustomWebChromeClient != null) {
951 | mCustomWebChromeClient.onPermissionRequestCanceled(request);
952 | } else {
953 | super.onPermissionRequestCanceled(request);
954 | }
955 | }
956 | }
957 |
958 | @Override
959 | public boolean onJsTimeout() {
960 | if (mCustomWebChromeClient != null) {
961 | return mCustomWebChromeClient.onJsTimeout();
962 | } else {
963 | return super.onJsTimeout();
964 | }
965 | }
966 |
967 | @Override
968 | public void onConsoleMessage(String message, int lineNumber, String sourceID) {
969 | if (mCustomWebChromeClient != null) {
970 | mCustomWebChromeClient.onConsoleMessage(message, lineNumber, sourceID);
971 | } else {
972 | super.onConsoleMessage(message, lineNumber, sourceID);
973 | }
974 | }
975 |
976 | @Override
977 | public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
978 | if (mCustomWebChromeClient != null) {
979 | return mCustomWebChromeClient.onConsoleMessage(consoleMessage);
980 | } else {
981 | return super.onConsoleMessage(consoleMessage);
982 | }
983 | }
984 |
985 | @Override
986 | public Bitmap getDefaultVideoPoster() {
987 | if (mCustomWebChromeClient != null) {
988 | return mCustomWebChromeClient.getDefaultVideoPoster();
989 | } else {
990 | return super.getDefaultVideoPoster();
991 | }
992 | }
993 |
994 | @Override
995 | public View getVideoLoadingProgressView() {
996 | if (mCustomWebChromeClient != null) {
997 | return mCustomWebChromeClient.getVideoLoadingProgressView();
998 | } else {
999 | return super.getVideoLoadingProgressView();
1000 | }
1001 | }
1002 |
1003 | @Override
1004 | public void getVisitedHistory(ValueCallback callback) {
1005 | if (mCustomWebChromeClient != null) {
1006 | mCustomWebChromeClient.getVisitedHistory(callback);
1007 | } else {
1008 | super.getVisitedHistory(callback);
1009 | }
1010 | }
1011 |
1012 | @Override
1013 | public void onExceededDatabaseQuota(String url, String databaseIdentifier, long quota, long estimatedDatabaseSize, long totalQuota, QuotaUpdater quotaUpdater) {
1014 | if (mCustomWebChromeClient != null) {
1015 | mCustomWebChromeClient.onExceededDatabaseQuota(url, databaseIdentifier, quota, estimatedDatabaseSize, totalQuota, quotaUpdater);
1016 | } else {
1017 | super.onExceededDatabaseQuota(url, databaseIdentifier, quota, estimatedDatabaseSize, totalQuota, quotaUpdater);
1018 | }
1019 | }
1020 |
1021 | @Override
1022 | public void onReachedMaxAppCacheSize(long requiredStorage, long quota, QuotaUpdater quotaUpdater) {
1023 | if (mCustomWebChromeClient != null) {
1024 | mCustomWebChromeClient.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater);
1025 | } else {
1026 | super.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater);
1027 | }
1028 | }
1029 |
1030 | });
1031 |
1032 | setDownloadListener((url, userAgent, contentDisposition, mimeType, contentLength) -> {
1033 | final String suggestedFilename = URLUtil.guessFileName(url, contentDisposition, mimeType);
1034 |
1035 | if (mListener != null) {
1036 | mListener.onDownloadRequested(url, suggestedFilename, mimeType, contentLength, contentDisposition, userAgent);
1037 | }
1038 | });
1039 | }
1040 |
1041 | @Override
1042 | public void loadUrl(final String url, Map additionalHttpHeaders) {
1043 | if (additionalHttpHeaders == null) {
1044 | additionalHttpHeaders = mHttpHeaders;
1045 | } else if (mHttpHeaders.size() > 0) {
1046 | additionalHttpHeaders.putAll(mHttpHeaders);
1047 | }
1048 |
1049 | super.loadUrl(url, additionalHttpHeaders);
1050 | }
1051 |
1052 | @Override
1053 | public void loadUrl(final String url) {
1054 | if (mHttpHeaders.size() > 0) {
1055 | super.loadUrl(url, mHttpHeaders);
1056 | } else {
1057 | super.loadUrl(url);
1058 | }
1059 | }
1060 |
1061 | public void loadUrl(String url, final boolean preventCaching) {
1062 | if (preventCaching) {
1063 | url = makeUrlUnique(url);
1064 | }
1065 |
1066 | loadUrl(url);
1067 | }
1068 |
1069 | public void loadUrl(String url, final boolean preventCaching, final Map additionalHttpHeaders) {
1070 | if (preventCaching) {
1071 | url = makeUrlUnique(url);
1072 | }
1073 |
1074 | loadUrl(url, additionalHttpHeaders);
1075 | }
1076 |
1077 | public boolean isPermittedUrl(final String url) {
1078 | // if the permitted hostnames have not been restricted to a specific set
1079 | if (mPermittedHostnames.size() == 0) {
1080 | // all hostnames are allowed
1081 | return true;
1082 | }
1083 |
1084 | final Uri parsedUrl = Uri.parse(url);
1085 |
1086 | // get the hostname of the URL that is to be checked
1087 | final String actualHost = parsedUrl.getHost();
1088 |
1089 | // if the hostname could not be determined, usually because the URL has been invalid
1090 | if (actualHost == null) {
1091 | return false;
1092 | }
1093 |
1094 | // if the host contains invalid characters (e.g. a backslash)
1095 | if (!actualHost.matches("^[a-zA-Z0-9._!~*')(;:&=+$,%\\[\\]-]*$")) {
1096 | // prevent mismatches between interpretations by `Uri` and `WebView`, e.g. for `http://evil.example.com\.good.example.com/`
1097 | return false;
1098 | }
1099 |
1100 | // get the user information from the authority part of the URL that is to be checked
1101 | final String actualUserInformation = parsedUrl.getUserInfo();
1102 |
1103 | // if the user information contains invalid characters (e.g. a backslash)
1104 | if (actualUserInformation != null && !actualUserInformation.matches("^[a-zA-Z0-9._!~*')(;:&=+$,%-]*$")) {
1105 | // prevent mismatches between interpretations by `Uri` and `WebView`, e.g. for `http://evil.example.com\@good.example.com/`
1106 | return false;
1107 | }
1108 |
1109 | // for every hostname in the set of permitted hosts
1110 | for (String expectedHost : mPermittedHostnames) {
1111 | // if the two hostnames match or if the actual host is a subdomain of the expected host
1112 | if (actualHost.equals(expectedHost) || actualHost.endsWith("." + expectedHost)) {
1113 | // the actual hostname of the URL to be checked is allowed
1114 | return true;
1115 | }
1116 | }
1117 |
1118 | // the actual hostname of the URL to be checked is not allowed since there were no matches
1119 | return false;
1120 | }
1121 |
1122 | /**
1123 | * @deprecated use `isPermittedUrl` instead
1124 | */
1125 | protected boolean isHostnameAllowed(final String url) {
1126 | return isPermittedUrl(url);
1127 | }
1128 |
1129 | protected void setLastError() {
1130 | mLastError = System.currentTimeMillis();
1131 | }
1132 |
1133 | protected boolean hasError() {
1134 | return (mLastError + 500) >= System.currentTimeMillis();
1135 | }
1136 |
1137 | /**
1138 | * Provides localizations for the 25 most widely spoken languages that have a ISO 639-2/T code
1139 | *
1140 | * @return the label for the file upload prompts as a string
1141 | */
1142 | protected String getFileUploadPromptLabel() {
1143 | try {
1144 | switch (mLanguageIso3) {
1145 | case "zho":
1146 | return decodeBase64("6YCJ5oup5LiA5Liq5paH5Lu2");
1147 | case "spa":
1148 | return decodeBase64("RWxpamEgdW4gYXJjaGl2bw==");
1149 | case "hin":
1150 | return decodeBase64("4KSP4KSVIOCkq+CkvOCkvuCkh+CksiDgpJrgpYHgpKjgpYfgpII=");
1151 | case "ben":
1152 | return decodeBase64("4KaP4KaV4Kaf4Ka/IOCmq+CmvuCmh+CmsiDgpqjgpr/gprDgp43gpqzgpr7gpprgpqg=");
1153 | case "ara":
1154 | return decodeBase64("2KfYrtiq2YrYp9ixINmF2YTZgSDZiNin2K3Yrw==");
1155 | case "por":
1156 | return decodeBase64("RXNjb2xoYSB1bSBhcnF1aXZv");
1157 | case "rus":
1158 | return decodeBase64("0JLRi9Cx0LXRgNC40YLQtSDQvtC00LjQvSDRhNCw0LnQuw==");
1159 | case "jpn":
1160 | return decodeBase64("MeODleOCoeOCpOODq+OCkumBuOaKnuOBl+OBpuOBj+OBoOOBleOBhA==");
1161 | case "pan":
1162 | return decodeBase64("4KiH4Kmx4KiVIOCoq+CovuCoh+CosiDgqJrgqYHgqKPgqYs=");
1163 | case "deu":
1164 | return decodeBase64("V8OkaGxlIGVpbmUgRGF0ZWk=");
1165 | case "jav":
1166 | return decodeBase64("UGlsaWggc2lqaSBiZXJrYXM=");
1167 | case "msa":
1168 | return decodeBase64("UGlsaWggc2F0dSBmYWls");
1169 | case "tel":
1170 | return decodeBase64("4LCS4LCVIOCwq+CxhuCxluCwsuCxjeCwqOCxgSDgsI7gsILgsJrgsYHgsJXgsYvgsILgsKHgsL8=");
1171 | case "vie":
1172 | return decodeBase64("Q2jhu41uIG3hu5l0IHThuq1wIHRpbg==");
1173 | case "kor":
1174 | return decodeBase64("7ZWY64KY7J2YIO2MjOydvOydhCDshKDtg50=");
1175 | case "fra":
1176 | return decodeBase64("Q2hvaXNpc3NleiB1biBmaWNoaWVy");
1177 | case "mar":
1178 | return decodeBase64("4KSr4KS+4KSH4KSyIOCkqOCkv+CkteCkoeCkvg==");
1179 | case "tam":
1180 | return decodeBase64("4K6S4K6w4K+BIOCuleCvh+CuvuCuquCvjeCuquCviCDgrqTgr4fgrrDgr43grrXgr4E=");
1181 | case "urd":
1182 | return decodeBase64("2KfbjNqpINmB2KfYptmEINmF24zauiDYs9uSINin2YbYqtiu2KfYqCDaqdix24zaug==");
1183 | case "fas":
1184 | return decodeBase64("2LHYpyDYp9mG2KrYrtin2Kgg2qnZhtuM2K8g24zaqSDZgdin24zZhA==");
1185 | case "tur":
1186 | return decodeBase64("QmlyIGRvc3lhIHNlw6dpbg==");
1187 | case "ita":
1188 | return decodeBase64("U2NlZ2xpIHVuIGZpbGU=");
1189 | case "tha":
1190 | return decodeBase64("4LmA4Lil4Li34Lit4LiB4LmE4Lif4Lil4LmM4Lir4LiZ4Li24LmI4LiH");
1191 | case "guj":
1192 | return decodeBase64("4KqP4KqVIOCqq+CqvuCqh+CqsuCqqOCrhyDgqqrgqrjgqoLgqqY=");
1193 | }
1194 | } catch (Exception ignored) {
1195 | }
1196 |
1197 | // return English translation by default
1198 | return "Choose a file";
1199 | }
1200 |
1201 | protected void openFileInput(final ValueCallback fileUploadCallbackSecond, final boolean allowMultiple) {
1202 | if (mFileUploadCallbackFirst != null) {
1203 | mFileUploadCallbackFirst.onReceiveValue(null);
1204 | }
1205 | mFileUploadCallbackFirst = null;
1206 |
1207 | if (mFileUploadCallbackSecond != null) {
1208 | mFileUploadCallbackSecond.onReceiveValue(null);
1209 | }
1210 | mFileUploadCallbackSecond = fileUploadCallbackSecond;
1211 |
1212 | Intent i = new Intent(Intent.ACTION_GET_CONTENT);
1213 | i.addCategory(Intent.CATEGORY_OPENABLE);
1214 |
1215 | if (allowMultiple) {
1216 | if (Build.VERSION.SDK_INT >= 18) {
1217 | i.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
1218 | }
1219 | }
1220 |
1221 | i.setType(mUploadableFileTypes);
1222 |
1223 | if (mFragment != null && mFragment.get() != null) {
1224 | mFragment.get().startActivityForResult(Intent.createChooser(i, getFileUploadPromptLabel()), mRequestCodeFilePicker);
1225 | } else if (mActivity != null && mActivity.get() != null) {
1226 | mActivity.get().startActivityForResult(Intent.createChooser(i, getFileUploadPromptLabel()), mRequestCodeFilePicker);
1227 | }
1228 | }
1229 |
1230 | public interface Listener {
1231 | void onPageStarted(String url, Bitmap favicon);
1232 |
1233 | void onPageFinished(String url);
1234 |
1235 | void onPageError(int errorCode, String description, String failingUrl);
1236 |
1237 | void onDownloadRequested(String url, String suggestedFilename, String mimeType, long contentLength, String contentDisposition, String userAgent);
1238 |
1239 | void onExternalPageRequest(String url);
1240 | }
1241 |
1242 | /**
1243 | * Wrapper for methods related to alternative browsers that have their own rendering engines
1244 | */
1245 | public static class Browsers {
1246 |
1247 | /**
1248 | * Package name of an alternative browser that is installed on this device
1249 | */
1250 | private static String mAlternativePackage;
1251 |
1252 | /**
1253 | * Returns whether there is an alternative browser with its own rendering engine currently installed
1254 | *
1255 | * @param context a valid `Context` reference
1256 | * @return whether there is an alternative browser or not
1257 | */
1258 | public static boolean hasAlternative(final Context context) {
1259 | return getAlternative(context) != null;
1260 | }
1261 |
1262 | /**
1263 | * Returns the package name of an alternative browser with its own rendering engine or `null`
1264 | *
1265 | * @param context a valid `Context` reference
1266 | * @return the package name or `null`
1267 | */
1268 | public static String getAlternative(final Context context) {
1269 | if (mAlternativePackage != null) {
1270 | return mAlternativePackage;
1271 | }
1272 |
1273 | final List alternativeBrowsers = Arrays.asList(ALTERNATIVE_BROWSERS);
1274 | final List apps = context.getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA);
1275 |
1276 | for (ApplicationInfo app : apps) {
1277 | if (!app.enabled) {
1278 | continue;
1279 | }
1280 |
1281 | if (alternativeBrowsers.contains(app.packageName)) {
1282 | mAlternativePackage = app.packageName;
1283 |
1284 | return app.packageName;
1285 | }
1286 | }
1287 |
1288 | return null;
1289 | }
1290 |
1291 | /**
1292 | * Opens the given URL in an alternative browser
1293 | *
1294 | * @param context a valid `Activity` reference
1295 | * @param url the URL to open
1296 | */
1297 | public static void openUrl(final Activity context, final String url) {
1298 | openUrl(context, url, false);
1299 | }
1300 |
1301 | /**
1302 | * Opens the given URL in an alternative browser
1303 | *
1304 | * @param context a valid `Activity` reference
1305 | * @param url the URL to open
1306 | * @param withoutTransition whether to switch to the browser `Activity` without a transition
1307 | */
1308 | public static void openUrl(final Activity context, final String url, final boolean withoutTransition) {
1309 | final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
1310 | intent.setPackage(getAlternative(context));
1311 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1312 |
1313 | context.startActivity(intent);
1314 |
1315 | if (withoutTransition) {
1316 | context.overridePendingTransition(0, 0);
1317 | }
1318 | }
1319 |
1320 | }
1321 |
1322 | }
1323 |
--------------------------------------------------------------------------------
/Source/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion '29.0.3'
6 |
7 | defaultConfig {
8 | applicationId "im.delight.android.examples.webview"
9 | minSdkVersion 14
10 | targetSdkVersion 28
11 | }
12 |
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 |
20 | compileOptions {
21 | encoding "UTF-8"
22 | sourceCompatibility JavaVersion.VERSION_1_8
23 | targetCompatibility JavaVersion.VERSION_1_8
24 | }
25 |
26 | lintOptions {
27 | disable 'GoogleAppIndexingWarning'
28 | }
29 |
30 | packagingOptions {
31 | exclude 'META-INF/*'
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation project(':library')
37 | }
38 |
--------------------------------------------------------------------------------
/Source/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Source/sample/src/main/java/im/delight/android/examples/webview/MainActivity.java:
--------------------------------------------------------------------------------
1 | package im.delight.android.examples.webview;
2 |
3 | import android.app.Activity;
4 | import android.content.Intent;
5 | import android.graphics.Bitmap;
6 | import android.os.Bundle;
7 | import android.view.View;
8 | import android.webkit.WebChromeClient;
9 | import android.webkit.WebView;
10 | import android.webkit.WebViewClient;
11 | import android.widget.Toast;
12 |
13 | import im.delight.android.webview.AdvancedWebView;
14 |
15 | public class MainActivity extends Activity implements AdvancedWebView.Listener {
16 |
17 | private static final String TEST_PAGE_URL = "https://www.example.org/";
18 | private AdvancedWebView mWebView;
19 |
20 | @Override
21 | protected void onCreate(Bundle savedInstanceState) {
22 | super.onCreate(savedInstanceState);
23 | setContentView(R.layout.activity_main);
24 |
25 | mWebView = findViewById(R.id.webview);
26 | mWebView.setListener(this, this);
27 | mWebView.setGeolocationEnabled(false);
28 | mWebView.setMixedContentAllowed(false);
29 | mWebView.setCookiesEnabled(true);
30 | mWebView.setThirdPartyCookiesEnabled(true);
31 | mWebView.setWebViewClient(new WebViewClient() {
32 |
33 | @Override
34 | public void onPageFinished(WebView view, String url) {
35 | Toast.makeText(MainActivity.this, "Finished loading", Toast.LENGTH_SHORT).show();
36 | }
37 |
38 | });
39 | mWebView.setWebChromeClient(new WebChromeClient() {
40 |
41 | @Override
42 | public void onReceivedTitle(WebView view, String title) {
43 | super.onReceivedTitle(view, title);
44 | Toast.makeText(MainActivity.this, title, Toast.LENGTH_SHORT).show();
45 | }
46 |
47 | });
48 | mWebView.addHttpHeader("X-Requested-With", "");
49 | mWebView.loadUrl(TEST_PAGE_URL);
50 | }
51 |
52 | @Override
53 | protected void onResume() {
54 | super.onResume();
55 | mWebView.onResume();
56 | // ...
57 | }
58 |
59 | @Override
60 | protected void onPause() {
61 | mWebView.onPause();
62 | // ...
63 | super.onPause();
64 | }
65 |
66 | @Override
67 | protected void onDestroy() {
68 | mWebView.onDestroy();
69 | // ...
70 | super.onDestroy();
71 | }
72 |
73 | @Override
74 | protected void onActivityResult(int requestCode, int resultCode, Intent intent) {
75 | super.onActivityResult(requestCode, resultCode, intent);
76 | mWebView.onActivityResult(requestCode, resultCode, intent);
77 | // ...
78 | }
79 |
80 | @Override
81 | public void onBackPressed() {
82 | if (!mWebView.onBackPressed()) {
83 | return;
84 | }
85 | // ...
86 | super.onBackPressed();
87 | }
88 |
89 | @Override
90 | public void onPageStarted(String url, Bitmap favicon) {
91 | mWebView.setVisibility(View.INVISIBLE);
92 | }
93 |
94 | @Override
95 | public void onPageFinished(String url) {
96 | mWebView.setVisibility(View.VISIBLE);
97 | }
98 |
99 | @Override
100 | public void onPageError(int errorCode, String description, String failingUrl) {
101 | Toast.makeText(MainActivity.this, "onPageError(errorCode = " + errorCode + ", description = " + description + ", failingUrl = " + failingUrl + ")", Toast.LENGTH_SHORT).show();
102 | }
103 |
104 | @Override
105 | public void onDownloadRequested(String url, String suggestedFilename, String mimeType, long contentLength, String contentDisposition, String userAgent) {
106 | Toast.makeText(MainActivity.this, "onDownloadRequested(url = " + url + ", suggestedFilename = " + suggestedFilename + ", mimeType = " + mimeType + ", contentLength = " + contentLength + ", contentDisposition = " + contentDisposition + ", userAgent = " + userAgent + ")", Toast.LENGTH_LONG).show();
107 |
108 | /*if (AdvancedWebView.handleDownload(this, url, suggestedFilename)) {
109 | // download successfully handled
110 | }
111 | else {
112 | // download couldn't be handled because user has disabled download manager app on the device
113 | }*/
114 | }
115 |
116 | @Override
117 | public void onExternalPageRequest(String url) {
118 | Toast.makeText(MainActivity.this, "onExternalPageRequest(url = " + url + ")", Toast.LENGTH_SHORT).show();
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/Source/sample/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TacoTheDank/Android-AdvancedWebView/e461b74d34d6ef099b27a35d38ce5c9058e37d94/Source/sample/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Source/sample/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TacoTheDank/Android-AdvancedWebView/e461b74d34d6ef099b27a35d38ce5c9058e37d94/Source/sample/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Source/sample/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TacoTheDank/Android-AdvancedWebView/e461b74d34d6ef099b27a35d38ce5c9058e37d94/Source/sample/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/Source/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/Source/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Android-AdvancedWebView-Test
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Source/sample/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/Source/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':library'
2 | include ':sample'
3 |
--------------------------------------------------------------------------------