/* * Copyright 2014 A.C.R. Development */ package acr.browser.lightning; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.net.URISyntaxException; import android.annotation.SuppressLint; import android.app.Activity; import android.app.AlertDialog; import android.content.ActivityNotFoundException; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.ColorMatrix; import android.graphics.ColorMatrixColorFilter; import android.graphics.Paint; import android.net.MailTo; import android.net.Uri; import android.net.http.SslError; import android.os.Build; import android.os.Message; import android.text.InputType; import android.text.method.PasswordTransformationMethod; import android.util.Log; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; import android.webkit.CookieManager; import android.webkit.GeolocationPermissions; import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; import android.webkit.ValueCallback; import android.webkit.WebChromeClient; import android.webkit.WebResourceRequest; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebSettings.LayoutAlgorithm; import android.webkit.WebSettings.PluginState; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.EditText; import android.widget.LinearLayout; public class LightningView { private Title mTitle; private WebView mWebView; private BrowserController mBrowserController; private GestureDetector mGestureDetector; private Activity mActivity; private WebSettings mSettings; private static String mHomepage; private static String mDefaultUserAgent; private static Bitmap mWebpageBitmap; private static PreferenceManager mPreferences; private AdBlock mAdBlock; private IntentUtils mIntentUtils; private final Paint mPaint = new Paint(); private boolean isForegroundTab; private boolean mTextReflow = false; private boolean mInvertPage = false; private static final int API = android.os.Build.VERSION.SDK_INT; private static final int SCROLL_UP_THRESHOLD = Utils.convertDpToPixels(10); private static final int SCROLL_DOWN_THRESHOLD = Utils.convertDpToPixels(100); private static final float[] mNegativeColorArray = { -1.0f, 0, 0, 0, 255, // red 0, -1.0f, 0, 0, 255, // green 0, 0, -1.0f, 0, 255, // blue 0, 0, 0, 1.0f, 0 // alpha }; @SuppressWarnings("deprecation") @SuppressLint("NewApi") public LightningView(Activity activity, String url, boolean darkTheme) { mActivity = activity; mWebView = new WebView(activity); mTitle = new Title(activity, darkTheme); mAdBlock = AdBlock.getInstance(activity.getApplicationContext()); mWebpageBitmap = Utils.getWebpageBitmap(activity.getResources(), darkTheme); try { mBrowserController = (BrowserController) activity; } catch (ClassCastException e) { throw new ClassCastException(activity + " must implement BrowserController"); } mIntentUtils = new IntentUtils(mBrowserController); mWebView.setDrawingCacheBackgroundColor(0x00000000); mWebView.setFocusableInTouchMode(true); mWebView.setFocusable(true); mWebView.setAnimationCacheEnabled(false); mWebView.setDrawingCacheEnabled(false); mWebView.setWillNotCacheDrawing(true); mWebView.setAlwaysDrawnWithCacheEnabled(false); mWebView.setBackgroundColor(activity.getResources().getColor(android.R.color.white)); if (API > 15) { mWebView.setBackground(null); mWebView.getRootView().setBackground(null); } else if (mWebView.getRootView() != null) { mWebView.getRootView().setBackgroundDrawable(null); } mWebView.setScrollbarFadingEnabled(true); mWebView.setSaveEnabled(true); mWebView.setWebChromeClient(new LightningChromeClient(activity)); mWebView.setWebViewClient(new LightningWebClient(activity)); mWebView.setDownloadListener(new LightningDownloadListener(activity)); mGestureDetector = new GestureDetector(activity, new CustomGestureListener()); mWebView.setOnTouchListener(new TouchListener()); mDefaultUserAgent = mWebView.getSettings().getUserAgentString(); mSettings = mWebView.getSettings(); initializeSettings(mWebView.getSettings(), activity); initializePreferences(activity); if (url != null) { if (!url.trim().isEmpty()) { mWebView.loadUrl(url); } else { // don't load anything, the user is looking for a blank tab } } else { if (mHomepage.startsWith("about:home")) { mWebView.loadUrl(getHomepage()); } else if (mHomepage.startsWith("about:bookmarks")) { mBrowserController.openBookmarkPage(mWebView); } else { mWebView.loadUrl(mHomepage); } } } public String getHomepage() { StringBuilder homepageBuilder = new StringBuilder(); homepageBuilder.append(HomepageVariables.HEAD); String icon; String searchUrl; switch (mPreferences.getSearchChoice()) { case 0: // CUSTOM SEARCH icon = "file:///android_asset/lightning.png"; searchUrl = mPreferences.getSearchUrl(); break; case 1: // GOOGLE_SEARCH; icon = "file:///android_asset/google.png"; // "https://www.google.com/images/srpr/logo11w.png"; searchUrl = Constants.GOOGLE_SEARCH; break; case 2: // ANDROID SEARCH; icon = "file:///android_asset/ask.png"; searchUrl = Constants.ASK_SEARCH; break; case 3: // BING_SEARCH; icon = "file:///android_asset/bing.png"; // "http://upload.wikimedia.org/wikipedia/commons/thumb/b/b1/Bing_logo_%282013%29.svg/500px-Bing_logo_%282013%29.svg.png"; searchUrl = Constants.BING_SEARCH; break; case 4: // YAHOO_SEARCH; icon = "file:///android_asset/yahoo.png"; // "http://upload.wikimedia.org/wikipedia/commons/thumb/2/24/Yahoo%21_logo.svg/799px-Yahoo%21_logo.svg.png"; searchUrl = Constants.YAHOO_SEARCH; break; case 5: // STARTPAGE_SEARCH; icon = "file:///android_asset/startpage.png"; // "https://startpage.com/graphics/startp_logo.gif"; searchUrl = Constants.STARTPAGE_SEARCH; break; case 6: // STARTPAGE_MOBILE icon = "file:///android_asset/startpage.png"; // "https://startpage.com/graphics/startp_logo.gif"; searchUrl = Constants.STARTPAGE_MOBILE_SEARCH; case 7: // DUCK_SEARCH; icon = "file:///android_asset/duckduckgo.png"; // "https://duckduckgo.com/assets/logo_homepage.normal.v101.png"; searchUrl = Constants.DUCK_SEARCH; break; case 8: // DUCK_LITE_SEARCH; icon = "file:///android_asset/duckduckgo.png"; // "https://duckduckgo.com/assets/logo_homepage.normal.v101.png"; searchUrl = Constants.DUCK_LITE_SEARCH; break; case 9: // BAIDU_SEARCH; icon = "file:///android_asset/baidu.png"; // "http://www.baidu.com/img/bdlogo.gif"; searchUrl = Constants.BAIDU_SEARCH; break; case 10: // YANDEX_SEARCH; icon = "file:///android_asset/yandex.png"; // "http://upload.wikimedia.org/wikipedia/commons/thumb/9/91/Yandex.svg/600px-Yandex.svg.png"; searchUrl = Constants.YANDEX_SEARCH; break; default: // DEFAULT GOOGLE_SEARCH; icon = "file:///android_asset/google.png"; searchUrl = Constants.GOOGLE_SEARCH; break; } homepageBuilder.append(icon); homepageBuilder.append(HomepageVariables.MIDDLE); homepageBuilder.append(searchUrl); homepageBuilder.append(HomepageVariables.END); File homepage = new File(mActivity.getFilesDir(), "homepage.html"); try { FileWriter hWriter = new FileWriter(homepage, false); hWriter.write(homepageBuilder.toString()); hWriter.close(); } catch (IOException e) { e.printStackTrace(); } return Constants.FILE + homepage; } @SuppressWarnings("deprecation") @SuppressLint({ "NewApi", "SetJavaScriptEnabled" }) public synchronized void initializePreferences(Context context) { mPreferences = PreferenceManager.getInstance(); mHomepage = mPreferences.getHomepage(); mAdBlock.updatePreference(); if (mSettings == null && mWebView != null) { mSettings = mWebView.getSettings(); } else if (mSettings == null) { return; } setColorMode(mPreferences.getRenderingMode()); if (!mBrowserController.isIncognito()) { mSettings.setGeolocationEnabled(mPreferences.getLocationEnabled()); } else { mSettings.setGeolocationEnabled(false); } if (API < 19) { switch (mPreferences.getFlashSupport()) { case 0: mSettings.setPluginState(PluginState.OFF); break; case 1: mSettings.setPluginState(PluginState.ON_DEMAND); break; case 2: mSettings.setPluginState(PluginState.ON); break; default: break; } } switch (mPreferences.getUserAgentChoice()) { case 1: if (API > 16) { mSettings.setUserAgentString(WebSettings.getDefaultUserAgent(context)); } else { mSettings.setUserAgentString(mDefaultUserAgent); } break; case 2: mSettings.setUserAgentString(Constants.DESKTOP_USER_AGENT); break; case 3: mSettings.setUserAgentString(Constants.MOBILE_USER_AGENT); break; case 4: mSettings.setUserAgentString(mPreferences.getUserAgentString(mDefaultUserAgent)); break; } if (mPreferences.getSavePasswordsEnabled() && !mBrowserController.isIncognito()) { if (API < 18) { mSettings.setSavePassword(true); } mSettings.setSaveFormData(true); } else { if (API < 18) { mSettings.setSavePassword(false); } mSettings.setSaveFormData(false); } if (mPreferences.getJavaScriptEnabled()) { mSettings.setJavaScriptEnabled(true); mSettings.setJavaScriptCanOpenWindowsAutomatically(true); } if (mPreferences.getTextReflowEnabled()) { mTextReflow = true; mSettings.setLayoutAlgorithm(LayoutAlgorithm.NARROW_COLUMNS); if (API >= android.os.Build.VERSION_CODES.KITKAT) { try { mSettings.setLayoutAlgorithm(LayoutAlgorithm.TEXT_AUTOSIZING); } catch (Exception e) { // This shouldn't be necessary, but there are a number // of KitKat devices that crash trying to set this Log.e(Constants.TAG, "Problem setting LayoutAlgorithm to TEXT_AUTOSIZING"); } } } else { mTextReflow = false; mSettings.setLayoutAlgorithm(LayoutAlgorithm.NORMAL); } mSettings.setBlockNetworkImage(mPreferences.getBlockImagesEnabled()); mSettings.setSupportMultipleWindows(mPreferences.getPopupsEnabled()); mSettings.setUseWideViewPort(mPreferences.getUseWideViewportEnabled()); mSettings.setLoadWithOverviewMode(mPreferences.getOverviewModeEnabled()); switch (mPreferences.getTextSize()) { case 1: mSettings.setTextZoom(200); break; case 2: mSettings.setTextZoom(150); break; case 3: mSettings.setTextZoom(100); break; case 4: mSettings.setTextZoom(75); break; case 5: mSettings.setTextZoom(50); break; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { CookieManager.getInstance().setAcceptThirdPartyCookies(mWebView, !mPreferences.getBlockThirdPartyCookiesEnabled()); } } @SuppressWarnings("deprecation") @SuppressLint({ "SetJavaScriptEnabled", "NewApi" }) public void initializeSettings(WebSettings settings, Context context) { if (API < 18) { settings.setAppCacheMaxSize(Long.MAX_VALUE); } if (API < 17) { settings.setEnableSmoothTransition(true); } if (API > 16) { settings.setMediaPlaybackRequiresUserGesture(true); } if (API >= Build.VERSION_CODES.LOLLIPOP && !mBrowserController.isIncognito()) { settings.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE); } else if (API >= Build.VERSION_CODES.LOLLIPOP) { // We're in Incognito mode, reject settings.setMixedContentMode(WebSettings.MIXED_CONTENT_NEVER_ALLOW); } settings.setDomStorageEnabled(true); settings.setAppCacheEnabled(true); settings.setCacheMode(WebSettings.LOAD_DEFAULT); settings.setDatabaseEnabled(true); settings.setSupportZoom(true); settings.setBuiltInZoomControls(true); settings.setDisplayZoomControls(false); settings.setAllowContentAccess(true); settings.setAllowFileAccess(true); settings.setDefaultTextEncodingName("utf-8"); if (API > 16) { settings.setAllowFileAccessFromFileURLs(false); settings.setAllowUniversalAccessFromFileURLs(false); } settings.setAppCachePath(context.getDir("appcache", 0).getPath()); settings.setGeolocationDatabasePath(context.getDir("geolocation", 0).getPath()); if (API < Build.VERSION_CODES.KITKAT) { settings.setDatabasePath(context.getDir("databases", 0).getPath()); } } public boolean isShown() { return mWebView != null && mWebView.isShown(); } public synchronized void onPause() { if (mWebView != null) { mWebView.onPause(); } } public synchronized void onResume() { if (mWebView != null) { mWebView.onResume(); } } public void setForegroundTab(boolean isForeground) { isForegroundTab = isForeground; mBrowserController.update(); } public boolean isForegroundTab() { return isForegroundTab; } public int getProgress() { if (mWebView != null) { return mWebView.getProgress(); } else { return 100; } } public synchronized void stopLoading() { if (mWebView != null) { mWebView.stopLoading(); } } public void setHardwareRendering() { mWebView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); } public void setNormalRendering() { mWebView.setLayerType(View.LAYER_TYPE_NONE, null); } public void setSoftwareRendering() { mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } public void setColorMode(int mode) { mInvertPage = false; switch (mode) { case 0: mPaint.setColorFilter(null); // setSoftwareRendering(); // Some devices get segfaults // in the WebView with Hardware Acceleration enabled, // the only fix is to disable hardware rendering setNormalRendering(); mInvertPage = false; break; case 1: ColorMatrixColorFilter filterInvert = new ColorMatrixColorFilter( mNegativeColorArray); mPaint.setColorFilter(filterInvert); setHardwareRendering(); mInvertPage = true; break; case 2: ColorMatrix cm = new ColorMatrix(); cm.setSaturation(0); ColorMatrixColorFilter filterGray = new ColorMatrixColorFilter(cm); mPaint.setColorFilter(filterGray); setHardwareRendering(); break; case 3: ColorMatrix matrix = new ColorMatrix(); matrix.set(mNegativeColorArray); ColorMatrix matrixGray = new ColorMatrix(); matrixGray.setSaturation(0); ColorMatrix concat = new ColorMatrix(); concat.setConcat(matrix, matrixGray); ColorMatrixColorFilter filterInvertGray = new ColorMatrixColorFilter(concat); mPaint.setColorFilter(filterInvertGray); setHardwareRendering(); mInvertPage = true; break; } } public synchronized void pauseTimers() { if (mWebView != null) { mWebView.pauseTimers(); } } public synchronized void resumeTimers() { if (mWebView != null) { mWebView.resumeTimers(); } } public void requestFocus() { if (mWebView != null && !mWebView.hasFocus()) { mWebView.requestFocus(); } } public void setVisibility(int visible) { if (mWebView != null) { mWebView.setVisibility(visible); } } public void clearCache(boolean disk) { if (mWebView != null) { mWebView.clearCache(disk); } } public synchronized void reload() { if (mWebView != null) { mWebView.reload(); } } private void cacheFavicon(Bitmap icon) { String hash = String.valueOf(Utils.getDomainName(getUrl()).hashCode()); Log.d(Constants.TAG, "Caching icon for " + Utils.getDomainName(getUrl())); File image = new File(mActivity.getCacheDir(), hash + ".png"); try { FileOutputStream fos = new FileOutputStream(image); icon.compress(Bitmap.CompressFormat.PNG, 100, fos); fos.flush(); fos.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } @SuppressWarnings("deprecation") @SuppressLint("NewApi") public synchronized void find(String text) { if (mWebView != null) { if (API > 16) { mWebView.findAllAsync(text); } else { mWebView.findAll(text); } } } public Activity getActivity() { return mActivity; } public synchronized void onDestroy() { if (mWebView != null) { mWebView.stopLoading(); mWebView.onPause(); mWebView.clearHistory(); mWebView.setVisibility(View.GONE); mWebView.removeAllViews(); mWebView.destroyDrawingCache(); // mWebView.destroy(); //this is causing the segfault mWebView = null; } } public synchronized void goBack() { if (mWebView != null) { mWebView.goBack(); } } public String getUserAgent() { if (mWebView != null) { return mWebView.getSettings().getUserAgentString(); } else { return ""; } } public synchronized void goForward() { if (mWebView != null) { mWebView.goForward(); } } public boolean canGoBack() { return mWebView != null && mWebView.canGoBack(); } public boolean canGoForward() { return mWebView != null && mWebView.canGoForward(); } public WebView getWebView() { return mWebView; } public Bitmap getFavicon() { return mTitle.getFavicon(); } public synchronized void loadUrl(String url) { if (mWebView != null) { mWebView.loadUrl(url); } } public synchronized void invalidate() { if (mWebView != null) { mWebView.invalidate(); } } public String getTitle() { return mTitle.getTitle(); } public String getUrl() { if (mWebView != null) { return mWebView.getUrl(); } else { return ""; } } public class LightningWebClient extends WebViewClient { Context mActivity; LightningWebClient(Context context) { mActivity = context; } @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { if (mAdBlock.isAd(request.getUrl().getHost())) { ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); return new WebResourceResponse("text/plain", "utf-8", EMPTY); } return super.shouldInterceptRequest(view, request); } @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { if (mAdBlock.isAd(url)) { ByteArrayInputStream EMPTY = new ByteArrayInputStream("".getBytes()); return new WebResourceResponse("text/plain", "utf-8", EMPTY); } return null; } @Override public void onPageFinished(WebView view, String url) { if (view.isShown()) { mBrowserController.updateUrl(url, true); view.postInvalidate(); } if (view.getTitle() == null || view.getTitle().isEmpty()) { mTitle.setTitle(mActivity.getString(R.string.untitled)); } else { mTitle.setTitle(view.getTitle()); } if (API >= android.os.Build.VERSION_CODES.KITKAT && mInvertPage) { view.evaluateJavascript(Constants.JAVASCRIPT_INVERT_PAGE, null); } mBrowserController.update(); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { if (isShown()) { mBrowserController.updateUrl(url, false); mBrowserController.showActionBar(); } mTitle.setFavicon(mWebpageBitmap); mBrowserController.update(); } @Override public void onReceivedHttpAuthRequest(final WebView view, final HttpAuthHandler handler, final String host, final String realm) { AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); final EditText name = new EditText(mActivity); final EditText password = new EditText(mActivity); LinearLayout passLayout = new LinearLayout(mActivity); passLayout.setOrientation(LinearLayout.VERTICAL); passLayout.addView(name); passLayout.addView(password); name.setHint(mActivity.getString(R.string.hint_username)); name.setSingleLine(); password.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD); password.setSingleLine(); password.setTransformationMethod(new PasswordTransformationMethod()); password.setHint(mActivity.getString(R.string.hint_password)); builder.setTitle(mActivity.getString(R.string.title_sign_in)); builder.setView(passLayout); builder.setCancelable(true) .setPositiveButton(mActivity.getString(R.string.title_sign_in), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { String user = name.getText().toString(); String pass = password.getText().toString(); handler.proceed(user.trim(), pass.trim()); Log.d(Constants.TAG, "Request Login"); } }) .setNegativeButton(mActivity.getString(R.string.action_cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { handler.cancel(); } }); AlertDialog alert = builder.create(); alert.show(); } private boolean mIsRunning = false; private float mZoomScale = 0.0f; @Override public void onScaleChanged(final WebView view, final float oldScale, final float newScale) { if (view.isShown() && mTextReflow && API >= android.os.Build.VERSION_CODES.KITKAT) { if (mIsRunning) return; if (Math.abs(mZoomScale - newScale) > 0.01f) { mIsRunning = view.postDelayed(new Runnable() { @Override public void run() { mZoomScale = newScale; view.evaluateJavascript(Constants.JAVASCRIPT_TEXT_REFLOW, null); mIsRunning = false; } }, 100); } } } @Override public void onReceivedSslError(WebView view, final SslErrorHandler handler, SslError error) { AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); builder.setTitle(mActivity.getString(R.string.title_warning)); builder.setMessage(mActivity.getString(R.string.message_untrusted_certificate)) .setCancelable(true) .setPositiveButton(mActivity.getString(R.string.action_yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { handler.proceed(); } }) .setNegativeButton(mActivity.getString(R.string.action_no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { handler.cancel(); } }); AlertDialog alert = builder.create(); if (error.getPrimaryError() == SslError.SSL_UNTRUSTED) { alert.show(); } else { handler.proceed(); } } @Override public void onFormResubmission(WebView view, final Message dontResend, final Message resend) { AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); builder.setTitle(mActivity.getString(R.string.title_form_resubmission)); builder.setMessage(mActivity.getString(R.string.message_form_resubmission)) .setCancelable(true) .setPositiveButton(mActivity.getString(R.string.action_yes), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { resend.sendToTarget(); } }) .setNegativeButton(mActivity.getString(R.string.action_no), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { dontResend.sendToTarget(); } }); AlertDialog alert = builder.create(); alert.show(); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (mBrowserController.isIncognito()) { return super.shouldOverrideUrlLoading(view, url); } if (url.startsWith("about:")) { return super.shouldOverrideUrlLoading(view, url); } if (url.contains("mailto:")) { MailTo mailTo = MailTo.parse(url); Intent i = Utils.newEmailIntent(mActivity, mailTo.getTo(), mailTo.getSubject(), mailTo.getBody(), mailTo.getCc()); mActivity.startActivity(i); view.reload(); return true; } else if (url.startsWith("intent://")) { Intent intent = null; try { intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); } catch (URISyntaxException ex) { return false; } if (intent != null) { try { mActivity.startActivity(intent); } catch (ActivityNotFoundException e) { Log.e(Constants.TAG, "ActivityNotFoundException"); } return true; } } return mIntentUtils.startActivityForUrl(mWebView, url); } } public class LightningChromeClient extends WebChromeClient { Context mActivity; LightningChromeClient(Context context) { mActivity = context; } @Override public void onProgressChanged(WebView view, int newProgress) { if (isShown()) { mBrowserController.updateProgress(newProgress); } } @Override public void onReceivedIcon(WebView view, Bitmap icon) { mTitle.setFavicon(icon); mBrowserController.update(); cacheFavicon(icon); } @Override public void onReceivedTitle(WebView view, String title) { if (!title.isEmpty()) { mTitle.setTitle(title); } else { mTitle.setTitle(mActivity.getString(R.string.untitled)); } mBrowserController.update(); mBrowserController.updateHistory(title, view.getUrl()); } @Override public void onGeolocationPermissionsShowPrompt(final String origin, final GeolocationPermissions.Callback callback) { final boolean remember = true; AlertDialog.Builder builder = new AlertDialog.Builder(mActivity); builder.setTitle(mActivity.getString(R.string.location)); String org = null; if (origin.length() > 50) { org = origin.subSequence(0, 50) + "..."; } else { org = origin; } builder.setMessage(org + mActivity.getString(R.string.message_location)) .setCancelable(true) .setPositiveButton(mActivity.getString(R.string.action_allow), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { callback.invoke(origin, true, remember); } }) .setNegativeButton(mActivity.getString(R.string.action_dont_allow), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { callback.invoke(origin, false, remember); } }); AlertDialog alert = builder.create(); alert.show(); } @Override public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) { mBrowserController.onCreateWindow(isUserGesture, resultMsg); return true; } @Override public void onCloseWindow(WebView window) { // TODO Auto-generated method stub super.onCloseWindow(window); } public void openFileChooser(ValueCallback uploadMsg) { mBrowserController.openFileChooser(uploadMsg); } public void openFileChooser(ValueCallback uploadMsg, String acceptType) { mBrowserController.openFileChooser(uploadMsg); } public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) { mBrowserController.openFileChooser(uploadMsg); } public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) { mBrowserController.showFileChooser(filePathCallback); return true; } @Override public Bitmap getDefaultVideoPoster() { return mBrowserController.getDefaultVideoPoster(); } @Override public View getVideoLoadingProgressView() { return mBrowserController.getVideoLoadingProgressView(); } @Override public void onHideCustomView() { mBrowserController.onHideCustomView(); super.onHideCustomView(); } @Override public void onShowCustomView(View view, CustomViewCallback callback) { // While these lines might look like they work, in practice, // Full-screen videos won't work correctly. I may test this out some // more // if (view instanceof FrameLayout) { // FrameLayout frame = (FrameLayout) view; // if (frame.getFocusedChild() instanceof VideoView) { // VideoView video = (VideoView) frame.getFocusedChild(); // video.stopPlayback(); // frame.removeView(video); // video.setVisibility(View.GONE); // } // } else { Activity activity = mBrowserController.getActivity(); mBrowserController.onShowCustomView(view, activity.getRequestedOrientation(), callback); // } super.onShowCustomView(view, callback); } @Override @Deprecated public void onShowCustomView(View view, int requestedOrientation, CustomViewCallback callback) { // While these lines might look like they work, in practice, // Full-screen videos won't work correctly. I may test this out some // more // if (view instanceof FrameLayout) { // FrameLayout frame = (FrameLayout) view; // if (frame.getFocusedChild() instanceof VideoView) { // VideoView video = (VideoView) frame.getFocusedChild(); // video.stopPlayback(); // frame.removeView(video); // video.setVisibility(View.GONE); // } // } else { mBrowserController.onShowCustomView(view, requestedOrientation, callback); // } super.onShowCustomView(view, requestedOrientation, callback); } } public class Title { private Bitmap mFavicon; private String mTitle; private Bitmap mDefaultIcon; public Title(Context context, boolean darkTheme) { mDefaultIcon = Utils.getWebpageBitmap(context.getResources(), darkTheme); mFavicon = mDefaultIcon; mTitle = mActivity.getString(R.string.action_new_tab); } public void setFavicon(Bitmap favicon) { if (favicon == null) { mFavicon = mDefaultIcon; } else { mFavicon = Utils.padFavicon(favicon); } } public void setTitle(String title) { if (title == null) { mTitle = ""; } else { mTitle = title; } } public void setTitleAndFavicon(String title, Bitmap favicon) { mTitle = title; if (favicon == null) { mFavicon = mDefaultIcon; } else { mFavicon = Utils.padFavicon(favicon); } } public String getTitle() { return mTitle; } public Bitmap getFavicon() { return mFavicon; } } private class TouchListener implements OnTouchListener { float mLocation; float mY; int mAction; @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(View view, MotionEvent arg1) { if (view != null && !view.hasFocus()) { view.requestFocus(); } mAction = arg1.getAction(); mY = arg1.getY(); if (mAction == MotionEvent.ACTION_DOWN) { mLocation = mY; } else if (mAction == MotionEvent.ACTION_UP) { if ((mY - mLocation) > SCROLL_DOWN_THRESHOLD) { if (mWebView.getScrollY() != 0) { mBrowserController.showActionBar(); } else { mBrowserController.toggleActionBar(); } } else if ((mY - mLocation) < -SCROLL_UP_THRESHOLD) { mBrowserController.hideActionBar(); } mLocation = 0; } mGestureDetector.onTouchEvent(arg1); return false; } } private class CustomGestureListener extends SimpleOnGestureListener { /** * Without this, onLongPress is not called when user is zooming using * two fingers, but is when using only one. * * The required behaviour is to not trigger this when the user is * zooming, it shouldn't matter how much fingers the user's using. */ private boolean mCanTriggerLongPress = true; @Override public void onLongPress(MotionEvent e) { if (mCanTriggerLongPress) mBrowserController.onLongPress(); } /** * Is called when the user is swiping after the doubletap, which in our * case means that he is zooming. */ @Override public boolean onDoubleTapEvent(MotionEvent e) { mCanTriggerLongPress = false; return false; } /** * Is called when something is starting being pressed, always before * onLongPress. */ @Override public void onShowPress(MotionEvent e) { mCanTriggerLongPress = true; } } }