package com.qmusic.webdoengine;
import java.lang.reflect.Method;
import java.util.LinkedList;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import com.qmusic.MyApplication;
import com.qmusic.common.BConstants;
import com.qmusic.uitls.BLog;
@SuppressLint("NewApi")
public class BWebView extends WebView {
private static final String TAG = BWebView.class.getSimpleName();
private BWebHost webHost;
private boolean isReady;
private BNativeToJsMessageQueue queue;
private LinkedList<String> cachedJavascript;
// ==============================
static {
if (MyApplication.DEBUG && android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT) {
WebView.setWebContentsDebuggingEnabled(true);
}
}
public BWebView(Context context) {
super(context);
init(context);
}
public BWebView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public BWebView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
@SuppressWarnings("deprecation")
@SuppressLint("SetJavaScriptEnabled")
private void init(Context context) {
setHorizontalScrollBarEnabled(false);
setVerticalScrollBarEnabled(false);
setScrollbarFadingEnabled(true);
setWebChromeClient(new BWebChromeClient(this));
setWebViewClient(new BWebViewClient(this));
final WebSettings settings = getSettings();
settings.setAllowContentAccess(true);
settings.setAllowFileAccessFromFileURLs(true);
settings.setAllowUniversalAccessFromFileURLs(true);
settings.setAppCacheEnabled(true);
settings.setDatabaseEnabled(true);
settings.setDomStorageEnabled(true);
settings.setAppCachePath(context.getDir("appcache", 0).getPath());
settings.setGeolocationDatabasePath(context.getDir("geolocation", 0).getPath());
settings.setJavaScriptEnabled(true);
settings.setSaveFormData(false);
settings.setSavePassword(false);
settings.setDatabaseEnabled(true);
// use GAJavaScript name space to be compatible with iOS version
queue = new BNativeToJsMessageQueue(this);
addJavascriptInterface(this, "GAJavaScript");
// removeJavascriptInterface("GAJavaScript");
}
public void attachWebview(BWebHost webHost, ViewGroup parent) {
this.webHost = webHost;
this.webHost.setWebView(this);
ViewParent oldParent = getParent();
if (oldParent == null) {
parent.addView(this);
} else if (oldParent != parent) {
BLog.w(TAG, "webview has already a parent.");
((ViewGroup) oldParent).removeView(this);
parent.addView(this);
}
webHost.postMessage(BConstants.MSG_PAGE_START_LOADING, 0, null);
if (isReady) {
webHost.postMessage(BConstants.MSG_PAGE_FINISH_LOADING, 0, null);
}
}
public void detachWebview(ViewGroup parent) {
ViewParent parent1 = getParent();
if (null != parent1) {
// Check if it is the right parent
if (parent == parent1) {
this.webHost = null;
parent.removeView(this);
} else {
BLog.w(TAG, "parent is not the webview's parent");
}
}
}
public void sendJavascript(String statement) {
if (isReady) {
queue.addJavaScript(statement);
} else {
if (cachedJavascript == null) {
cachedJavascript = new LinkedList<String>();
}
cachedJavascript.add(statement);
BLog.w(TAG, "execute js before page loaded:" + statement);
}
}
public void setWebHost(BWebHost webHost) {
this.webHost = webHost;
this.webHost.setWebView(this);
webHost.postMessage(BConstants.MSG_PAGE_START_LOADING, 0, null);
if (isReady) {
webHost.postMessage(BConstants.MSG_PAGE_FINISH_LOADING, 0, null);
}
}
public BWebHost getWebHost() {
return webHost;
}
public boolean getState() {
return isReady;
}
public void setState(boolean isReady) {
this.isReady = isReady;
if (isReady && cachedJavascript != null && cachedJavascript.size() > 0) {
for (String statement : cachedJavascript) {
queue.addJavaScript(statement);
}
cachedJavascript.clear();
}
}
// ===========================================================
@JavascriptInterface
public void performSelector(String method) {
this.invokeJavaMethod(method);
}
@JavascriptInterface
public void performSelector(String method, String arg) {
this.invokeJavaMethod(method, arg);
}
@JavascriptInterface
public void performSelector(String method, String arg1, String arg2) {
this.invokeJavaMethod(method, arg1, arg2);
}
@JavascriptInterface
public void performSelector(String method, String arg1, String arg2, String arg3) {
this.invokeJavaMethod(method, arg1, arg2, arg3);
}
/** JS bridge */
@SuppressWarnings("rawtypes")
private void invokeJavaMethod(String... args) {
try {
if (webHost == null) {
BLog.e(TAG, "webHost is null");
return;
}
Object jsInterface = webHost.getJSInterface();
if (jsInterface == null) {
BLog.e(TAG, "jsInterface is null");
return;
}
int numberOfArgs = args.length;
Class[] argClasses = null;
if (numberOfArgs < 1) {
BLog.e(TAG, "Javascript called performSelector with no arguments");
return;
} else if (numberOfArgs > 1) {
argClasses = new Class[numberOfArgs - 1];
for (int i = 0; i < numberOfArgs - 1; i++) {
argClasses[i] = String.class;
}
}
// originally the method name is for Objective C, on Android, the
// ":" needs to be removed
// e.g. executeTask:params: -> executeTaskparams
final String methodName = args[0].replaceAll(":", "");
BLog.d(TAG, "MethodName=" + args[0] + " normalized method=" + methodName);
Method method = jsInterface.getClass().getDeclaredMethod(methodName, argClasses);
method.invoke(jsInterface, (Object[]) java.util.Arrays.copyOfRange(args, 1, numberOfArgs));
} catch (Exception e) {
e.printStackTrace();
for (String a : args) {
BLog.e(TAG, a);
}
}
}
}