diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 96cc43e..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml deleted file mode 100644 index e7bedf3..0000000 --- a/.idea/copyright/profiles_settings.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/dictionaries/du.xml b/.idea/dictionaries/du.xml deleted file mode 100644 index 68bbf78..0000000 --- a/.idea/dictionaries/du.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index cb9c4d8..2996d53 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -3,15 +3,11 @@ diff --git a/.idea/misc.xml b/.idea/misc.xml index 1c5662a..89d557a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,67 +1,11 @@ - - + + + + - - - - - - - - - - - - - - - - - - - - - 1.8 - - - - - - + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 9a89c1c..99a31c8 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index a2ad81f..04b3f8a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,12 +1,12 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 23 - buildToolsVersion '25.0.0' + compileSdkVersion 28 + buildToolsVersion '28.0.3' defaultConfig { applicationId "wendu.jsbdemo" - minSdkVersion 11 - targetSdkVersion 23 + minSdkVersion 14 + targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" @@ -16,29 +16,25 @@ android { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } - debug { - testCoverageEnabled true - } } - lintOptions { abortOnError false } - - testOptions { - unitTests.returnDefaultValues = true + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 } } dependencies { - compile fileTree(dir: 'libs', include: ['*.jar']) - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(include: ['*.jar'], dir: 'libs') + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile project(path: ':dsbridge') - compile 'com.android.support:appcompat-v7:23.4.0' - compile 'com.squareup.okhttp3:okhttp:3.3.0' - testCompile 'junit:junit:4.12' + implementation project(path: ':dsbridge') + implementation 'com.android.support:appcompat-v7:28.0.0' + implementation 'com.squareup.okhttp3:okhttp:3.3.0' + testImplementation 'junit:junit:4.12' } diff --git a/app/src/main/java/wendu/jsbdemo/CallJavascriptActivity.java b/app/src/main/java/wendu/jsbdemo/CallJavascriptActivity.java index fce3720..1ad091a 100644 --- a/app/src/main/java/wendu/jsbdemo/CallJavascriptActivity.java +++ b/app/src/main/java/wendu/jsbdemo/CallJavascriptActivity.java @@ -9,38 +9,40 @@ import wendu.dsbridge.DWebView; import wendu.dsbridge.OnReturnValue; +import wendu.dsbridge.WebViewEventImpl; -public class CallJavascriptActivity extends AppCompatActivity implements View.OnClickListener { +public class CallJavascriptActivity extends AppCompatActivity implements View.OnClickListener{ DWebView dWebView; + public WebViewEventImpl mWebViewEvent; + - public T getView(int viewId) { - View view = findViewById(viewId); - return (T) view; - } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_call_javascript); - getView(R.id.addValue).setOnClickListener(this); - getView(R.id.append).setOnClickListener(this); - getView(R.id.startTimer).setOnClickListener(this); - getView(R.id.synAddValue).setOnClickListener(this); - getView(R.id.synGetInfo).setOnClickListener(this); - getView(R.id.asynAddValue).setOnClickListener(this); - getView(R.id.asynGetInfo).setOnClickListener(this); - getView(R.id.hasMethodAddValue).setOnClickListener(this); - getView(R.id.hasMethodXX).setOnClickListener(this); - getView(R.id.hasMethodAsynAddValue).setOnClickListener(this); - getView(R.id.hasMethodAsynXX).setOnClickListener(this); - DWebView.setWebContentsDebuggingEnabled(true); - dWebView= getView(R.id.webview); - dWebView.loadUrl("file:///android_asset/native-call-js.html"); + findViewById(R.id.addValue).setOnClickListener(this); + findViewById(R.id.append).setOnClickListener(this); + findViewById(R.id.startTimer).setOnClickListener(this); + findViewById(R.id.synAddValue).setOnClickListener(this); + findViewById(R.id.synGetInfo).setOnClickListener(this); + findViewById(R.id.asynAddValue).setOnClickListener(this); + findViewById(R.id.asynGetInfo).setOnClickListener(this); + findViewById(R.id.hasMethodAddValue).setOnClickListener(this); + findViewById(R.id.hasMethodXX).setOnClickListener(this); + findViewById(R.id.hasMethodAsynAddValue).setOnClickListener(this); + findViewById(R.id.hasMethodAsynXX).setOnClickListener(this); + + dWebView = findViewById(R.id.webview); + mWebViewEvent = new WebViewEventImpl(dWebView); + mWebViewEvent.setWebContentsDebuggingEnabled(true); + mWebViewEvent.loadUrl("file:///android_asset/native-call-js.html"); } + void showToast(Object o) { Toast.makeText(this, o.toString(), Toast.LENGTH_SHORT).show(); } @@ -49,7 +51,7 @@ void showToast(Object o) { public void onClick(View v) { switch (v.getId()) { case R.id.addValue: - dWebView.callHandler("addValue", new Object[]{3, 4}, new OnReturnValue() { + mWebViewEvent.callHandler("addValue", new Object[]{3, 4}, new OnReturnValue() { @Override public void onValue(Integer retValue) { showToast(retValue); @@ -57,7 +59,7 @@ public void onValue(Integer retValue) { }); break; case R.id.append: - dWebView.callHandler("append", new Object[]{"I", "love", "you"}, new OnReturnValue() { + mWebViewEvent.callHandler("append", new Object[]{"I", "love", "you"}, new OnReturnValue() { @Override public void onValue(String retValue) { showToast(retValue); @@ -65,7 +67,7 @@ public void onValue(String retValue) { }); break; case R.id.startTimer: - dWebView.callHandler("startTimer", new OnReturnValue() { + mWebViewEvent.callHandler("startTimer", new OnReturnValue() { @Override public void onValue(Integer retValue) { showToast(retValue); @@ -73,7 +75,7 @@ public void onValue(Integer retValue) { }); break; case R.id.synAddValue: - dWebView.callHandler("syn.addValue", new Object[]{5, 6}, new OnReturnValue() { + mWebViewEvent.callHandler("syn.addValue", new Object[]{5, 6}, new OnReturnValue() { @Override public void onValue(Integer retValue) { showToast(retValue); @@ -81,7 +83,7 @@ public void onValue(Integer retValue) { }); break; case R.id.synGetInfo: - dWebView.callHandler("syn.getInfo", new OnReturnValue() { + mWebViewEvent.callHandler("syn.getInfo", new OnReturnValue() { @Override public void onValue(JSONObject retValue) { showToast(retValue); @@ -89,7 +91,7 @@ public void onValue(JSONObject retValue) { }); break; case R.id.asynAddValue: - dWebView.callHandler("asyn.addValue", new Object[]{5, 6}, new OnReturnValue() { + mWebViewEvent.callHandler("asyn.addValue", new Object[]{5, 6}, new OnReturnValue() { @Override public void onValue(Integer retValue) { showToast(retValue); @@ -97,7 +99,7 @@ public void onValue(Integer retValue) { }); break; case R.id.asynGetInfo: - dWebView.callHandler("asyn.getInfo", new OnReturnValue() { + mWebViewEvent.callHandler("asyn.getInfo", new OnReturnValue() { @Override public void onValue(JSONObject retValue) { showToast(retValue); @@ -105,7 +107,7 @@ public void onValue(JSONObject retValue) { }); break; case R.id.hasMethodAddValue: - dWebView.hasJavascriptMethod("addValue", new OnReturnValue() { + mWebViewEvent.hasJavascriptMethod("addValue", new OnReturnValue() { @Override public void onValue(Boolean retValue) { showToast(retValue); @@ -113,7 +115,7 @@ public void onValue(Boolean retValue) { }); break; case R.id.hasMethodXX: - dWebView.hasJavascriptMethod("XX", new OnReturnValue() { + mWebViewEvent.hasJavascriptMethod("XX", new OnReturnValue() { @Override public void onValue(Boolean retValue) { showToast(retValue); @@ -121,7 +123,7 @@ public void onValue(Boolean retValue) { }); break; case R.id.hasMethodAsynAddValue: - dWebView.hasJavascriptMethod("asyn.addValue", new OnReturnValue() { + mWebViewEvent.hasJavascriptMethod("asyn.addValue", new OnReturnValue() { @Override public void onValue(Boolean retValue) { showToast(retValue); @@ -129,7 +131,7 @@ public void onValue(Boolean retValue) { }); break; case R.id.hasMethodAsynXX: - dWebView.hasJavascriptMethod("asyn.XX", new OnReturnValue() { + mWebViewEvent.hasJavascriptMethod("asyn.XX", new OnReturnValue() { @Override public void onValue(Boolean retValue) { showToast(retValue); diff --git a/app/src/main/java/wendu/jsbdemo/JavascriptCallNativeActivity.java b/app/src/main/java/wendu/jsbdemo/JavascriptCallNativeActivity.java index e85d458..b91b157 100644 --- a/app/src/main/java/wendu/jsbdemo/JavascriptCallNativeActivity.java +++ b/app/src/main/java/wendu/jsbdemo/JavascriptCallNativeActivity.java @@ -4,18 +4,22 @@ import android.support.v7.app.AppCompatActivity; import wendu.dsbridge.DWebView; +import wendu.dsbridge.WebViewEventImpl; public class JavascriptCallNativeActivity extends AppCompatActivity { + public WebViewEventImpl mWebViewEvent; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_js_call_native); - final DWebView dwebView= (DWebView) findViewById(R.id.webview); + DWebView dwebView = findViewById(R.id.webview); // set debug mode - DWebView.setWebContentsDebuggingEnabled(true); - dwebView.addJavascriptObject(new JsApi(), null); - dwebView.addJavascriptObject(new JsEchoApi(),"echo"); - dwebView.loadUrl("file:///android_asset/js-call-native.html"); + mWebViewEvent = new WebViewEventImpl(dwebView); + mWebViewEvent.setWebContentsDebuggingEnabled(true); + mWebViewEvent.addJavascriptObject(new JsApi(), null); + mWebViewEvent.addJavascriptObject(new JsEchoApi(), "echo"); + mWebViewEvent.loadUrl("file:///android_asset/js-call-native.html"); } } diff --git a/app/src/main/java/wendu/jsbdemo/MainActivity.java b/app/src/main/java/wendu/jsbdemo/MainActivity.java index d8b4a81..fd7383f 100644 --- a/app/src/main/java/wendu/jsbdemo/MainActivity.java +++ b/app/src/main/java/wendu/jsbdemo/MainActivity.java @@ -3,27 +3,14 @@ import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; -import android.util.Log; import android.view.View; -import com.tencent.smtt.sdk.QbSdk; - public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() { - @Override - public void onCoreInitFinished() { - Log.d("jsbridge","onCoreInitFinished"); - } - @Override - public void onViewInitFinished(boolean b) { - Log.d("jsbridge","onCoreInitFinished"); - } - }); + super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.callJs).setOnClickListener(new View.OnClickListener() { @Override diff --git a/app/src/main/java/wendu/jsbdemo/NetUtils.java b/app/src/main/java/wendu/jsbdemo/NetUtils.java index b3d52c5..ef9d733 100644 --- a/app/src/main/java/wendu/jsbdemo/NetUtils.java +++ b/app/src/main/java/wendu/jsbdemo/NetUtils.java @@ -1,11 +1,12 @@ package wendu.jsbdemo; -import java.net.CookieManager; import org.json.JSONObject; + import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.PrintWriter; +import java.net.CookieManager; import java.net.HttpCookie; import java.net.HttpURLConnection; import java.net.URL; @@ -14,7 +15,6 @@ import java.util.List; import java.util.Map; - import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; diff --git a/app/src/main/java/wendu/jsbdemo/WrokWithFlyioTestActivity.java b/app/src/main/java/wendu/jsbdemo/WrokWithFlyioTestActivity.java index f69de4c..b7668ca 100644 --- a/app/src/main/java/wendu/jsbdemo/WrokWithFlyioTestActivity.java +++ b/app/src/main/java/wendu/jsbdemo/WrokWithFlyioTestActivity.java @@ -8,6 +8,7 @@ import wendu.dsbridge.CompletionHandler; import wendu.dsbridge.DWebView; +import wendu.dsbridge.WebViewEventImpl; public class WrokWithFlyioTestActivity extends AppCompatActivity { @@ -15,8 +16,9 @@ public class WrokWithFlyioTestActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_wrok_with_flyio_test); - DWebView dWebView= (DWebView) findViewById(R.id.webview); - dWebView.addJavascriptObject(new Object(){ + DWebView dWebView = findViewById(R.id.webview); + WebViewEventImpl webViewEvent = new WebViewEventImpl(dWebView); + webViewEvent.addJavascriptObject(new Object() { /** * Note: This method is for Fly.js @@ -26,13 +28,13 @@ protected void onCreate(Bundle savedInstanceState) { * @param handler */ @JavascriptInterface - public void onAjaxRequest(Object requestData, CompletionHandler handler){ + public void onAjaxRequest(Object requestData, CompletionHandler handler) { // Handle ajax request redirected by Fly - AjaxHandler.onAjaxRequest((JSONObject)requestData,handler); + AjaxHandler.onAjaxRequest((JSONObject) requestData, handler); } - },null); + }, null); - dWebView.loadUrl("file:///android_asset/fly.html"); + webViewEvent.loadUrl("file:///android_asset/fly.html"); } } diff --git a/build.gradle b/build.gradle index eecd4af..437dfcf 100644 --- a/build.gradle +++ b/build.gradle @@ -3,9 +3,13 @@ buildscript { repositories { jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' + classpath 'com.android.tools.build:gradle:3.3.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files @@ -14,6 +18,10 @@ buildscript { allprojects { repositories { jcenter() + maven { + url 'https://maven.google.com/' + name 'Google' + } } } task clean(type: Delete) { diff --git a/dsbridge/build.gradle b/dsbridge/build.gradle index 2ea6578..66a61dc 100644 --- a/dsbridge/build.gradle +++ b/dsbridge/build.gradle @@ -1,14 +1,15 @@ apply plugin: 'com.android.library' android { - compileSdkVersion 23 - buildToolsVersion '25.0.0' + compileSdkVersion 28 + buildToolsVersion '28.0.3' defaultConfig { - minSdkVersion 11 - targetSdkVersion 23 + minSdkVersion 14 + targetSdkVersion 28 versionCode 1 versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } @@ -18,18 +19,21 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - lintOptions { abortOnError false } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { - compile fileTree(include: ['*.jar'], dir: 'libs') - androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + implementation fileTree(include: ['*.jar'], dir: 'libs') + androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) - compile 'com.android.support:appcompat-v7:23.4.0' - testCompile 'junit:junit:4.12' - compile files('src/libs/tbs_sdk_thirdapp_v3.6.0.1325_43613_sharewithdownload_withoutGame_obfs_20180807_151115.jar') + implementation 'com.android.support:appcompat-v7:28.0.0' + testImplementation 'junit:junit:4.12' + api files('src/libs/tbs_sdk_thirdapp_v3.6.0.1325_43613_sharewithdownload_withoutGame_obfs_20180807_151115.jar') } diff --git a/dsbridge/src/main/java/wendu/dsbridge/CallInfo.java b/dsbridge/src/main/java/wendu/dsbridge/CallInfo.java new file mode 100644 index 0000000..161e209 --- /dev/null +++ b/dsbridge/src/main/java/wendu/dsbridge/CallInfo.java @@ -0,0 +1,33 @@ +package wendu.dsbridge; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Arrays; + +public class CallInfo { + private String data; + protected int callbackId; + private String method; + + CallInfo(String handlerName, int id, Object[] args) { + if (args == null) args = new Object[0]; + data = new JSONArray(Arrays.asList(args)).toString(); + callbackId = id; + method = handlerName; + } + + @Override + public String toString() { + JSONObject jo = new JSONObject(); + try { + jo.put("method", method); + jo.put("callbackId", callbackId); + jo.put("data", data); + } catch (JSONException e) { + e.printStackTrace(); + } + return jo.toString(); + } +} \ No newline at end of file diff --git a/dsbridge/src/main/java/wendu/dsbridge/DWebView.java b/dsbridge/src/main/java/wendu/dsbridge/DWebView.java index 0513243..619eebc 100644 --- a/dsbridge/src/main/java/wendu/dsbridge/DWebView.java +++ b/dsbridge/src/main/java/wendu/dsbridge/DWebView.java @@ -1,960 +1,33 @@ package wendu.dsbridge; -import android.annotation.TargetApi; -import android.app.Activity; -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.AttributeSet; -import android.util.Log; -import android.view.Gravity; -import android.view.View; -import android.view.ViewGroup; -import android.webkit.JavascriptInterface; -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 org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.io.File; -import java.lang.ref.WeakReference; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -import static android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW; - /** * Created by du on 16/12/29. */ -public class DWebView extends WebView { - private static final String BRIDGE_NAME = "_dsbridge"; - private static final String LOG_TAG = "dsBridge"; - private static boolean isDebug = false; - private Map javaScriptNamespaceInterfaces = new HashMap(); - private String APP_CACHE_DIRNAME; - int callID = 0; - private WebChromeClient webChromeClient; - private volatile boolean alertBoxBlock = true; - private JavascriptCloseWindowListener javascriptCloseWindowListener = null; - private ArrayList callInfoList; - private InnerJavascriptInterface innerJavascriptInterface = new InnerJavascriptInterface(); - private Handler mainHandler = new Handler(Looper.getMainLooper()); - - - class InnerJavascriptInterface { - - private void PrintDebugInfo(String error) { - Log.d(LOG_TAG, error); - if (isDebug) { - evaluateJavascript(String.format("alert('%s')", "DEBUG ERR MSG:\\n" + error.replaceAll("\\'", "\\\\'"))); - } - } - - @Keep - @JavascriptInterface - public String call(String methodName, String argStr) { - String error = "Js bridge called, but can't find a corresponded " + - "JavascriptInterface object , please check your code!"; - String[] nameStr = parseNamespace(methodName.trim()); - methodName = nameStr[1]; - Object jsb = javaScriptNamespaceInterfaces.get(nameStr[0]); - JSONObject ret = new JSONObject(); - try { - ret.put("code", -1); - } catch (JSONException e) { - e.printStackTrace(); - } - if (jsb == null) { - PrintDebugInfo(error); - return ret.toString(); - } - Object arg = null; - Method method = null; - String callback = null; - - try { - JSONObject args = new JSONObject(argStr); - if (args.has("_dscbstub")) { - callback = args.getString("_dscbstub"); - } - if (args.has("data")) { - arg = args.get("data"); - } - - } catch (JSONException e) { - error = String.format("The argument of \"%s\" must be a JSON object string!", methodName); - PrintDebugInfo(error); - e.printStackTrace(); - return ret.toString(); - } - - - Class cls = jsb.getClass(); - boolean asyn = false; - try { - method = cls.getMethod(methodName, - new Class[]{Object.class, CompletionHandler.class}); - asyn = true; - } catch (Exception e) { - try { - method = cls.getMethod(methodName, new Class[]{Object.class}); - } catch (Exception ex) { - - } - } - - if (method == null) { - error = "Not find method \"" + methodName + "\" implementation! please check if the signature or namespace of the method is right "; - PrintDebugInfo(error); - return ret.toString(); - } - - JavascriptInterface annotation = method.getAnnotation(JavascriptInterface.class); - if (annotation == null) { - error = "Method " + methodName + " is not invoked, since " + - "it is not declared with JavascriptInterface annotation! "; - PrintDebugInfo(error); - return ret.toString(); - } - - Object retData; - method.setAccessible(true); - try { - if (asyn) { - final String cb = callback; - method.invoke(jsb, arg, new CompletionHandler() { - - @Override - public void complete(Object retValue) { - complete(retValue, true); - } - - @Override - public void complete() { - complete(null, true); - } - - @Override - public void setProgressData(Object value) { - complete(value, false); - } +public class DWebView extends WebView implements WebViewEvent.View { - private void complete(Object retValue, boolean complete) { - try { - JSONObject ret = new JSONObject(); - ret.put("code", 0); - ret.put("data", retValue); - //retValue = URLEncoder.encode(ret.toString(), "UTF-8").replaceAll("\\+", "%20"); - if (cb != null) { - //String script = String.format("%s(JSON.parse(decodeURIComponent(\"%s\")).data);", cb, retValue); - String script = String.format("%s(%s.data);", cb, ret.toString()); - if (complete) { - script += "delete window." + cb; - } - evaluateJavascript(script); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - }); - } else { - retData = method.invoke(jsb, arg); - ret.put("code", 0); - ret.put("data", retData); - return ret.toString(); - } - } catch (Exception e) { - e.printStackTrace(); - error = String.format("Call failed:The parameter of \"%s\" in Java is invalid.", methodName); - PrintDebugInfo(error); - return ret.toString(); - } - return ret.toString(); - } - - } - - - Map handlerMap = new HashMap<>(); - - public interface JavascriptCloseWindowListener { - /** - * @return If true, close the current activity, otherwise, do nothing. - */ - boolean onClose(); - } - - @Deprecated - public interface FileChooser { - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - void openFileChooser(ValueCallback valueCallback, String acceptType); - - @TargetApi(Build.VERSION_CODES.JELLY_BEAN) - void openFileChooser(ValueCallback valueCallback, - String acceptType, String capture); - } - - public DWebView(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } public DWebView(Context context) { super(context); - init(); - } - - /** - * Set debug mode. if in debug mode, some errors will be prompted by a dialog - * and the exception caused by the native handlers will not be captured. - * - * @param enabled - */ - public static void setWebContentsDebuggingEnabled(boolean enabled) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - WebView.setWebContentsDebuggingEnabled(enabled); - } - isDebug = enabled; - } - - @Keep - private void init() { - APP_CACHE_DIRNAME = getContext().getFilesDir().getAbsolutePath() + "/webcache"; - WebSettings settings = getSettings(); - settings.setDomStorageEnabled(true); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - CookieManager.getInstance().setAcceptThirdPartyCookies(this, 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); - super.setWebChromeClient(mWebChromeClient); - addInternalJavascriptObject(); - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) { - super.addJavascriptInterface(innerJavascriptInterface, BRIDGE_NAME); - } else { - // add bridge tag in lower android version - settings.setUserAgentString(settings.getUserAgentString() + " _dsbridge"); - } - } - private 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 Object() { - - @Keep - @JavascriptInterface - public boolean hasNativeMethod(Object args) throws JSONException { - - JSONObject jsonObject = (JSONObject) args; - String methodName = jsonObject.getString("name").trim(); - String type = jsonObject.getString("type").trim(); - String[] nameStr = parseNamespace(methodName); - Object jsb = javaScriptNamespaceInterfaces.get(nameStr[0]); - if (jsb != null) { - Class cls = jsb.getClass(); - boolean asyn = false; - Method method = null; - try { - method = cls.getMethod(nameStr[1], - new Class[]{Object.class, CompletionHandler.class}); - asyn = true; - } catch (Exception e) { - try { - method = cls.getMethod(nameStr[1], new Class[]{Object.class}); - } catch (Exception ex) { - - } - } - if (method != null) { - JavascriptInterface annotation = method.getAnnotation(JavascriptInterface.class); - if (annotation != null) { - if ("all".equals(type) || (asyn && "asyn".equals(type) || (!asyn && "syn".equals(type)))) { - return true; - } - } - } - } - return false; - } - - @Keep - @JavascriptInterface - public String closePage(Object object) throws JSONException { - runOnMainThread(new Runnable() { - @Override - public void run() { - if (javascriptCloseWindowListener == null - || javascriptCloseWindowListener.onClose()) { - Context context = getContext(); - if (context instanceof Activity) { - ((Activity) getContext()).onBackPressed(); - } - } - } - }); - return null; - } - - @Keep - @JavascriptInterface - public void disableJavascriptDialogBlock(Object object) throws JSONException { - JSONObject jsonObject = (JSONObject) object; - alertBoxBlock = !jsonObject.getBoolean("disable"); - } - - @Keep - @JavascriptInterface - public void dsinit(Object jsonObject) { - DWebView.this.dispatchStartupQueue(); - } - - @Keep - @JavascriptInterface - public void returnValue(final Object obj) throws JSONException { - runOnMainThread(new Runnable() { - @Override - public void run() { - JSONObject jsonObject = (JSONObject) obj; - Object data = null; - try { - int id = jsonObject.getInt("id"); - boolean isCompleted = jsonObject.getBoolean("complete"); - OnReturnValue handler = handlerMap.get(id); - if (jsonObject.has("data")) { - data = jsonObject.get("data"); - } - if (handler != null) { - handler.onValue(data); - if (isCompleted) { - handlerMap.remove(id); - } - } - } catch (JSONException e) { - e.printStackTrace(); - } - } - }); - } - - }, "_dsb"); - } - - private void _evaluateJavascript(String script) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - DWebView.super.evaluateJavascript(script, null); - } else { - super.loadUrl("javascript:" + script); - } - } - - /** - * This method can be called in any thread, and if it is not called in the main thread, - * it will be automatically distributed to the main thread. - * - * @param script - */ - public void evaluateJavascript(final String script) { - runOnMainThread(new Runnable() { - @Override - public void run() { - _evaluateJavascript(script); - } - }); - } - - /** - * This method can be called in any thread, and if it is not called in the main thread, - * it will be automatically distributed to the main thread. - * - * @param url - */ - @Override - public void loadUrl(final String url) { - runOnMainThread(new Runnable() { - @Override - public void run() { - callInfoList = new ArrayList<>(); - DWebView.super.loadUrl(url); - } - }); - } - - /** - * This method can be called in any thread, and if it is not called in the main thread, - * it will be automatically distributed to the main thread. - * - * @param url - * @param additionalHttpHeaders - */ - @Override - public void loadUrl(final String url, final Map additionalHttpHeaders) { - runOnMainThread(new Runnable() { - @Override - public void run() { - callInfoList = new ArrayList<>(); - DWebView.super.loadUrl(url, additionalHttpHeaders); - } - }); - } - - @Override - public void reload() { - runOnMainThread(new Runnable() { - @Override - public void run() { - callInfoList = new ArrayList<>(); - DWebView.super.reload(); - } - }); - } - - /** - * set a listener for javascript closing the current activity. - */ - public void setJavascriptCloseWindowListener(JavascriptCloseWindowListener listener) { - javascriptCloseWindowListener = listener; - } - - - private class CallInfo { - public CallInfo(String handlerName, int id, Object[] args) { - if (args == null) args = new Object[0]; - data = new JSONArray(Arrays.asList(args)).toString(); - callbackId = id; - method = handlerName; - } - - @Override - public String toString() { - JSONObject jo = new JSONObject(); - try { - jo.put("method", method); - jo.put("callbackId", callbackId); - jo.put("data", data); - } catch (JSONException e) { - e.printStackTrace(); - } - return jo.toString(); - } - - public String data = null; - public int callbackId; - public String method; - } - - private synchronized void dispatchStartupQueue() { - if (callInfoList != null) { - for (CallInfo info : callInfoList) { - dispatchJavascriptCall(info); - } - callInfoList = null; - } - } - - 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); - } - - /** - * Add a java object which implemented the javascript interfaces to dsBridge with namespace. - * Remove the object using {@link #removeJavascriptObject(String) removeJavascriptObject(String)} - * - * @param object - * @param namespace if empty, the object have no namespace. - */ - public void addJavascriptObject(Object object, String namespace) { - if (namespace == null) { - namespace = ""; - } - if (object != null) { - javaScriptNamespaceInterfaces.put(namespace, object); - } - } - - /** - * 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 DWebView(Context context, AttributeSet attrs) { + super(context, attrs); } - @Override - public void setWebChromeClient(WebChromeClient client) { - webChromeClient = client; + public DWebView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); } - 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(View view, IX5WebChromeClient.CustomViewCallback callback) { - if (webChromeClient != null) { - webChromeClient.onShowCustomView(view, callback); - } else { - super.onShowCustomView(view, callback); - } - } - - @Override - public void onShowCustomView(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(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(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(getContext()); - editText.setText(defaultValue); - if (defaultValue != null) { - editText.setSelection(defaultValue.length()); - } - float dpi = 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(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 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); - } - } - - }; - @Override - public void clearCache(boolean includeDiskFiles) { - super.clearCache(includeDiskFiles); - CookieManager.getInstance().removeAllCookie(); - Context context = 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()) { - deleteFile(webviewCacheDir); - } - - if (appCacheDir.exists()) { - deleteFile(appCacheDir); - } - } - - public void deleteFile(File file) { - if (file.exists()) { - if (file.isFile()) { - file.delete(); - } else if (file.isDirectory()) { - File files[] = file.listFiles(); - for (int i = 0; i < files.length; i++) { - deleteFile(files[i]); - } - } - file.delete(); - } else { - Log.e("Webview", "delete file no exists " + file.getAbsolutePath()); - } - } - - private void runOnMainThread(Runnable runnable) { - if (Looper.getMainLooper() == Looper.myLooper()) { - runnable.run(); - return; - } - mainHandler.post(runnable); + public DWebView getWebView() { + return this; } - } diff --git a/dsbridge/src/main/java/wendu/dsbridge/FileChooser.java b/dsbridge/src/main/java/wendu/dsbridge/FileChooser.java new file mode 100644 index 0000000..2d90d80 --- /dev/null +++ b/dsbridge/src/main/java/wendu/dsbridge/FileChooser.java @@ -0,0 +1,16 @@ +package wendu.dsbridge; + +import android.annotation.TargetApi; +import android.net.Uri; +import android.os.Build; +import android.webkit.ValueCallback; + + +public interface FileChooser { + @TargetApi(Build.VERSION_CODES.HONEYCOMB) + void openFileChooser(ValueCallback valueCallback, String acceptType); + + @TargetApi(Build.VERSION_CODES.JELLY_BEAN) + void openFileChooser(ValueCallback valueCallback, + String acceptType, String capture); +} \ No newline at end of file diff --git a/dsbridge/src/main/java/wendu/dsbridge/FileUtils.java b/dsbridge/src/main/java/wendu/dsbridge/FileUtils.java new file mode 100644 index 0000000..a72a192 --- /dev/null +++ b/dsbridge/src/main/java/wendu/dsbridge/FileUtils.java @@ -0,0 +1,24 @@ +package wendu.dsbridge; + +import android.util.Log; + +import java.io.File; + + +public class FileUtils { + public static void deleteFile(File file) { + if (file.exists()) { + if (file.isFile()) { + file.delete(); + } else if (file.isDirectory()) { + File files[] = file.listFiles(); + for (int i = 0; i < files.length; i++) { + deleteFile(files[i]); + } + } + file.delete(); + } else { + Log.e("Webview", "delete file no exists " + file.getAbsolutePath()); + } + } +} diff --git a/dsbridge/src/main/java/wendu/dsbridge/InnerJavascriptInterface.java b/dsbridge/src/main/java/wendu/dsbridge/InnerJavascriptInterface.java new file mode 100644 index 0000000..40e545f --- /dev/null +++ b/dsbridge/src/main/java/wendu/dsbridge/InnerJavascriptInterface.java @@ -0,0 +1,155 @@ +package wendu.dsbridge; + +import android.support.annotation.Keep; +import android.util.Log; +import android.webkit.JavascriptInterface; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Method; + +public class InnerJavascriptInterface { + + private static final String LOG_TAG = "dsBridge"; + + private WebViewEventImpl mWebViewEvent; + + public InnerJavascriptInterface(WebViewEventImpl webViewEvent) { + mWebViewEvent = webViewEvent; + } + + private void PrintDebugInfo(String error) { + Log.d(LOG_TAG, error); + if (mWebViewEvent.isDebug) { + mWebViewEvent.evaluateJavascript(String.format("alert('%s')", "DEBUG ERR MSG:\\n" + error.replaceAll("\\'", "\\\\'"))); + } + } + + @Keep + @JavascriptInterface + public String call(String methodName, String argStr) { + String error = "Js bridge called, but can't find a corresponded " + + "JavascriptInterface object , please check your code!"; + String[] nameStr = mWebViewEvent.parseNamespace(methodName.trim()); + methodName = nameStr[1]; + Object jsb = mWebViewEvent.javaScriptNamespaceInterfaces.get(nameStr[0]); + JSONObject ret = new JSONObject(); + try { + ret.put("code", -1); + } catch (JSONException e) { + e.printStackTrace(); + } + if (jsb == null) { + PrintDebugInfo(error); + return ret.toString(); + } + Object arg = null; + Method method = null; + String callback = null; + + try { + JSONObject args = new JSONObject(argStr); + if (args.has("_dscbstub")) { + callback = args.getString("_dscbstub"); + } + if (args.has("data")) { + arg = args.get("data"); + } + } catch (JSONException e) { + error = String.format("The argument of \"%s\" must be a JSON object string!", methodName); + PrintDebugInfo(error); + e.printStackTrace(); + return ret.toString(); + } + + + Class cls = jsb.getClass(); + boolean asyn = false; + try { + method = cls.getMethod(methodName, + Object.class, CompletionHandler.class); + asyn = true; + } catch (Exception e) { + try { + method = cls.getMethod(methodName, Object.class); + } catch (Exception ex) { + e.printStackTrace(); + } + } + + if (method == null) { + error = "Not find method \"" + methodName + "\" implementation! please check if the signature or namespace of the method is right "; + PrintDebugInfo(error); + return ret.toString(); + } + + + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + JavascriptInterface annotation = method.getAnnotation(JavascriptInterface.class); + if (annotation == null) { + error = "Method " + methodName + " is not invoked, since " + + "it is not declared with JavascriptInterface annotation! "; + PrintDebugInfo(error); + return ret.toString(); + } + } + + Object retData; + method.setAccessible(true); + try { + if (asyn) { + final String cb = callback; + method.invoke(jsb, arg, new CompletionHandler() { + + @Override + public void complete(Object retValue) { + complete(retValue, true); + } + + @Override + public void complete() { + complete(null, true); + } + + @Override + public void setProgressData(Object value) { + complete(value, false); + } + + private void complete(Object retValue, boolean complete) { + try { + JSONObject ret = new JSONObject(); + ret.put("code", 0); + ret.put("data", retValue); + //retValue = URLEncoder.encode(ret.toString(), "UTF-8").replaceAll("\\+", "%20"); + if (cb != null) { + //String script = String.format("%s(JSON.parse(decodeURIComponent(\"%s\")).data);", cb, retValue); + String script = String.format("%s(%s.data);", cb, ret.toString()); + if (complete) { + script += "delete window." + cb; + } + //Log.d(LOG_TAG, "complete " + script); + mWebViewEvent.evaluateJavascript(script); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } else { + retData = method.invoke(jsb, arg); + ret.put("code", 0); + ret.put("data", retData); + return ret.toString(); + } + } catch (Exception e) { + e.printStackTrace(); + error = String.format("Call failed:The parameter of \"%s\" in Java is invalid.", methodName); + PrintDebugInfo(error); + return ret.toString(); + } + return ret.toString(); + } + +} \ No newline at end of file diff --git a/dsbridge/src/main/java/wendu/dsbridge/JavascriptCloseWindowListener.java b/dsbridge/src/main/java/wendu/dsbridge/JavascriptCloseWindowListener.java new file mode 100644 index 0000000..b818108 --- /dev/null +++ b/dsbridge/src/main/java/wendu/dsbridge/JavascriptCloseWindowListener.java @@ -0,0 +1,8 @@ +package wendu.dsbridge; + +public interface JavascriptCloseWindowListener { + /** + * @return If true, close the current activity, otherwise, do nothing. + */ + boolean onClose(); + } \ No newline at end of file diff --git a/dsbridge/src/main/java/wendu/dsbridge/JsBridgeObject.java b/dsbridge/src/main/java/wendu/dsbridge/JsBridgeObject.java new file mode 100644 index 0000000..5c0a952 --- /dev/null +++ b/dsbridge/src/main/java/wendu/dsbridge/JsBridgeObject.java @@ -0,0 +1,120 @@ +package wendu.dsbridge; + +import android.app.Activity; +import android.content.Context; +import android.support.annotation.Keep; +import android.webkit.JavascriptInterface; + +import org.json.JSONException; +import org.json.JSONObject; + +import java.lang.reflect.Method; + + +public class JsBridgeObject { + + private WebViewEventImpl mWebViewEvent; + + public JsBridgeObject(WebViewEventImpl webViewEvent) { + this.mWebViewEvent = webViewEvent; + } + + @Keep + @JavascriptInterface + public boolean hasNativeMethod(Object args) throws JSONException { + JSONObject jsonObject = (JSONObject) args; + String methodName = jsonObject.getString("name").trim(); + String type = jsonObject.getString("type").trim(); + String[] nameStr = mWebViewEvent.parseNamespace(methodName); + Object jsb = mWebViewEvent.javaScriptNamespaceInterfaces.get(nameStr[0]); + if (jsb != null) { + Class cls = jsb.getClass(); + boolean asyn = false; + Method method = null; + try { + method = cls.getMethod(nameStr[1], + Object.class, CompletionHandler.class); + asyn = true; + } catch (Exception e) { + try { + method = cls.getMethod(nameStr[1], Object.class); + } catch (Exception ex) { + + } + } + if (method != null) { + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN_MR1) { + JavascriptInterface annotation = method.getAnnotation(JavascriptInterface.class); + if (annotation == null) { + return false; + } + } + if ("all".equals(type) || (asyn && "asyn".equals(type) || (!asyn && "syn".equals(type)))) { + return true; + } + + } + } + return false; + } + + @Keep + @JavascriptInterface + public String closePage(Object object) throws JSONException { + mWebViewEvent.runOnMainThread(new Runnable() { + @Override + public void run() { + if (mWebViewEvent.javascriptCloseWindowListener == null + || mWebViewEvent.javascriptCloseWindowListener.onClose()) { + Context context = mWebViewEvent.getWebView().getContext(); + if (context instanceof Activity) { + ((Activity) context).onBackPressed(); + } + } + } + }); + return null; + } + + @Keep + @JavascriptInterface + public void disableJavascriptDialogBlock(Object object) throws JSONException { + JSONObject jsonObject = (JSONObject) object; + mWebViewEvent.alertBoxBlock = !jsonObject.getBoolean("disable"); + } + + @Keep + @JavascriptInterface + public void dsinit(Object jsonObject) { + mWebViewEvent.dispatchStartupQueue(); + } + + @Keep + @JavascriptInterface + public void returnValue(final Object obj) { + mWebViewEvent.runOnMainThread(new Runnable() { + @Override + public void run() { + JSONObject jsonObject = (JSONObject) obj; + Object data = null; + try { + int id = jsonObject.getInt("id"); + boolean isCompleted = jsonObject.getBoolean("complete"); + OnReturnValue handler = mWebViewEvent.handlerMap.get(id); + if (jsonObject.has("data")) { + data = jsonObject.get("data"); + } + if (handler != null) { + handler.onValue(data); + if (isCompleted) { + mWebViewEvent.handlerMap.remove(id); + } + } + } catch (JSONException e) { + e.printStackTrace(); + } + } + }); + } + +} diff --git a/dsbridge/src/main/java/wendu/dsbridge/OnReturnValue.java b/dsbridge/src/main/java/wendu/dsbridge/OnReturnValue.java index 1cc7d2d..9ab9111 100644 --- a/dsbridge/src/main/java/wendu/dsbridge/OnReturnValue.java +++ b/dsbridge/src/main/java/wendu/dsbridge/OnReturnValue.java @@ -5,5 +5,5 @@ */ public interface OnReturnValue { - 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