Lightning browser with I2P configuration
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

1118 lines
32 KiB

/*
* 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<Uri> uploadMsg) {
mBrowserController.openFileChooser(uploadMsg);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mBrowserController.openFileChooser(uploadMsg);
}
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
mBrowserController.openFileChooser(uploadMsg);
}
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> 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;
}
}
}