/*
* Zirco Browser for Android
*
* Copyright (C) 2010 J. Devauchelle and contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 3 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
package org.shadowsocks.zirco.ui.components;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.shadowsocks.ProxySettings;
import org.shadowsocks.zirco.controllers.Controller;
import org.shadowsocks.zirco.utils.ApplicationUtils;
import org.shadowsocks.zirco.utils.Constants;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.webkit.CookieManager;
import android.webkit.WebSettings;
import android.webkit.WebSettings.PluginState;
import android.webkit.WebSettings.ZoomDensity;
import android.webkit.WebView;
/**
* A convenient extension of WebView.
*/
public class CustomWebView extends WebView {
private Context mContext;
private int mProgress = 100;
private boolean mIsLoading = false;
private String mLoadedUrl;
private static boolean mBoMethodsLoaded = false;
private static Method mOnPauseMethod = null;
private static Method mOnResumeMethod = null;
private static Method mSetFindIsUp = null;
private static Method mNotifyFindDialogDismissed = null;
/**
* Constructor.
*
* @param context The current context.
*/
public CustomWebView(Context context) {
super(context);
mContext = context;
initializeOptions();
loadMethods();
}
/**
* Constructor.
*
* @param context The current context.
* @param attrs The attribute set.
*/
public CustomWebView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
initializeOptions();
loadMethods();
}
public void doNotifyFindDialogDismissed() {
if (mNotifyFindDialogDismissed != null) {
try {
mNotifyFindDialogDismissed.invoke(this);
} catch (IllegalArgumentException e) {
Log.e("CustomWebView",
"doNotifyFindDialogDismissed(): " + e.getMessage());
} catch (IllegalAccessException e) {
Log.e("CustomWebView",
"doNotifyFindDialogDismissed(): " + e.getMessage());
} catch (InvocationTargetException e) {
Log.e("CustomWebView",
"doNotifyFindDialogDismissed(): " + e.getMessage());
}
}
}
/**
* Perform an 'onPause' on this WebView through reflexion.
*/
public void doOnPause() {
if (mOnPauseMethod != null) {
try {
mOnPauseMethod.invoke(this);
} catch (IllegalArgumentException e) {
Log.e("CustomWebView", "doOnPause(): " + e.getMessage());
} catch (IllegalAccessException e) {
Log.e("CustomWebView", "doOnPause(): " + e.getMessage());
} catch (InvocationTargetException e) {
Log.e("CustomWebView", "doOnPause(): " + e.getMessage());
}
}
}
/**
* Perform an 'onResume' on this WebView through reflexion.
*/
public void doOnResume() {
if (mOnResumeMethod != null) {
try {
mOnResumeMethod.invoke(this);
} catch (IllegalArgumentException e) {
Log.e("CustomWebView", "doOnResume(): " + e.getMessage());
} catch (IllegalAccessException e) {
Log.e("CustomWebView", "doOnResume(): " + e.getMessage());
} catch (InvocationTargetException e) {
Log.e("CustomWebView", "doOnResume(): " + e.getMessage());
}
}
}
public void doSetFindIsUp(boolean value) {
if (mSetFindIsUp != null) {
try {
mSetFindIsUp.invoke(this, value);
} catch (IllegalArgumentException e) {
Log.e("CustomWebView", "doSetFindIsUp(): " + e.getMessage());
} catch (IllegalAccessException e) {
Log.e("CustomWebView", "doSetFindIsUp(): " + e.getMessage());
} catch (InvocationTargetException e) {
Log.e("CustomWebView", "doSetFindIsUp(): " + e.getMessage());
}
}
}
/**
* Get the loaded url, e.g. the one asked by the user, without redirections.
*
* @return The loaded url.
*/
public String getLoadedUrl() {
return mLoadedUrl;
}
/**
* Get the current loading progress of the view.
*
* @return The current loading progress of the view.
*/
@Override
public int getProgress() {
return mProgress;
}
private void initializeProxy() {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(mContext);
int port = 1984;
try {
port = Integer.valueOf(settings.getString("port", "1984"));
} catch (NumberFormatException ignore) {
}
ProxySettings.setProxy(mContext, "127.0.0.1", port);
}
/**
* Initialize the WebView with the options set by the user through
* preferences.
*/
public void initializeOptions() {
initializeProxy();
WebSettings settings = getSettings();
// User settings
settings.setJavaScriptEnabled(Controller
.getInstance()
.getPreferences()
.getBoolean(Constants.PREFERENCES_BROWSER_ENABLE_JAVASCRIPT,
true));
settings.setLoadsImagesAutomatically(Controller.getInstance()
.getPreferences()
.getBoolean(Constants.PREFERENCES_BROWSER_ENABLE_IMAGES, true));
settings.setUseWideViewPort(Controller
.getInstance()
.getPreferences()
.getBoolean(Constants.PREFERENCES_BROWSER_USE_WIDE_VIEWPORT,
true));
settings.setLoadWithOverviewMode(Controller
.getInstance()
.getPreferences()
.getBoolean(Constants.PREFERENCES_BROWSER_LOAD_WITH_OVERVIEW,
false));
settings.setSaveFormData(Controller
.getInstance()
.getPreferences()
.getBoolean(Constants.PREFERENCES_BROWSER_ENABLE_FORM_DATA,
true));
settings.setSavePassword(Controller
.getInstance()
.getPreferences()
.getBoolean(Constants.PREFERENCES_BROWSER_ENABLE_PASSWORDS,
true));
settings.setDefaultZoom(ZoomDensity.valueOf(Controller
.getInstance()
.getPreferences()
.getString(Constants.PREFERENCES_DEFAULT_ZOOM_LEVEL,
ZoomDensity.MEDIUM.toString())));
settings.setUserAgentString(Controller
.getInstance()
.getPreferences()
.getString(Constants.PREFERENCES_BROWSER_USER_AGENT,
Constants.USER_AGENT_DEFAULT));
CookieManager.getInstance().setAcceptCookie(
Controller
.getInstance()
.getPreferences()
.getBoolean(
Constants.PREFERENCES_BROWSER_ENABLE_COOKIES,
true));
settings.setPluginState(PluginState.valueOf(Controller
.getInstance()
.getPreferences()
.getString(Constants.PREFERENCES_BROWSER_ENABLE_PLUGINS,
PluginState.ON_DEMAND.toString())));
settings.setSupportZoom(true);
// Technical settings
settings.setSupportMultipleWindows(true);
setLongClickable(true);
setScrollbarFadingEnabled(true);
setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);
setDrawingCacheEnabled(true);
settings.setAppCacheEnabled(true);
settings.setDatabaseEnabled(true);
settings.setDomStorageEnabled(true);
}
/**
* Check if the view is currently loading.
*
* @return True if the view is currently loading.
*/
public boolean isLoading() {
return mIsLoading;
}
public boolean isSameUrl(String url) {
if (url != null) {
return url.equalsIgnoreCase(this.getUrl());
}
return false;
}
/**
* Inject the AdSweep javascript.
*/
public void loadAdSweep() {
super.loadUrl(ApplicationUtils.getAdSweepString(mContext));
}
/**
* Load static reflected methods.
*/
private void loadMethods() {
if (!mBoMethodsLoaded) {
try {
mOnPauseMethod = WebView.class.getMethod("onPause");
mOnResumeMethod = WebView.class.getMethod("onResume");
} catch (SecurityException e) {
Log.e("CustomWebView", "loadMethods(): " + e.getMessage());
mOnPauseMethod = null;
mOnResumeMethod = null;
} catch (NoSuchMethodException e) {
Log.e("CustomWebView", "loadMethods(): " + e.getMessage());
mOnPauseMethod = null;
mOnResumeMethod = null;
}
try {
mSetFindIsUp = WebView.class.getMethod("setFindIsUp",
Boolean.TYPE);
mNotifyFindDialogDismissed = WebView.class
.getMethod("notifyFindDialogDismissed");
} catch (SecurityException e) {
Log.e("CustomWebView", "loadMethods(): " + e.getMessage());
mSetFindIsUp = null;
mNotifyFindDialogDismissed = null;
} catch (NoSuchMethodException e) {
Log.e("CustomWebView", "loadMethods(): " + e.getMessage());
mSetFindIsUp = null;
mNotifyFindDialogDismissed = null;
}
mBoMethodsLoaded = true;
}
}
@Override
public void loadUrl(String url) {
mLoadedUrl = url;
super.loadUrl(url);
}
/**
* Triggered when the page has finished loading.
*/
public void notifyPageFinished() {
mProgress = 100;
mIsLoading = false;
}
/**
* Triggered when a new page loading is requested.
*/
public void notifyPageStarted() {
mIsLoading = true;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
// Enable / disable zoom support in case of multiple pointer, e.g.
// enable zoom when we have two down pointers, disable with one pointer
// or when pointer up.
// We do this to prevent the display of zoom controls, which are not
// useful and override over the right bubble.
if ((action == MotionEvent.ACTION_DOWN)
|| (action == MotionEvent.ACTION_POINTER_DOWN)
|| (action == MotionEvent.ACTION_POINTER_1_DOWN)
|| (action == MotionEvent.ACTION_POINTER_2_DOWN)
|| (action == MotionEvent.ACTION_POINTER_3_DOWN)) {
if (ev.getPointerCount() > 1) {
this.getSettings().setBuiltInZoomControls(true);
this.getSettings().setSupportZoom(true);
} else {
this.getSettings().setBuiltInZoomControls(false);
this.getSettings().setSupportZoom(false);
}
} else if ((action == MotionEvent.ACTION_UP)
|| (action == MotionEvent.ACTION_POINTER_UP)
|| (action == MotionEvent.ACTION_POINTER_1_UP)
|| (action == MotionEvent.ACTION_POINTER_2_UP)
|| (action == MotionEvent.ACTION_POINTER_3_UP)) {
this.getSettings().setBuiltInZoomControls(false);
this.getSettings().setSupportZoom(false);
}
return super.onTouchEvent(ev);
}
/**
* Reset the loaded url.
*/
public void resetLoadedUrl() {
mLoadedUrl = null;
}
/**
* Set the current loading progress of this view.
*
* @param progress The current loading progress.
*/
public void setProgress(int progress) {
mProgress = progress;
}
}