{
- void onValue( T retValue);
+ void onValue(T retValue);
}
diff --git a/dsbridge/src/main/java/wendu/dsbridge/WebViewEvent.java b/dsbridge/src/main/java/wendu/dsbridge/WebViewEvent.java
new file mode 100644
index 0000000..f20a96b
--- /dev/null
+++ b/dsbridge/src/main/java/wendu/dsbridge/WebViewEvent.java
@@ -0,0 +1,39 @@
+package wendu.dsbridge;
+
+import com.tencent.smtt.sdk.WebChromeClient;
+
+import java.util.Map;
+
+/**
+ *
+ * author : senjoeson
+ * e-mail : senjoeson@foxmail.com
+ * time : 2019/08/15
+ * desc : webview行为外部代理,抽取
+ * version: 1.0
+ *
+ */
+public interface WebViewEvent {
+
+ void init();
+
+ void setWebChromeClient(WebChromeClient client);
+
+ void clearCache(boolean includeDiskFiles);
+
+ void reload();
+
+ void loadUrl(final String url);
+
+ void loadUrl(final String url, final Map additionalHttpHeaders);
+
+
+ void addJavascriptObject(Object object, String nameSpace);
+
+
+ void onDestroyed();
+
+ public interface View {
+ DWebView getWebView();
+ }
+}
diff --git a/dsbridge/src/main/java/wendu/dsbridge/WebViewEventImpl.java b/dsbridge/src/main/java/wendu/dsbridge/WebViewEventImpl.java
new file mode 100644
index 0000000..6a2de1b
--- /dev/null
+++ b/dsbridge/src/main/java/wendu/dsbridge/WebViewEventImpl.java
@@ -0,0 +1,641 @@
+package wendu.dsbridge;
+
+import android.annotation.SuppressLint;
+import android.annotation.TargetApi;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.support.annotation.Keep;
+import android.support.v7.app.AlertDialog;
+import android.util.SparseArray;
+import android.view.Gravity;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.FrameLayout;
+
+import com.tencent.smtt.export.external.interfaces.ConsoleMessage;
+import com.tencent.smtt.export.external.interfaces.GeolocationPermissionsCallback;
+import com.tencent.smtt.export.external.interfaces.IX5WebChromeClient;
+import com.tencent.smtt.export.external.interfaces.JsPromptResult;
+import com.tencent.smtt.export.external.interfaces.JsResult;
+import com.tencent.smtt.sdk.CookieManager;
+import com.tencent.smtt.sdk.ValueCallback;
+import com.tencent.smtt.sdk.WebChromeClient;
+import com.tencent.smtt.sdk.WebSettings;
+import com.tencent.smtt.sdk.WebStorage;
+import com.tencent.smtt.sdk.WebView;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * author : sunqiao
+ * e-mail : sunqiao@kayak.com.cn
+ * time : 2019/08/15
+ * desc : 代理实现类,代理webview具体行为, webview不再做处理
+ * version: 1.0
+ *
+ */
+public class WebViewEventImpl implements WebViewEvent {
+
+ private DWebView mDWebView;
+ private String APP_CACHE_DIRNAME;
+ private InnerJavascriptInterface innerJavascriptInterface = new InnerJavascriptInterface(this);
+ private static final String BRIDGE_NAME = "_dsbridge";
+ public static boolean isDebug = true;
+ public volatile boolean alertBoxBlock = true;
+
+ public Map javaScriptNamespaceInterfaces = new HashMap<>();
+ SparseArray handlerMap = new SparseArray<>();
+ private Handler mainHandler = new Handler(Looper.getMainLooper());
+ JavascriptCloseWindowListener javascriptCloseWindowListener = null;
+ private ArrayList callInfoList = null;
+ private int callID = 0;
+ private WebChromeClient webChromeClient;
+ int MIXED_CONTENT_ALWAYS_ALLOW = 0;
+
+
+ public DWebView getWebView() {
+ return mDWebView;
+ }
+
+ public WebViewEventImpl(View view) {
+ mDWebView = view.getWebView();
+ init();
+ }
+
+ @Override
+ public void setWebChromeClient(WebChromeClient client) {
+ mDWebView.setWebChromeClient(client);
+ }
+
+ @Override
+ public void clearCache(boolean includeDiskFiles) {
+ CookieManager.getInstance().removeAllCookie();
+ Context context = mDWebView.getContext();
+ try {
+ context.deleteDatabase("webview.db");
+ context.deleteDatabase("webviewCache.db");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ File appCacheDir = new File(APP_CACHE_DIRNAME);
+ File webviewCacheDir = new File(context.getCacheDir()
+ .getAbsolutePath() + "/webviewCache");
+
+ if (webviewCacheDir.exists()) {
+ FileUtils.deleteFile(webviewCacheDir);
+ }
+
+ if (appCacheDir.exists()) {
+ FileUtils.deleteFile(appCacheDir);
+ }
+ mDWebView.clearCache(includeDiskFiles);
+ }
+
+ @Override
+ public void reload() {
+ runOnMainThread(() -> {
+ callInfoList = new ArrayList<>();
+ mDWebView.reload();
+ });
+ }
+
+ @Override
+ public void loadUrl(String url) {
+ runOnMainThread(new Runnable() {
+ @Override
+ public void run() {
+ if (url != null && url.startsWith("javascript:")) {
+ mDWebView.loadUrl(url);
+ } else {
+ callInfoList = new ArrayList<>();
+ mDWebView.loadUrl(url);
+ }
+ }
+ });
+ }
+
+ @Override
+ public void loadUrl(String url, Map additionalHttpHeaders) {
+ runOnMainThread(() -> {
+ if (url != null && url.startsWith("javascript:")) {
+ mDWebView.loadUrl(url, additionalHttpHeaders);
+ } else {
+ callInfoList = new ArrayList<>();
+ mDWebView.loadUrl(url, additionalHttpHeaders);
+ }
+ });
+ }
+
+ @Override
+ public void addJavascriptObject(Object object, String nameSpace) {
+ if (nameSpace == null) {
+ nameSpace = "";
+ }
+ if (object != null) {
+ javaScriptNamespaceInterfaces.put(nameSpace, object);
+ }
+ }
+
+ @Override
+ public void onDestroyed() {
+ if (javascriptCloseWindowListener != null) {
+ javascriptCloseWindowListener = null;
+ }
+ handlerMap.clear();
+ javaScriptNamespaceInterfaces.clear();
+ if (mainHandler != null) {
+ mainHandler.removeCallbacksAndMessages(null);
+ }
+ mDWebView.destroy();
+ }
+
+
+
+ @SuppressLint({"AddJavascriptInterface", "SetJavaScriptEnabled"})
+ @Override
+ public void init() {
+ APP_CACHE_DIRNAME = mDWebView.getContext().getFilesDir().getAbsolutePath() + "/webcache";
+ WebSettings settings = mDWebView.getSettings();
+ settings.setDomStorageEnabled(true);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ CookieManager.getInstance().setAcceptThirdPartyCookies(mDWebView, true);
+ settings.setMixedContentMode(MIXED_CONTENT_ALWAYS_ALLOW);
+ }
+ settings.setAllowFileAccess(false);
+ settings.setAppCacheEnabled(false);
+ settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
+ settings.setJavaScriptEnabled(true);
+ settings.setLoadWithOverviewMode(true);
+ settings.setAppCachePath(APP_CACHE_DIRNAME);
+ settings.setUseWideViewPort(true);
+ setWebChromeClient(mWebChromeClient);
+ addInternalJavascriptObject();
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
+ mDWebView.addJavascriptInterface(innerJavascriptInterface, BRIDGE_NAME);
+ } else {
+ // add dsbridge tag in lower android version
+ settings.setUserAgentString(settings.getUserAgentString() + " _dsbridge");
+ }
+ }
+
+
+ public void setWebContentsDebuggingEnabled(boolean enabled) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ WebView.setWebContentsDebuggingEnabled(enabled);
+ }
+ isDebug = enabled;
+ }
+
+ public String[] parseNamespace(String method) {
+ int pos = method.lastIndexOf('.');
+ String namespace = "";
+ if (pos != -1) {
+ namespace = method.substring(0, pos);
+ method = method.substring(pos + 1);
+ }
+ return new String[]{namespace, method};
+ }
+
+ @Keep
+ private void addInternalJavascriptObject() {
+ addJavascriptObject(new JsBridgeObject(this), "_dsb");
+ }
+
+ public void runOnMainThread(Runnable runnable) {
+ if (Looper.getMainLooper() == Looper.myLooper()) {
+ runnable.run();
+ return;
+ }
+ mainHandler.post(runnable);
+ }
+
+ public void setJavascriptCloseWindowListener(JavascriptCloseWindowListener listener) {
+ javascriptCloseWindowListener = listener;
+ }
+
+ private void dispatchJavascriptCall(CallInfo info) {
+ evaluateJavascript(String.format("window._handleMessageFromNative(%s)", info.toString()));
+ }
+
+ public synchronized void callHandler(String method, Object[] args, final OnReturnValue handler) {
+ CallInfo callInfo = new CallInfo(method, callID++, args);
+ if (handler != null) {
+ handlerMap.put(callInfo.callbackId, handler);
+ }
+
+ if (callInfoList != null) {
+ callInfoList.add(callInfo);
+ } else {
+ dispatchJavascriptCall(callInfo);
+ }
+
+ }
+
+
+ public void callHandler(String method, Object[] args) {
+ callHandler(method, args, null);
+ }
+
+ public void callHandler(String method, OnReturnValue handler) {
+ callHandler(method, null, handler);
+ }
+
+
+ /**
+ * Test whether the handler exist in javascript
+ *
+ * @param handlerName
+ * @param existCallback
+ */
+ public void hasJavascriptMethod(String handlerName, OnReturnValue existCallback) {
+ callHandler("_hasJavascriptMethod", new Object[]{handlerName}, existCallback);
+ }
+
+
+ public synchronized void dispatchStartupQueue() {
+ if (callInfoList != null) {
+ for (CallInfo info : callInfoList) {
+ dispatchJavascriptCall(info);
+ }
+ callInfoList = null;
+ }
+ }
+
+
+ /**
+ * remove the javascript object with supplied namespace.
+ *
+ * @param namespace
+ */
+ public void removeJavascriptObject(String namespace) {
+ if (namespace == null) {
+ namespace = "";
+ }
+ javaScriptNamespaceInterfaces.remove(namespace);
+
+ }
+
+
+ public void disableJavascriptDialogBlock(boolean disable) {
+ alertBoxBlock = !disable;
+ }
+
+
+ public void evaluateJavascript(final String script) {
+ runOnMainThread(() -> _evaluateJavascript(script));
+ }
+
+ private void _evaluateJavascript(String script) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ mDWebView.evaluateJavascript(script, null);
+ } else {
+ loadUrl("javascript:" + script);
+ }
+ }
+
+ private WebChromeClient mWebChromeClient = new WebChromeClient() {
+
+ @Override
+ public void onProgressChanged(WebView view, int newProgress) {
+ if (webChromeClient != null) {
+ webChromeClient.onProgressChanged(view, newProgress);
+ } else {
+ super.onProgressChanged(view, newProgress);
+ }
+ }
+
+ @Override
+ public void onReceivedTitle(WebView view, String title) {
+ if (webChromeClient != null) {
+ webChromeClient.onReceivedTitle(view, title);
+ } else {
+ super.onReceivedTitle(view, title);
+ }
+ }
+
+ @Override
+ public void onReceivedIcon(WebView view, Bitmap icon) {
+ if (webChromeClient != null) {
+ webChromeClient.onReceivedIcon(view, icon);
+ } else {
+ super.onReceivedIcon(view, icon);
+ }
+ }
+
+ @Override
+ public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) {
+ if (webChromeClient != null) {
+ webChromeClient.onReceivedTouchIconUrl(view, url, precomposed);
+ } else {
+ super.onReceivedTouchIconUrl(view, url, precomposed);
+ }
+ }
+
+ @Override
+ public void onShowCustomView(android.view.View view, IX5WebChromeClient.CustomViewCallback callback) {
+ if (webChromeClient != null) {
+ webChromeClient.onShowCustomView(view, callback);
+ } else {
+ super.onShowCustomView(view, callback);
+ }
+ }
+
+ @Override
+ public void onShowCustomView(android.view.View view, int requestedOrientation,
+ IX5WebChromeClient.CustomViewCallback callback) {
+ if (webChromeClient != null) {
+ webChromeClient.onShowCustomView(view, requestedOrientation, callback);
+ } else {
+ super.onShowCustomView(view, requestedOrientation, callback);
+ }
+ }
+
+ @Override
+ public void onHideCustomView() {
+ if (webChromeClient != null) {
+ webChromeClient.onHideCustomView();
+ } else {
+ super.onHideCustomView();
+ }
+ }
+
+ @Override
+ public boolean onCreateWindow(WebView view, boolean isDialog,
+ boolean isUserGesture, Message resultMsg) {
+ if (webChromeClient != null) {
+ return webChromeClient.onCreateWindow(view, isDialog,
+ isUserGesture, resultMsg);
+ }
+ return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg);
+ }
+
+ @Override
+ public void onRequestFocus(WebView view) {
+ if (webChromeClient != null) {
+ webChromeClient.onRequestFocus(view);
+ } else {
+ super.onRequestFocus(view);
+ }
+ }
+
+ @Override
+ public void onCloseWindow(WebView window) {
+ if (webChromeClient != null) {
+ webChromeClient.onCloseWindow(window);
+ } else {
+ super.onCloseWindow(window);
+ }
+ }
+
+
+ @Override
+ public boolean onJsAlert(WebView view, String url, final String message, final JsResult result) {
+
+ if (!alertBoxBlock) {
+ result.confirm();
+ }
+ if (webChromeClient != null) {
+ if (webChromeClient.onJsAlert(view, url, message, result)) {
+ return true;
+ }
+ }
+ Dialog alertDialog = new AlertDialog.Builder(mDWebView.getContext()).
+ setMessage(message).
+ setCancelable(false).
+ setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ dialog.dismiss();
+ if (alertBoxBlock) {
+ result.confirm();
+ }
+ }
+ })
+ .create();
+ alertDialog.show();
+ return true;
+ }
+
+ @Override
+ public boolean onJsConfirm(WebView view, String url, String message,
+ final JsResult result) {
+ if (!alertBoxBlock) {
+ result.confirm();
+ }
+ if (webChromeClient != null && webChromeClient.onJsConfirm(view, url, message, result)) {
+ return true;
+ } else {
+ DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (alertBoxBlock) {
+ if (which == Dialog.BUTTON_POSITIVE) {
+ result.confirm();
+ } else {
+ result.cancel();
+ }
+ }
+ }
+ };
+ new AlertDialog.Builder(mDWebView.getContext())
+ .setMessage(message)
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok, listener)
+ .setNegativeButton(android.R.string.cancel, listener).show();
+ return true;
+
+ }
+
+ }
+
+ @Override
+ public boolean onJsPrompt(WebView view, String url, final String message,
+ String defaultValue, final JsPromptResult result) {
+
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
+ String prefix = "_dsbridge=";
+ if (message.startsWith(prefix)) {
+ result.confirm(innerJavascriptInterface.call(message.substring(prefix.length()), defaultValue));
+ return true;
+ }
+ }
+
+ if (!alertBoxBlock) {
+ result.confirm();
+ }
+
+ if (webChromeClient != null && webChromeClient.onJsPrompt(view, url, message, defaultValue, result)) {
+ return true;
+ } else {
+ final EditText editText = new EditText(mDWebView.getContext());
+ editText.setText(defaultValue);
+ if (defaultValue != null) {
+ editText.setSelection(defaultValue.length());
+ }
+ float dpi =mDWebView. getContext().getResources().getDisplayMetrics().density;
+ DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (alertBoxBlock) {
+ if (which == Dialog.BUTTON_POSITIVE) {
+ result.confirm(editText.getText().toString());
+ } else {
+ result.cancel();
+ }
+ }
+ }
+ };
+ new AlertDialog.Builder(mDWebView.getContext())
+ .setTitle(message)
+ .setView(editText)
+ .setCancelable(false)
+ .setPositiveButton(android.R.string.ok, listener)
+ .setNegativeButton(android.R.string.cancel, listener)
+ .show();
+ FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ int t = (int) (dpi * 16);
+ layoutParams.setMargins(t, 0, t, 0);
+ layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
+ editText.setLayoutParams(layoutParams);
+ int padding = (int) (15 * dpi);
+ editText.setPadding(padding - (int) (5 * dpi), padding, padding, padding);
+ return true;
+ }
+
+ }
+
+ @Override
+ public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
+ if (webChromeClient != null) {
+ return webChromeClient.onJsBeforeUnload(view, url, message, result);
+ }
+ return super.onJsBeforeUnload(view, url, message, result);
+ }
+
+ @Override
+ public void onExceededDatabaseQuota(String url, String databaseIdentifier, long quota,
+ long estimatedDatabaseSize,
+ long totalQuota,
+ WebStorage.QuotaUpdater quotaUpdater) {
+ if (webChromeClient != null) {
+ webChromeClient.onExceededDatabaseQuota(url, databaseIdentifier, quota,
+ estimatedDatabaseSize, totalQuota, quotaUpdater);
+ } else {
+ super.onExceededDatabaseQuota(url, databaseIdentifier, quota,
+ estimatedDatabaseSize, totalQuota, quotaUpdater);
+ }
+ }
+
+ @Override
+ public void onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater) {
+ if (webChromeClient != null) {
+ webChromeClient.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater);
+ }
+ super.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater);
+ }
+
+ @Override
+ public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissionsCallback callback) {
+ if (webChromeClient != null) {
+ webChromeClient.onGeolocationPermissionsShowPrompt(origin, callback);
+ } else {
+ super.onGeolocationPermissionsShowPrompt(origin, callback);
+ }
+ }
+
+ @Override
+ public void onGeolocationPermissionsHidePrompt() {
+ if (webChromeClient != null) {
+ webChromeClient.onGeolocationPermissionsHidePrompt();
+ } else {
+ super.onGeolocationPermissionsHidePrompt();
+ }
+ }
+
+
+ @Override
+ public boolean onJsTimeout() {
+ if (webChromeClient != null) {
+ return webChromeClient.onJsTimeout();
+ }
+ return super.onJsTimeout();
+ }
+
+
+ @Override
+ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+ if (webChromeClient != null) {
+ return webChromeClient.onConsoleMessage(consoleMessage);
+ }
+ return super.onConsoleMessage(consoleMessage);
+ }
+
+ @Override
+ public Bitmap getDefaultVideoPoster() {
+
+ if (webChromeClient != null) {
+ return webChromeClient.getDefaultVideoPoster();
+ }
+ return super.getDefaultVideoPoster();
+ }
+
+ @Override
+ public android.view.View getVideoLoadingProgressView() {
+ if (webChromeClient != null) {
+ return webChromeClient.getVideoLoadingProgressView();
+ }
+ return super.getVideoLoadingProgressView();
+ }
+
+ @Override
+ public void getVisitedHistory(ValueCallback callback) {
+ if (webChromeClient != null) {
+ webChromeClient.getVisitedHistory(callback);
+ } else {
+ super.getVisitedHistory(callback);
+ }
+ }
+
+ @Override
+ public void openFileChooser(ValueCallback valueCallback, String s, String s1) {
+ if (webChromeClient != null) {
+ webChromeClient.openFileChooser(valueCallback, s, s1);
+ return;
+ }
+ super.openFileChooser(valueCallback, s, s1);
+ }
+
+ @Override
+ public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback,
+ FileChooserParams fileChooserParams) {
+ if (webChromeClient != null) {
+ return webChromeClient.onShowFileChooser(webView, filePathCallback, fileChooserParams);
+ }
+ return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
+ }
+
+
+ @Keep
+ @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ public void openFileChooser(ValueCallback valueCallback, String acceptType) {
+ if (webChromeClient instanceof FileChooser) {
+ ((FileChooser) webChromeClient).openFileChooser(valueCallback, acceptType);
+ }
+ }
+
+ };
+}
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index c01f703..96de6a7 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -4,4 +4,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip