() {
50 | @Override
51 | public void onValue(Integer retValue) {
52 | assertEquals(7,retValue.intValue());
53 | }
54 | });
55 | SystemClock.sleep(5000);
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/wendu/jsbdemo/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package wendu.jsbdemo;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("wendu.jsbdemo", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/assets/dsbridge.js:
--------------------------------------------------------------------------------
1 | var bridge = {
2 | default:this,// for typescript
3 | call: function (method, args, cb) {
4 | var ret = '';
5 | if (typeof args == 'function') {
6 | cb = args;
7 | args = {};
8 | }
9 | var arg={data:args===undefined?null:args}
10 | if (typeof cb == 'function') {
11 | var cbName = 'dscb' + window.dscb++;
12 | window[cbName] = cb;
13 | arg['_dscbstub'] = cbName;
14 | }
15 | arg = JSON.stringify(arg)
16 |
17 | //if in webview that dsBridge provided, call!
18 | if(window._dsbridge){
19 | ret= _dsbridge.call(method, arg)
20 | }else if(window._dswk||navigator.userAgent.indexOf("_dsbridge")!=-1){
21 | ret = prompt("_dsbridge=" + method, arg);
22 | }
23 |
24 | return JSON.parse(ret||'{}').data
25 | },
26 | register: function (name, fun, asyn) {
27 | var q = asyn ? window._dsaf : window._dsf
28 | if (!window._dsInit) {
29 | window._dsInit = true;
30 | //notify native that js apis register successfully on next event loop
31 | setTimeout(function () {
32 | bridge.call("_dsb.dsinit");
33 | }, 0)
34 | }
35 | if (typeof fun == "object") {
36 | q._obs[name] = fun;
37 | } else {
38 | q[name] = fun
39 | }
40 | },
41 | registerAsyn: function (name, fun) {
42 | this.register(name, fun, true);
43 | },
44 | hasNativeMethod: function (name, type) {
45 | return this.call("_dsb.hasNativeMethod", {name: name, type:type||"all"});
46 | },
47 | disableJavascriptDialogBlock: function (disable) {
48 | this.call("_dsb.disableJavascriptDialogBlock", {
49 | disable: disable !== false
50 | })
51 | }
52 | };
53 |
54 | !function () {
55 | if (window._dsf) return;
56 | var ob = {
57 | _dsf: {
58 | _obs: {}
59 | },
60 | _dsaf: {
61 | _obs: {}
62 | },
63 | dscb: 0,
64 | dsBridge: bridge,
65 | close: function () {
66 | bridge.call("_dsb.closePage")
67 | },
68 | _handleMessageFromNative: function (info) {
69 | var arg = JSON.parse(info.data);
70 | var ret = {
71 | id: info.callbackId,
72 | complete: true
73 | }
74 | var f = this._dsf[info.method];
75 | var af = this._dsaf[info.method]
76 | var callSyn = function (f, ob) {
77 | ret.data = f.apply(ob, arg)
78 | bridge.call("_dsb.returnValue", ret)
79 | }
80 | var callAsyn = function (f, ob) {
81 | arg.push(function (data, complete) {
82 | ret.data = data;
83 | ret.complete = complete!==false;
84 | bridge.call("_dsb.returnValue", ret)
85 | })
86 | f.apply(ob, arg)
87 | }
88 | if (f) {
89 | callSyn(f, this._dsf);
90 | } else if (af) {
91 | callAsyn(af, this._dsaf);
92 | } else {
93 | //with namespace
94 | var name = info.method.split('.');
95 | if (name.length<2) return;
96 | var method=name.pop();
97 | var namespace=name.join('.')
98 | var obs = this._dsf._obs;
99 | var ob = obs[namespace] || {};
100 | var m = ob[method];
101 | if (m && typeof m == "function") {
102 | callSyn(m, ob);
103 | return;
104 | }
105 | obs = this._dsaf._obs;
106 | ob = obs[namespace] || {};
107 | m = ob[method];
108 | if (m && typeof m == "function") {
109 | callAsyn(m, ob);
110 | return;
111 | }
112 | }
113 | }
114 | }
115 | for (var attr in ob) {
116 | window[attr] = ob[attr]
117 | }
118 | bridge.register("_hasJavascriptMethod", function (method, tag) {
119 | var name = method.split('.')
120 | if(name.length<2) {
121 | return !!(_dsf[name]||_dsaf[name])
122 | }else{
123 | // with namespace
124 | var method=name.pop()
125 | var namespace=name.join('.')
126 | var ob=_dsf._obs[namespace]||_dsaf._obs[namespace]
127 | return ob&&!!ob[method]
128 | }
129 | })
130 | }();
131 |
132 | module.exports = bridge;
--------------------------------------------------------------------------------
/app/src/main/assets/fly.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
38 |
39 |
40 |
Fly.js supports forwarding the http request (ajax) to Native through any Javascript bridge, And fly.js has already provide the dsBridge adapter. Because the Native side has no the same-origin policy restriction, fly.js can request any resource from any domain.
41 |
42 | Get home page source code of baidu.com
43 | Show the logo of github
44 |
45 | Close window
46 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/app/src/main/assets/js-call-native.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | DSBridge Test
6 |
7 |
8 |
9 |
10 |
11 |
12 |
30 |
31 | Synchronous call
32 | Asynchronous call
33 | Sync call without argument
34 | Async call without argument
35 | echo.syn
36 | echo.asyn
37 | Stress test,2K times consecutive asynchronous API calls
38 | Never call because without @JavascriptInterface annotation ( This test is
39 | just for Android ,should be ignored in IOS )
40 |
41 | call progress
42 | hasNativeMethod("xx")
43 | hasNativeMethod("testSyn")
44 |
45 |
107 |
108 |
109 |
--------------------------------------------------------------------------------
/app/src/main/assets/native-call-js.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | DSBridge Test
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/app/src/main/java/wendu/jsbdemo/AjaxHandler.java:
--------------------------------------------------------------------------------
1 | package wendu.jsbdemo;
2 |
3 | import android.util.Base64;
4 |
5 | import org.json.JSONObject;
6 |
7 | import java.io.IOException;
8 | import java.util.HashMap;
9 | import java.util.Iterator;
10 | import java.util.List;
11 | import java.util.Map;
12 | import java.util.concurrent.TimeUnit;
13 |
14 | import okhttp3.Call;
15 | import okhttp3.Callback;
16 | import okhttp3.MediaType;
17 | import okhttp3.OkHttpClient;
18 | import okhttp3.Request;
19 | import okhttp3.RequestBody;
20 | import okhttp3.Response;
21 | import wendu.dsbridge.CompletionHandler;
22 |
23 | /**
24 | * Created by du on 2017/10/31.
25 | *
26 | * This class handles the Ajax requests forwarded by fly.js in DWebView
27 | * More about fly.js see https://github.com/wendux/fly
28 | */
29 |
30 | public class AjaxHandler {
31 | public static void onAjaxRequest(final JSONObject requestData, final CompletionHandler handler){
32 |
33 | // Define response structure
34 | final Map responseData=new HashMap<>();
35 | responseData.put("statusCode",0);
36 |
37 | try {
38 | int timeout =requestData.getInt("timeout");
39 | // Create a okhttp instance and set timeout
40 | final OkHttpClient okHttpClient = new OkHttpClient
41 | .Builder()
42 | .connectTimeout(timeout, TimeUnit.MILLISECONDS)
43 | .build();
44 |
45 | // Determine whether you need to encode the response result.
46 | // And encode when responseType is stream.
47 | String contentType="";
48 | boolean encode=false;
49 | String responseType=requestData.optString("responseType",null);
50 | if(responseType!=null&&responseType.equals("stream")){
51 | encode=true;
52 | }
53 |
54 | Request.Builder rb= new Request.Builder();
55 | rb.url(requestData.getString("url"));
56 | JSONObject headers=requestData.getJSONObject("headers");
57 |
58 | // Set request headers
59 | Iterator iterator = headers.keys();
60 | while(iterator.hasNext()){
61 | String key = (String) iterator.next();
62 | String value = headers.getString(key);
63 | String lKey=key.toLowerCase();
64 | if(lKey.equals("cookie")){
65 | // Here you can use CookieJar to manage cookie in a unified way with you native code.
66 | continue;
67 | }
68 | if(lKey.toLowerCase().equals("content-type")){
69 | contentType=value;
70 | }
71 | rb.header(key,value);
72 | }
73 |
74 | // Create request body
75 | if(requestData.getString("method").equals("POST")){
76 | RequestBody requestBody=RequestBody
77 | .create(MediaType.parse(contentType),requestData.getString("data"));
78 | rb.post(requestBody) ;
79 | }
80 | // Create and send HTTP requests
81 | Call call=okHttpClient.newCall(rb.build());
82 | final boolean finalEncode = encode;
83 | call.enqueue(new Callback() {
84 | @Override
85 | public void onFailure(Call call, IOException e) {
86 | responseData.put("responseText",e.getMessage());
87 | handler.complete(new JSONObject(responseData).toString());
88 | }
89 |
90 | @Override
91 | public void onResponse(Call call, Response response) throws IOException {
92 | String data;
93 | // If encoding is needed, the result is encoded by Base64 and returned
94 | if(finalEncode){
95 | data= Base64.encodeToString(response.body().bytes(),Base64.DEFAULT);
96 | }else{
97 | data=response.body().string();
98 | }
99 | responseData.put("responseText",data);
100 | responseData.put("statusCode",response.code());
101 | responseData.put("statusMessage",response.message());
102 | Map> responseHeaders= response.headers().toMultimap();
103 | responseHeaders.remove(null);
104 | responseData.put("headers",responseHeaders);
105 | handler.complete(new JSONObject(responseData).toString());
106 | }
107 | });
108 |
109 | }catch (Exception e){
110 | responseData.put("responseText",e.getMessage());
111 | handler.complete(new JSONObject(responseData).toString());
112 | }
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/app/src/main/java/wendu/jsbdemo/CallJavascriptActivity.java:
--------------------------------------------------------------------------------
1 | package wendu.jsbdemo;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.view.View;
6 | import android.widget.Toast;
7 |
8 | import org.json.JSONObject;
9 |
10 | import wendu.dsbridge.DWebView;
11 | import wendu.dsbridge.OnReturnValue;
12 |
13 | public class CallJavascriptActivity extends AppCompatActivity implements View.OnClickListener {
14 |
15 | DWebView dWebView;
16 |
17 | public T getView(int viewId) {
18 | View view = findViewById(viewId);
19 | return (T) view;
20 | }
21 |
22 | @Override
23 | protected void onCreate(Bundle savedInstanceState) {
24 | super.onCreate(savedInstanceState);
25 | setContentView(R.layout.activity_call_javascript);
26 | getView(R.id.addValue).setOnClickListener(this);
27 | getView(R.id.append).setOnClickListener(this);
28 | getView(R.id.startTimer).setOnClickListener(this);
29 | getView(R.id.synAddValue).setOnClickListener(this);
30 | getView(R.id.synGetInfo).setOnClickListener(this);
31 | getView(R.id.asynAddValue).setOnClickListener(this);
32 | getView(R.id.asynGetInfo).setOnClickListener(this);
33 | getView(R.id.hasMethodAddValue).setOnClickListener(this);
34 | getView(R.id.hasMethodXX).setOnClickListener(this);
35 | getView(R.id.hasMethodAsynAddValue).setOnClickListener(this);
36 | getView(R.id.hasMethodAsynXX).setOnClickListener(this);
37 | DWebView.setWebContentsDebuggingEnabled(true);
38 | dWebView= getView(R.id.webview);
39 | dWebView.loadUrl("file:///android_asset/native-call-js.html");
40 |
41 |
42 |
43 | }
44 |
45 |
46 | void showToast(Object o) {
47 | Toast.makeText(this, o.toString(), Toast.LENGTH_SHORT).show();
48 | }
49 |
50 | @Override
51 | public void onClick(View v) {
52 | switch (v.getId()) {
53 | case R.id.addValue:
54 | dWebView.callHandler("addValue", new Object[]{3, 4}, new OnReturnValue() {
55 | @Override
56 | public void onValue(Integer retValue) {
57 | showToast(retValue);
58 | }
59 | });
60 | break;
61 | case R.id.append:
62 | dWebView.callHandler("append", new Object[]{"I", "love", "you"}, new OnReturnValue() {
63 | @Override
64 | public void onValue(String retValue) {
65 | showToast(retValue);
66 | }
67 | });
68 | break;
69 | case R.id.startTimer:
70 | dWebView.callHandler("startTimer", new OnReturnValue() {
71 | @Override
72 | public void onValue(Integer retValue) {
73 | showToast(retValue);
74 | }
75 | });
76 | break;
77 | case R.id.synAddValue:
78 | dWebView.callHandler("syn.addValue", new Object[]{5, 6}, new OnReturnValue() {
79 | @Override
80 | public void onValue(Integer retValue) {
81 | showToast(retValue);
82 | }
83 | });
84 | break;
85 | case R.id.synGetInfo:
86 | dWebView.callHandler("syn.getInfo", new OnReturnValue() {
87 | @Override
88 | public void onValue(JSONObject retValue) {
89 | showToast(retValue);
90 | }
91 | });
92 | break;
93 | case R.id.asynAddValue:
94 | dWebView.callHandler("asyn.addValue", new Object[]{5, 6}, new OnReturnValue() {
95 | @Override
96 | public void onValue(Integer retValue) {
97 | showToast(retValue);
98 | }
99 | });
100 | break;
101 | case R.id.asynGetInfo:
102 | dWebView.callHandler("asyn.getInfo", new OnReturnValue() {
103 | @Override
104 | public void onValue(JSONObject retValue) {
105 | showToast(retValue);
106 | }
107 | });
108 | break;
109 | case R.id.hasMethodAddValue:
110 | dWebView.hasJavascriptMethod("addValue", new OnReturnValue() {
111 | @Override
112 | public void onValue(Boolean retValue) {
113 | showToast(retValue);
114 | }
115 | });
116 | break;
117 | case R.id.hasMethodXX:
118 | dWebView.hasJavascriptMethod("XX", new OnReturnValue() {
119 | @Override
120 | public void onValue(Boolean retValue) {
121 | showToast(retValue);
122 | }
123 | });
124 | break;
125 | case R.id.hasMethodAsynAddValue:
126 | dWebView.hasJavascriptMethod("asyn.addValue", new OnReturnValue() {
127 | @Override
128 | public void onValue(Boolean retValue) {
129 | showToast(retValue);
130 | }
131 | });
132 | break;
133 | case R.id.hasMethodAsynXX:
134 | dWebView.hasJavascriptMethod("asyn.XX", new OnReturnValue() {
135 | @Override
136 | public void onValue(Boolean retValue) {
137 | showToast(retValue);
138 | }
139 | });
140 | break;
141 | }
142 |
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/app/src/main/java/wendu/jsbdemo/JavascriptCallNativeActivity.java:
--------------------------------------------------------------------------------
1 | package wendu.jsbdemo;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 |
6 | import wendu.dsbridge.DWebView;
7 |
8 | public class JavascriptCallNativeActivity extends AppCompatActivity {
9 |
10 | @Override
11 | protected void onCreate(Bundle savedInstanceState) {
12 | super.onCreate(savedInstanceState);
13 | setContentView(R.layout.activity_js_call_native);
14 | final DWebView dwebView= (DWebView) findViewById(R.id.webview);
15 | // set debug mode
16 | DWebView.setWebContentsDebuggingEnabled(true);
17 | dwebView.addJavascriptObject(new JsApi(), null);
18 | dwebView.addJavascriptObject(new JsEchoApi(),"echo");
19 | dwebView.loadUrl("file:///android_asset/js-call-native.html");
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/wendu/jsbdemo/JsApi.java:
--------------------------------------------------------------------------------
1 | package wendu.jsbdemo;
2 |
3 | import android.os.CountDownTimer;
4 | import android.webkit.JavascriptInterface;
5 |
6 | import org.json.JSONException;
7 | import org.json.JSONObject;
8 |
9 | import wendu.dsbridge.CompletionHandler;
10 |
11 | /**
12 | * Created by du on 16/12/31.
13 | */
14 |
15 | public class JsApi{
16 | @JavascriptInterface
17 | public String testSyn(Object msg) {
18 | return msg + "[syn call]";
19 | }
20 |
21 | @JavascriptInterface
22 | public void testAsyn(Object msg, CompletionHandler handler){
23 | handler.complete(msg+" [ asyn call]");
24 | }
25 |
26 | @JavascriptInterface
27 | public String testNoArgSyn(Object arg) throws JSONException {
28 | return "testNoArgSyn called [ syn call]";
29 | }
30 |
31 | @JavascriptInterface
32 | public void testNoArgAsyn(Object arg,CompletionHandler handler) {
33 | handler.complete( "testNoArgAsyn called [ asyn call]");
34 | }
35 |
36 |
37 | //@JavascriptInterface
38 | //without @JavascriptInterface annotation can't be called
39 | public String testNever(Object arg) throws JSONException {
40 | JSONObject jsonObject= (JSONObject) arg;
41 | return jsonObject.getString("msg") + "[ never call]";
42 | }
43 |
44 | @JavascriptInterface
45 | public void callProgress(Object args, final CompletionHandler handler) {
46 |
47 | new CountDownTimer(11000, 1000) {
48 | int i=10;
49 | @Override
50 | public void onTick(long millisUntilFinished) {
51 | //setProgressData can be called many times util complete be called.
52 | handler.setProgressData((i--));
53 |
54 | }
55 | @Override
56 | public void onFinish() {
57 | //complete the js invocation with data; handler will be invalid when complete is called
58 | handler.complete(0);
59 |
60 | }
61 | }.start();
62 | }
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/wendu/jsbdemo/JsEchoApi.java:
--------------------------------------------------------------------------------
1 | package wendu.jsbdemo;
2 |
3 | import android.webkit.JavascriptInterface;
4 |
5 | import org.json.JSONException;
6 |
7 | import wendu.dsbridge.CompletionHandler;
8 |
9 | /**
10 | * Created by du on 16/12/31.
11 | */
12 |
13 | public class JsEchoApi {
14 |
15 | @JavascriptInterface
16 | public Object syn(Object args) throws JSONException {
17 | return args;
18 | }
19 |
20 | @JavascriptInterface
21 | public void asyn(Object args,CompletionHandler handler){
22 | handler.complete(args);
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/wendu/jsbdemo/MainActivity.java:
--------------------------------------------------------------------------------
1 | package wendu.jsbdemo;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.v7.app.AppCompatActivity;
6 | import android.util.Log;
7 | import android.view.View;
8 |
9 | import com.tencent.smtt.sdk.QbSdk;
10 |
11 | public class MainActivity extends AppCompatActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 |
16 | super.onCreate(savedInstanceState);
17 | QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() {
18 | @Override
19 | public void onCoreInitFinished() {
20 | Log.d("jsbridge","onCoreInitFinished");
21 | }
22 |
23 | @Override
24 | public void onViewInitFinished(boolean b) {
25 | Log.d("jsbridge","onCoreInitFinished");
26 | }
27 | });
28 | setContentView(R.layout.activity_main);
29 | findViewById(R.id.callJs).setOnClickListener(new View.OnClickListener() {
30 | @Override
31 | public void onClick(View v) {
32 | startActivity(new Intent(MainActivity.this,CallJavascriptActivity.class));
33 | }
34 | });
35 | findViewById(R.id.callNative).setOnClickListener(new View.OnClickListener() {
36 | @Override
37 | public void onClick(View v) {
38 | startActivity(new Intent(MainActivity.this,JavascriptCallNativeActivity.class));
39 | }
40 | });
41 | findViewById(R.id.fly).setOnClickListener(new View.OnClickListener() {
42 | @Override
43 | public void onClick(View v) {
44 | startActivity(new Intent(MainActivity.this,WrokWithFlyioTestActivity.class));
45 | }
46 | });
47 |
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/wendu/jsbdemo/NetUtils.java:
--------------------------------------------------------------------------------
1 | package wendu.jsbdemo;
2 |
3 | import java.net.CookieManager;
4 | import org.json.JSONObject;
5 | import java.io.BufferedReader;
6 | import java.io.InputStream;
7 | import java.io.InputStreamReader;
8 | import java.io.PrintWriter;
9 | import java.net.HttpCookie;
10 | import java.net.HttpURLConnection;
11 | import java.net.URL;
12 | import java.util.HashMap;
13 | import java.util.Iterator;
14 | import java.util.List;
15 | import java.util.Map;
16 |
17 |
18 | import javax.net.ssl.HostnameVerifier;
19 | import javax.net.ssl.HttpsURLConnection;
20 | import javax.net.ssl.SSLSession;
21 |
22 | /**
23 | * Created by du on 2017/9/16.
24 | */
25 |
26 | public class NetUtils {
27 | public static CookieManager cookieManager=new CookieManager();
28 | public static Map request(String method, String url, String param, JSONObject headers) throws Exception {
29 | URL uri = new URL(url);
30 | method = method.toUpperCase();
31 | HttpURLConnection urlCon = (HttpURLConnection) uri.openConnection();
32 | urlCon.setRequestMethod(method);
33 | urlCon.setConnectTimeout(10000);
34 | handleRequestHeaders(urlCon,headers);
35 | if (urlCon instanceof HttpsURLConnection) {
36 | addCertVerifier((HttpsURLConnection) urlCon);
37 | }
38 | if (method.equals("POST")) {
39 | urlCon.setDoOutput(true);
40 | urlCon.setDoInput(true);
41 | if (!param.trim().isEmpty()) {
42 | PrintWriter pw = new PrintWriter(urlCon.getOutputStream());
43 | pw.print(param);
44 | pw.flush();
45 | pw.close();
46 | }
47 | }
48 | Map response=new HashMap<>();
49 | response.put("responseText",inputStream2String(urlCon.getInputStream()));
50 | response.put("statusCode",urlCon.getResponseCode());
51 | Map> responseHeaders= new HashMap<>(urlCon.getHeaderFields());
52 | responseHeaders.remove(null);
53 | responseHeaders=handleResponseHeaders(urlCon,responseHeaders);
54 | response.put("headers",responseHeaders);
55 | return response;
56 |
57 | }
58 |
59 | //对于https请求,进行证书校验
60 | private static void addCertVerifier(HttpsURLConnection urlCon) throws Exception {
61 | // 在此做证书校验
62 | // urlCon.setSSLSocketFactory(getSSLSocketFactory());
63 | urlCon.setHostnameVerifier(new HostnameVerifier() {
64 | @Override
65 | public boolean verify(String hostname, SSLSession session) {
66 | //return "api.dtworkroom.com".equals(hostname);
67 | HostnameVerifier hv=HttpsURLConnection.getDefaultHostnameVerifier();
68 | return hv.verify("*.dtworkroom.com",session);
69 | }
70 | });
71 | }
72 |
73 | //预处理请求头
74 | private static void handleRequestHeaders(HttpURLConnection connection,JSONObject headers) throws Exception {
75 |
76 | Iterator iterator = headers.keys();
77 | while(iterator.hasNext()){
78 | String key = (String) iterator.next();
79 | String value = headers.getString(key);
80 | if(!key.toLowerCase().equals("cookie")){
81 | //请求cookie
82 | connection.setRequestProperty(key, value);
83 | }
84 | }
85 | List cookies= cookieManager.getCookieStore().get(connection.getURL().toURI());
86 | cookies.toString();
87 | }
88 |
89 | private static Map> handleResponseHeaders(HttpURLConnection connection, Map> responseHeaders) throws Exception {
90 | //获取响应头中的cookies,端上统一管理cookie
91 | cookieManager.put(connection.getURL().toURI(),responseHeaders);
92 | responseHeaders.remove("set-cookie");
93 | return responseHeaders;
94 | }
95 |
96 |
97 | private static String inputStream2String(InputStream is) {
98 | String result = "";
99 | String line;
100 | InputStreamReader inputReader = new InputStreamReader(is);
101 | BufferedReader bufReader = new BufferedReader(inputReader);
102 | try {
103 | while ((line = bufReader.readLine()) != null)
104 | result += line + "\r\n";
105 | bufReader.close();
106 | } catch (Exception e) {
107 | e.printStackTrace();
108 | }
109 | return result;
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/wendu/jsbdemo/WrokWithFlyioTestActivity.java:
--------------------------------------------------------------------------------
1 | package wendu.jsbdemo;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.AppCompatActivity;
5 | import android.webkit.JavascriptInterface;
6 |
7 | import org.json.JSONObject;
8 |
9 | import wendu.dsbridge.CompletionHandler;
10 | import wendu.dsbridge.DWebView;
11 |
12 | public class WrokWithFlyioTestActivity extends AppCompatActivity {
13 |
14 | @Override
15 | protected void onCreate(Bundle savedInstanceState) {
16 | super.onCreate(savedInstanceState);
17 | setContentView(R.layout.activity_wrok_with_flyio_test);
18 | DWebView dWebView= (DWebView) findViewById(R.id.webview);
19 | dWebView.addJavascriptObject(new Object(){
20 |
21 | /**
22 | * Note: This method is for Fly.js
23 | * In browser, Ajax requests are sent by browser, but Fly can
24 | * redirect requests to native, more about Fly see https://github.com/wendux/fly
25 | * @param requestData passed by fly.js, more detail reference https://wendux.github.io/dist/#/doc/flyio-en/native
26 | * @param handler
27 | */
28 | @JavascriptInterface
29 | public void onAjaxRequest(Object requestData, CompletionHandler handler){
30 | // Handle ajax request redirected by Fly
31 | AjaxHandler.onAjaxRequest((JSONObject)requestData,handler);
32 | }
33 |
34 | },null);
35 |
36 | dWebView.loadUrl("file:///android_asset/fly.html");
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_call_javascript.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
14 |
15 |
20 |
21 |
27 |
28 |
34 |
35 |
41 |
42 |
48 |
49 |
55 |
56 |
62 |
63 |
69 |
70 |
76 |
77 |
83 |
84 |
90 |
96 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_js_call_native.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
14 |
20 |
28 |
35 |
36 |
43 |
44 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_wrok_with_flyio_test.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-Android/bfc28694bf78818d3d780fa739ae5f72f12a4da6/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-Android/bfc28694bf78818d3d780fa739ae5f72f12a4da6/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-Android/bfc28694bf78818d3d780fa739ae5f72f12a4da6/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-Android/bfc28694bf78818d3d780fa739ae5f72f12a4da6/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-Android/bfc28694bf78818d3d780fa739ae5f72f12a4da6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3a4854
4 | #3a4854
5 | #e4e4e4
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DSBridge Demo
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | google()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:4.1.3'
10 |
11 | // NOTE: Do not place your application dependencies here; they belong
12 | // in the individual module build.gradle files
13 | }
14 | }
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | google()
19 | }
20 | }
21 | task clean(type: Delete) {
22 | delete rootProject.buildDir
23 | }
24 |
--------------------------------------------------------------------------------
/changelist.md:
--------------------------------------------------------------------------------
1 | # DSBridge v3.0 change list
2 |
3 | DSBridge v3.0 is a milestone, Compared with v2.0.X, we have made a lot of changes. Note that V3.0 is **incompatible** with V2.0, but v2.0 will continue to maintain. If you are a new user, use >=v3.0
4 |
5 | #### In Java
6 |
7 | 1. **Deprecated**:~~setJavascriptInterface~~ , use `addJavascriptObject` instead.
8 |
9 | 2. **Deprecated**:~~setJavascriptContextInitedListener~~ ,`callHandler` can be called at any time.
10 |
11 | 3. **Deprecated**:~~DUIWebView~~ , `UIWebView` will not be supported ever.
12 |
13 | 4. **New**: `addJavascriptObject:(id) object namespace:(NSString *) namespace`
14 |
15 | 5. **New**: `removeJavascriptObject:NSString * namespace`
16 |
17 | 6. **New**: `disableJavascriptDialogBlock:(bool) disable`
18 |
19 | 7. **New**: `hasJavascriptMethod:(NSString *) handlerName methodExistCallback:(void(^ )(bool exist))callback`
20 |
21 | 8. **New**: ` setJavascriptCloseWindowListener:(void(^)(void))callback`
22 |
23 | 9. **New**: `setDebugMode:(bool) debug`
24 |
25 | 10. **New feature**: Support namespace
26 |
27 | 11. **New feature**: Can add multiple API object
28 |
29 | 12. **Changed**: Object-c API signature changed
30 |
31 | 13. **Changed**: `callHandler` can be called at any time.
32 |
33 |
34 |
35 |
36 | #### In Javascript
37 |
38 | 1. **New**: `hasNativeMethod(handlerName,[type])`
39 | 2. **New**: `disableJavascriptDialogBlock(disable)`
40 | 3. **New**: `registerAsyn(methodName|namespace,function|asyApiObject)`
41 | 4. **Changed**: `register(methodName|namespace,function|synApiObject)`
42 | 5. **New feature**: Support namespace
43 |
44 | # Why Only Support WKWebView?
45 |
46 | ### Advantages of WKWebView
47 |
48 | It is well known that **WKWebView loads web pages faster and more efficiently than UIWebView**, and also **doesn't have as much memory overhead** for you.
49 |
50 | Under the current timeline, most iOS apps only support iOS 9.0+.
51 |
52 | ### UIWebView Cross-Domain Access Vulnerability
53 |
54 | The reason for the iOS platform cross-domain access vulnerability is due to UIWebView turning on the WebKitAllowUniversalAccessFromFileURLs and WebKitAllowFileAccessFromFileURLs options.
55 |
56 | **WKWebView default allowFileAccessFromFileURLs and allowUniversalAccessFromFileURLs option is false.**
--------------------------------------------------------------------------------
/dsbridge/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/dsbridge/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 31
5 | buildToolsVersion '28.0.3'
6 |
7 | defaultConfig {
8 | minSdkVersion 23
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 | lintOptions {
23 | abortOnError false
24 | }
25 | }
26 |
27 | dependencies {
28 | compile fileTree(include: ['*.jar'], dir: 'libs')
29 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
30 | exclude group: 'com.android.support', module: 'support-annotations'
31 | })
32 | compile 'com.android.support:appcompat-v7:23.4.0'
33 | testCompile 'junit:junit:4.12'
34 | compile files('src/libs/tbs_sdk_thirdapp_v4.3.0.316_44216_sharewithdownloadwithfile_withoutGame_obfs_20220728_101601.jar')
35 | }
36 |
--------------------------------------------------------------------------------
/dsbridge/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Users/du/Library/Android/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | #-optimizationpasses 7
20 | #-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
21 | -keepattributes *JavascriptInterface*
22 | -keepattributes Signature
23 | -keepattributes *Annotation*
24 | -dontoptimize
25 | -dontusemixedcaseclassnames
26 | -verbose
27 | -dontskipnonpubliclibraryclasses
28 | -dontskipnonpubliclibraryclassmembers
29 | -dontwarn dalvik.**
30 | -dontwarn com.tencent.smtt.**
31 | #-overloadaggressively
32 |
33 | #@proguard_debug_start
34 | # ------------------ Keep LineNumbers and properties ---------------- #
35 | -keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
36 | -renamesourcefileattribute TbsSdkJava
37 | -keepattributes SourceFile,LineNumberTable
38 | #@proguard_debug_end
39 |
40 | # --------------------------------------------------------------------------
41 | # Addidional for x5.sdk classes for apps
42 |
43 | -keep class com.tencent.smtt.export.external.**{
44 | *;
45 | }
46 |
47 | -keep class com.tencent.tbs.video.interfaces.IUserStateChangedListener {
48 | *;
49 | }
50 |
51 | -keep class com.tencent.smtt.sdk.CacheManager {
52 | public *;
53 | }
54 |
55 | -keep class com.tencent.smtt.sdk.CookieManager {
56 | public *;
57 | }
58 |
59 | -keep class com.tencent.smtt.sdk.WebHistoryItem {
60 | public *;
61 | }
62 |
63 | -keep class com.tencent.smtt.sdk.WebViewDatabase {
64 | public *;
65 | }
66 |
67 | -keep class com.tencent.smtt.sdk.WebBackForwardList {
68 | public *;
69 | }
70 |
71 | -keep public class com.tencent.smtt.sdk.WebView {
72 | public ;
73 | public ;
74 | }
75 |
76 | -keep public class com.tencent.smtt.sdk.WebView$HitTestResult {
77 | public static final ;
78 | public java.lang.String getExtra();
79 | public int getType();
80 | }
81 |
82 | -keep public class com.tencent.smtt.sdk.WebView$WebViewTransport {
83 | public ;
84 | }
85 |
86 | -keep public class com.tencent.smtt.sdk.WebView$PictureListener {
87 | public ;
88 | public ;
89 | }
90 |
91 |
92 | -keepattributes InnerClasses
93 |
94 | -keep public enum com.tencent.smtt.sdk.WebSettings$** {
95 | *;
96 | }
97 |
98 | -keep public enum com.tencent.smtt.sdk.QbSdk$** {
99 | *;
100 | }
101 |
102 | -keep public class com.tencent.smtt.sdk.WebSettings {
103 | public *;
104 | }
105 |
106 |
107 | -keepattributes Signature
108 | -keep public class com.tencent.smtt.sdk.ValueCallback {
109 | public ;
110 | public ;
111 | }
112 |
113 | -keep public class com.tencent.smtt.sdk.WebViewClient {
114 | public ;
115 | public ;
116 | }
117 |
118 | -keep public class com.tencent.smtt.sdk.DownloadListener {
119 | public ;
120 | public ;
121 | }
122 |
123 | -keep public class com.tencent.smtt.sdk.WebChromeClient {
124 | public ;
125 | public ;
126 | }
127 |
128 | -keep public class com.tencent.smtt.sdk.WebChromeClient$FileChooserParams {
129 | public ;
130 | public ;
131 | }
132 |
133 | -keep class com.tencent.smtt.sdk.SystemWebChromeClient{
134 | public *;
135 | }
136 | # 1. extension interfaces should be apparent
137 | -keep public class com.tencent.smtt.export.external.extension.interfaces.* {
138 | public protected *;
139 | }
140 |
141 | # 2. interfaces should be apparent
142 | -keep public class com.tencent.smtt.export.external.interfaces.* {
143 | public protected *;
144 | }
145 |
146 | -keep public class com.tencent.smtt.sdk.WebViewCallbackClient {
147 | public protected *;
148 | }
149 |
150 | -keep public class com.tencent.smtt.sdk.WebStorage$QuotaUpdater {
151 | public ;
152 | public ;
153 | }
154 |
155 | -keep public class com.tencent.smtt.sdk.WebIconDatabase {
156 | public ;
157 | public ;
158 | }
159 |
160 | -keep public class com.tencent.smtt.sdk.WebStorage {
161 | public ;
162 | public ;
163 | }
164 |
165 | -keep public class com.tencent.smtt.sdk.DownloadListener {
166 | public ;
167 | public ;
168 | }
169 |
170 | -keep public class com.tencent.smtt.sdk.QbSdk {
171 | public ;
172 | public ;
173 | }
174 |
175 | -keep public class com.tencent.smtt.sdk.QbSdk$PreInitCallback {
176 | public ;
177 | public ;
178 | }
179 | -keep public class com.tencent.smtt.sdk.CookieSyncManager {
180 | public ;
181 | public ;
182 | }
183 |
184 | -keep public class com.tencent.smtt.sdk.Tbs* {
185 | public ;
186 | public ;
187 | }
188 |
189 | -keep public class com.tencent.smtt.utils.LogFileUtils {
190 | public ;
191 | public ;
192 | }
193 |
194 | -keep public class com.tencent.smtt.utils.TbsLog {
195 | public ;
196 | public ;
197 | }
198 |
199 | -keep public class com.tencent.smtt.utils.TbsLogClient {
200 | public ;
201 | public ;
202 | }
203 |
204 | -keep public class com.tencent.smtt.sdk.CookieSyncManager {
205 | public ;
206 | public ;
207 | }
208 |
209 | # Added for game demos
210 | -keep public class com.tencent.smtt.sdk.TBSGamePlayer {
211 | public ;
212 | public ;
213 | }
214 |
215 | -keep public class com.tencent.smtt.sdk.TBSGamePlayerClient* {
216 | public ;
217 | public ;
218 | }
219 |
220 | -keep public class com.tencent.smtt.sdk.TBSGamePlayerClientExtension {
221 | public ;
222 | public ;
223 | }
224 |
225 | -keep public class com.tencent.smtt.sdk.TBSGamePlayerService* {
226 | public ;
227 | public ;
228 | }
229 |
230 | -keep public class com.tencent.smtt.utils.Apn {
231 | public ;
232 | public ;
233 | }
234 | -keep class com.tencent.smtt.** {
235 | *;
236 | }
237 | # end
238 |
239 |
240 | -keep public class com.tencent.smtt.export.external.extension.proxy.ProxyWebViewClientExtension {
241 | public ;
242 | public ;
243 | }
244 |
245 | -keep class MTT.ThirdAppInfoNew {
246 | *;
247 | }
248 |
249 | -keep class com.tencent.mtt.MttTraceEvent {
250 | *;
251 | }
252 |
253 | # Game related
254 | -keep public class com.tencent.smtt.gamesdk.* {
255 | public protected *;
256 | }
257 |
258 | -keep public class com.tencent.smtt.sdk.TBSGameBooter {
259 | public ;
260 | public ;
261 | }
262 |
263 | -keep public class com.tencent.smtt.sdk.TBSGameBaseActivity {
264 | public protected *;
265 | }
266 |
267 | -keep public class com.tencent.smtt.sdk.TBSGameBaseActivityProxy {
268 | public protected *;
269 | }
270 |
271 | -keep public class com.tencent.smtt.gamesdk.internal.TBSGameServiceClient {
272 | public *;
273 | }
274 | #---------------------------------------------------------------------------
275 |
276 |
277 | #------------------ 下方是android平台自带的排除项,这里不要动 ----------------
278 |
279 | -keep public class * extends android.app.Activity{
280 | public ;
281 | public ;
282 | }
283 | -keep public class * extends android.app.Application{
284 | public ;
285 | public ;
286 | }
287 | -keep public class * extends android.app.Service
288 | -keep public class * extends android.content.BroadcastReceiver
289 | -keep public class * extends android.content.ContentProvider
290 | -keep public class * extends android.app.backup.BackupAgentHelper
291 | -keep public class * extends android.preference.Preference
292 |
293 | -keepclassmembers enum * {
294 | public static **[] values();
295 | public static ** valueOf(java.lang.String);
296 | }
297 |
298 | -keepclasseswithmembers class * {
299 | public (android.content.Context, android.util.AttributeSet);
300 | }
301 |
302 | -keepclasseswithmembers class * {
303 | public (android.content.Context, android.util.AttributeSet, int);
304 | }
305 |
306 | -keepattributes *Annotation*
307 |
308 | -keepclasseswithmembernames class *{
309 | native ;
310 | }
311 |
312 | -keep class * implements android.os.Parcelable {
313 | public static final android.os.Parcelable$Creator *;
314 | }
315 |
316 | #------------------ 下方是共性的排除项目 ----------------
317 | # 方法名中含有“JNI”字符的,认定是Java Native Interface方法,自动排除
318 | # 方法名中含有“JRI”字符的,认定是Java Reflection Interface方法,自动排除
319 |
320 | -keepclasseswithmembers class * {
321 | ... *JNI*(...);
322 | }
323 |
324 | -keepclasseswithmembernames class * {
325 | ... *JRI*(...);
326 | }
327 |
328 | -keep class **JNI* {*;}
329 |
330 |
331 |
--------------------------------------------------------------------------------
/dsbridge/src/libs/tbs_sdk_thirdapp_v4.3.0.316_44216_sharewithdownloadwithfile_withoutGame_obfs_20220728_101601.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-Android/bfc28694bf78818d3d780fa739ae5f72f12a4da6/dsbridge/src/libs/tbs_sdk_thirdapp_v4.3.0.316_44216_sharewithdownloadwithfile_withoutGame_obfs_20220728_101601.jar
--------------------------------------------------------------------------------
/dsbridge/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/dsbridge/src/main/java/wendu/dsbridge/CompletionHandler.java:
--------------------------------------------------------------------------------
1 | package wendu.dsbridge;
2 |
3 | /**
4 | * Created by du on 16/12/31.
5 | */
6 |
7 | public interface CompletionHandler {
8 | void complete(T retValue);
9 | void complete();
10 | void setProgressData(T value);
11 | }
12 |
--------------------------------------------------------------------------------
/dsbridge/src/main/java/wendu/dsbridge/DWebView.java:
--------------------------------------------------------------------------------
1 | package wendu.dsbridge;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.Activity;
5 | import android.app.Dialog;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.graphics.Bitmap;
9 | import android.net.Uri;
10 | import android.os.Build;
11 | import android.os.Handler;
12 | import android.os.Looper;
13 | import android.os.Message;
14 | import android.support.annotation.Keep;
15 | import android.support.v7.app.AlertDialog;
16 | import android.util.AttributeSet;
17 | import android.util.Log;
18 | import android.view.Gravity;
19 | import android.view.View;
20 | import android.view.ViewGroup;
21 | import android.webkit.JavascriptInterface;
22 | import android.widget.EditText;
23 | import android.widget.FrameLayout;
24 |
25 | import com.tencent.smtt.export.external.interfaces.ConsoleMessage;
26 | import com.tencent.smtt.export.external.interfaces.GeolocationPermissionsCallback;
27 | import com.tencent.smtt.export.external.interfaces.IX5WebChromeClient;
28 | import com.tencent.smtt.export.external.interfaces.JsPromptResult;
29 | import com.tencent.smtt.export.external.interfaces.JsResult;
30 | import com.tencent.smtt.sdk.CookieManager;
31 | import com.tencent.smtt.sdk.ValueCallback;
32 | import com.tencent.smtt.sdk.WebChromeClient;
33 | import com.tencent.smtt.sdk.WebSettings;
34 | import com.tencent.smtt.sdk.WebStorage;
35 | import com.tencent.smtt.sdk.WebView;
36 |
37 | import org.json.JSONArray;
38 | import org.json.JSONException;
39 | import org.json.JSONObject;
40 |
41 | import java.io.File;
42 | import java.lang.ref.WeakReference;
43 | import java.lang.reflect.Method;
44 | import java.util.ArrayList;
45 | import java.util.Arrays;
46 | import java.util.HashMap;
47 | import java.util.Map;
48 |
49 | import static android.webkit.WebSettings.MIXED_CONTENT_ALWAYS_ALLOW;
50 |
51 |
52 | /**
53 | * Created by du on 16/12/29.
54 | */
55 |
56 | public class DWebView extends WebView {
57 | private static final String BRIDGE_NAME = "_dsbridge";
58 | private static final String LOG_TAG = "dsBridge";
59 | private static boolean isDebug = false;
60 | private Map javaScriptNamespaceInterfaces = new HashMap();
61 | private String APP_CACHE_DIRNAME;
62 | int callID = 0;
63 | private WebChromeClient webChromeClient;
64 | private volatile boolean alertBoxBlock = true;
65 | private JavascriptCloseWindowListener javascriptCloseWindowListener = null;
66 | private ArrayList callInfoList;
67 | private InnerJavascriptInterface innerJavascriptInterface = new InnerJavascriptInterface();
68 | private Handler mainHandler = new Handler(Looper.getMainLooper());
69 |
70 |
71 | class InnerJavascriptInterface {
72 |
73 | private void PrintDebugInfo(String error) {
74 | Log.d(LOG_TAG, error);
75 | if (isDebug) {
76 | evaluateJavascript(String.format("alert('%s')", "DEBUG ERR MSG:\\n" + error.replaceAll("\\'", "\\\\'")));
77 | }
78 | }
79 |
80 | @Keep
81 | @JavascriptInterface
82 | public String call(String methodName, String argStr) {
83 | String error = "Js bridge called, but can't find a corresponded " +
84 | "JavascriptInterface object , please check your code!";
85 | String[] nameStr = parseNamespace(methodName.trim());
86 | methodName = nameStr[1];
87 | Object jsb = javaScriptNamespaceInterfaces.get(nameStr[0]);
88 | JSONObject ret = new JSONObject();
89 | try {
90 | ret.put("code", -1);
91 | } catch (JSONException e) {
92 | e.printStackTrace();
93 | }
94 | if (jsb == null) {
95 | PrintDebugInfo(error);
96 | return ret.toString();
97 | }
98 | Object arg = null;
99 | Method method = null;
100 | String callback = null;
101 |
102 | try {
103 | JSONObject args = new JSONObject(argStr);
104 | if (args.has("_dscbstub")) {
105 | callback = args.getString("_dscbstub");
106 | }
107 | if (args.has("data")) {
108 | arg = args.get("data");
109 | }
110 |
111 | } catch (JSONException e) {
112 | error = String.format("The argument of \"%s\" must be a JSON object string!", methodName);
113 | PrintDebugInfo(error);
114 | e.printStackTrace();
115 | return ret.toString();
116 | }
117 |
118 |
119 | Class> cls = jsb.getClass();
120 | boolean asyn = false;
121 | try {
122 | method = cls.getMethod(methodName,
123 | new Class[]{Object.class, CompletionHandler.class});
124 | asyn = true;
125 | } catch (Exception e) {
126 | try {
127 | method = cls.getMethod(methodName, new Class[]{Object.class});
128 | } catch (Exception ex) {
129 |
130 | }
131 | }
132 |
133 | if (method == null) {
134 | error = "Not find method \"" + methodName + "\" implementation! please check if the signature or namespace of the method is right ";
135 | PrintDebugInfo(error);
136 | return ret.toString();
137 | }
138 |
139 | JavascriptInterface annotation = method.getAnnotation(JavascriptInterface.class);
140 | if (annotation == null) {
141 | error = "Method " + methodName + " is not invoked, since " +
142 | "it is not declared with JavascriptInterface annotation! ";
143 | PrintDebugInfo(error);
144 | return ret.toString();
145 | }
146 |
147 | Object retData;
148 | method.setAccessible(true);
149 | try {
150 | if (asyn) {
151 | final String cb = callback;
152 | method.invoke(jsb, arg, new CompletionHandler() {
153 |
154 | @Override
155 | public void complete(Object retValue) {
156 | complete(retValue, true);
157 | }
158 |
159 | @Override
160 | public void complete() {
161 | complete(null, true);
162 | }
163 |
164 | @Override
165 | public void setProgressData(Object value) {
166 | complete(value, false);
167 | }
168 |
169 | private void complete(Object retValue, boolean complete) {
170 | try {
171 | JSONObject ret = new JSONObject();
172 | ret.put("code", 0);
173 | ret.put("data", retValue);
174 | //retValue = URLEncoder.encode(ret.toString(), "UTF-8").replaceAll("\\+", "%20");
175 | if (cb != null) {
176 | //String script = String.format("%s(JSON.parse(decodeURIComponent(\"%s\")).data);", cb, retValue);
177 | String script = String.format("%s(%s.data);", cb, ret.toString());
178 | if (complete) {
179 | script += "delete window." + cb;
180 | }
181 | evaluateJavascript(script);
182 | }
183 | } catch (Exception e) {
184 | e.printStackTrace();
185 | }
186 | }
187 | });
188 | } else {
189 | retData = method.invoke(jsb, arg);
190 | ret.put("code", 0);
191 | ret.put("data", retData);
192 | return ret.toString();
193 | }
194 | } catch (Exception e) {
195 | e.printStackTrace();
196 | error = String.format("Call failed:The parameter of \"%s\" in Java is invalid.", methodName);
197 | PrintDebugInfo(error);
198 | return ret.toString();
199 | }
200 | return ret.toString();
201 | }
202 |
203 | }
204 |
205 |
206 | Map handlerMap = new HashMap<>();
207 |
208 | public interface JavascriptCloseWindowListener {
209 | /**
210 | * @return If true, close the current activity, otherwise, do nothing.
211 | */
212 | boolean onClose();
213 | }
214 |
215 | @Deprecated
216 | public interface FileChooser {
217 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
218 | void openFileChooser(ValueCallback valueCallback, String acceptType);
219 |
220 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
221 | void openFileChooser(ValueCallback valueCallback,
222 | String acceptType, String capture);
223 | }
224 |
225 | public DWebView(Context context, AttributeSet attrs) {
226 | super(context, attrs);
227 | init();
228 | }
229 |
230 | public DWebView(Context context) {
231 | super(context);
232 | init();
233 | }
234 |
235 | /**
236 | * Set debug mode. if in debug mode, some errors will be prompted by a dialog
237 | * and the exception caused by the native handlers will not be captured.
238 | *
239 | * @param enabled
240 | */
241 | public static void setWebContentsDebuggingEnabled(boolean enabled) {
242 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
243 | WebView.setWebContentsDebuggingEnabled(enabled);
244 | }
245 | isDebug = enabled;
246 | }
247 |
248 | @Keep
249 | private void init() {
250 | APP_CACHE_DIRNAME = getContext().getFilesDir().getAbsolutePath() + "/webcache";
251 | WebSettings settings = getSettings();
252 | settings.setDomStorageEnabled(true);
253 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
254 | CookieManager.getInstance().setAcceptThirdPartyCookies(this, true);
255 | settings.setMixedContentMode(MIXED_CONTENT_ALWAYS_ALLOW);
256 | }
257 | settings.setAllowFileAccess(false);
258 | settings.setAppCacheEnabled(false);
259 | settings.setCacheMode(WebSettings.LOAD_NO_CACHE);
260 | settings.setJavaScriptEnabled(true);
261 | settings.setLoadWithOverviewMode(true);
262 | settings.setAppCachePath(APP_CACHE_DIRNAME);
263 | settings.setUseWideViewPort(true);
264 | super.setWebChromeClient(mWebChromeClient);
265 | addInternalJavascriptObject();
266 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
267 | super.addJavascriptInterface(innerJavascriptInterface, BRIDGE_NAME);
268 | } else {
269 | // add bridge tag in lower android version
270 | settings.setUserAgentString(settings.getUserAgentString() + " _dsbridge");
271 | }
272 |
273 | }
274 |
275 | private String[] parseNamespace(String method) {
276 | int pos = method.lastIndexOf('.');
277 | String namespace = "";
278 | if (pos != -1) {
279 | namespace = method.substring(0, pos);
280 | method = method.substring(pos + 1);
281 | }
282 | return new String[]{namespace, method};
283 | }
284 |
285 | @Keep
286 | private void addInternalJavascriptObject() {
287 | addJavascriptObject(new Object() {
288 |
289 | @Keep
290 | @JavascriptInterface
291 | public boolean hasNativeMethod(Object args) throws JSONException {
292 |
293 | JSONObject jsonObject = (JSONObject) args;
294 | String methodName = jsonObject.getString("name").trim();
295 | String type = jsonObject.getString("type").trim();
296 | String[] nameStr = parseNamespace(methodName);
297 | Object jsb = javaScriptNamespaceInterfaces.get(nameStr[0]);
298 | if (jsb != null) {
299 | Class> cls = jsb.getClass();
300 | boolean asyn = false;
301 | Method method = null;
302 | try {
303 | method = cls.getMethod(nameStr[1],
304 | new Class[]{Object.class, CompletionHandler.class});
305 | asyn = true;
306 | } catch (Exception e) {
307 | try {
308 | method = cls.getMethod(nameStr[1], new Class[]{Object.class});
309 | } catch (Exception ex) {
310 |
311 | }
312 | }
313 | if (method != null) {
314 | JavascriptInterface annotation = method.getAnnotation(JavascriptInterface.class);
315 | if (annotation != null) {
316 | if ("all".equals(type) || (asyn && "asyn".equals(type) || (!asyn && "syn".equals(type)))) {
317 | return true;
318 | }
319 | }
320 | }
321 | }
322 | return false;
323 | }
324 |
325 | @Keep
326 | @JavascriptInterface
327 | public String closePage(Object object) throws JSONException {
328 | runOnMainThread(new Runnable() {
329 | @Override
330 | public void run() {
331 | if (javascriptCloseWindowListener == null
332 | || javascriptCloseWindowListener.onClose()) {
333 | Context context = getContext();
334 | if (context instanceof Activity) {
335 | ((Activity) getContext()).onBackPressed();
336 | }
337 | }
338 | }
339 | });
340 | return null;
341 | }
342 |
343 | @Keep
344 | @JavascriptInterface
345 | public void disableJavascriptDialogBlock(Object object) throws JSONException {
346 | JSONObject jsonObject = (JSONObject) object;
347 | alertBoxBlock = !jsonObject.getBoolean("disable");
348 | }
349 |
350 | @Keep
351 | @JavascriptInterface
352 | public void dsinit(Object jsonObject) {
353 | DWebView.this.dispatchStartupQueue();
354 | }
355 |
356 | @Keep
357 | @JavascriptInterface
358 | public void returnValue(final Object obj) throws JSONException {
359 | runOnMainThread(new Runnable() {
360 | @Override
361 | public void run() {
362 | JSONObject jsonObject = (JSONObject) obj;
363 | Object data = null;
364 | try {
365 | int id = jsonObject.getInt("id");
366 | boolean isCompleted = jsonObject.getBoolean("complete");
367 | OnReturnValue handler = handlerMap.get(id);
368 | if (jsonObject.has("data")) {
369 | data = jsonObject.get("data");
370 | }
371 | if (handler != null) {
372 | handler.onValue(data);
373 | if (isCompleted) {
374 | handlerMap.remove(id);
375 | }
376 | }
377 | } catch (JSONException e) {
378 | e.printStackTrace();
379 | }
380 | }
381 | });
382 | }
383 |
384 | }, "_dsb");
385 | }
386 |
387 | private void _evaluateJavascript(String script) {
388 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
389 | DWebView.super.evaluateJavascript(script, null);
390 | } else {
391 | super.loadUrl("javascript:" + script);
392 | }
393 | }
394 |
395 | /**
396 | * This method can be called in any thread, and if it is not called in the main thread,
397 | * it will be automatically distributed to the main thread.
398 | *
399 | * @param script
400 | */
401 | public void evaluateJavascript(final String script) {
402 | runOnMainThread(new Runnable() {
403 | @Override
404 | public void run() {
405 | _evaluateJavascript(script);
406 | }
407 | });
408 | }
409 |
410 | /**
411 | * This method can be called in any thread, and if it is not called in the main thread,
412 | * it will be automatically distributed to the main thread.
413 | *
414 | * @param url
415 | */
416 | @Override
417 | public void loadUrl(final String url) {
418 | runOnMainThread(new Runnable() {
419 | @Override
420 | public void run() {
421 | callInfoList = new ArrayList<>();
422 | DWebView.super.loadUrl(url);
423 | }
424 | });
425 | }
426 |
427 | /**
428 | * This method can be called in any thread, and if it is not called in the main thread,
429 | * it will be automatically distributed to the main thread.
430 | *
431 | * @param url
432 | * @param additionalHttpHeaders
433 | */
434 | @Override
435 | public void loadUrl(final String url, final Map additionalHttpHeaders) {
436 | runOnMainThread(new Runnable() {
437 | @Override
438 | public void run() {
439 | callInfoList = new ArrayList<>();
440 | DWebView.super.loadUrl(url, additionalHttpHeaders);
441 | }
442 | });
443 | }
444 |
445 | @Override
446 | public void reload() {
447 | runOnMainThread(new Runnable() {
448 | @Override
449 | public void run() {
450 | callInfoList = new ArrayList<>();
451 | DWebView.super.reload();
452 | }
453 | });
454 | }
455 |
456 | /**
457 | * set a listener for javascript closing the current activity.
458 | */
459 | public void setJavascriptCloseWindowListener(JavascriptCloseWindowListener listener) {
460 | javascriptCloseWindowListener = listener;
461 | }
462 |
463 |
464 | private class CallInfo {
465 | public CallInfo(String handlerName, int id, Object[] args) {
466 | if (args == null) args = new Object[0];
467 | data = new JSONArray(Arrays.asList(args)).toString();
468 | callbackId = id;
469 | method = handlerName;
470 | }
471 |
472 | @Override
473 | public String toString() {
474 | JSONObject jo = new JSONObject();
475 | try {
476 | jo.put("method", method);
477 | jo.put("callbackId", callbackId);
478 | jo.put("data", data);
479 | } catch (JSONException e) {
480 | e.printStackTrace();
481 | }
482 | return jo.toString();
483 | }
484 |
485 | public String data = null;
486 | public int callbackId;
487 | public String method;
488 | }
489 |
490 | private synchronized void dispatchStartupQueue() {
491 | if (callInfoList != null) {
492 | for (CallInfo info : callInfoList) {
493 | dispatchJavascriptCall(info);
494 | }
495 | callInfoList = null;
496 | }
497 | }
498 |
499 | private void dispatchJavascriptCall(CallInfo info) {
500 | evaluateJavascript(String.format("window._handleMessageFromNative(%s)", info.toString()));
501 | }
502 |
503 | public synchronized void callHandler(String method, Object[] args, final OnReturnValue handler) {
504 |
505 | CallInfo callInfo = new CallInfo(method, callID++, args);
506 | if (handler != null) {
507 | handlerMap.put(callInfo.callbackId, handler);
508 | }
509 |
510 | if (callInfoList != null) {
511 | callInfoList.add(callInfo);
512 | } else {
513 | dispatchJavascriptCall(callInfo);
514 | }
515 |
516 | }
517 |
518 | public void callHandler(String method, Object[] args) {
519 | callHandler(method, args, null);
520 | }
521 |
522 | public void callHandler(String method, OnReturnValue handler) {
523 | callHandler(method, null, handler);
524 | }
525 |
526 |
527 | /**
528 | * Test whether the handler exist in javascript
529 | *
530 | * @param handlerName
531 | * @param existCallback
532 | */
533 | public void hasJavascriptMethod(String handlerName, OnReturnValue existCallback) {
534 | callHandler("_hasJavascriptMethod", new Object[]{handlerName}, existCallback);
535 | }
536 |
537 | /**
538 | * Add a java object which implemented the javascript interfaces to dsBridge with namespace.
539 | * Remove the object using {@link #removeJavascriptObject(String) removeJavascriptObject(String)}
540 | *
541 | * @param object
542 | * @param namespace if empty, the object have no namespace.
543 | */
544 | public void addJavascriptObject(Object object, String namespace) {
545 | if (namespace == null) {
546 | namespace = "";
547 | }
548 | if (object != null) {
549 | javaScriptNamespaceInterfaces.put(namespace, object);
550 | }
551 | }
552 |
553 | /**
554 | * remove the javascript object with supplied namespace.
555 | *
556 | * @param namespace
557 | */
558 | public void removeJavascriptObject(String namespace) {
559 | if (namespace == null) {
560 | namespace = "";
561 | }
562 | javaScriptNamespaceInterfaces.remove(namespace);
563 |
564 | }
565 |
566 |
567 | public void disableJavascriptDialogBlock(boolean disable) {
568 | alertBoxBlock = !disable;
569 | }
570 |
571 | @Override
572 | public void setWebChromeClient(WebChromeClient client) {
573 | webChromeClient = client;
574 | }
575 |
576 | private WebChromeClient mWebChromeClient = new WebChromeClient() {
577 |
578 | @Override
579 | public void onProgressChanged(WebView view, int newProgress) {
580 | if (webChromeClient != null) {
581 | webChromeClient.onProgressChanged(view, newProgress);
582 | } else {
583 | super.onProgressChanged(view, newProgress);
584 | }
585 | }
586 |
587 | @Override
588 | public void onReceivedTitle(WebView view, String title) {
589 | if (webChromeClient != null) {
590 | webChromeClient.onReceivedTitle(view, title);
591 | } else {
592 | super.onReceivedTitle(view, title);
593 | }
594 | }
595 |
596 | @Override
597 | public void onReceivedIcon(WebView view, Bitmap icon) {
598 | if (webChromeClient != null) {
599 | webChromeClient.onReceivedIcon(view, icon);
600 | } else {
601 | super.onReceivedIcon(view, icon);
602 | }
603 | }
604 |
605 | @Override
606 | public void onReceivedTouchIconUrl(WebView view, String url, boolean precomposed) {
607 | if (webChromeClient != null) {
608 | webChromeClient.onReceivedTouchIconUrl(view, url, precomposed);
609 | } else {
610 | super.onReceivedTouchIconUrl(view, url, precomposed);
611 | }
612 | }
613 |
614 | @Override
615 | public void onShowCustomView(View view, IX5WebChromeClient.CustomViewCallback callback) {
616 | if (webChromeClient != null) {
617 | webChromeClient.onShowCustomView(view, callback);
618 | } else {
619 | super.onShowCustomView(view, callback);
620 | }
621 | }
622 |
623 | @Override
624 | public void onShowCustomView(View view, int requestedOrientation,
625 | IX5WebChromeClient.CustomViewCallback callback) {
626 | if (webChromeClient != null) {
627 | webChromeClient.onShowCustomView(view, requestedOrientation, callback);
628 | } else {
629 | super.onShowCustomView(view, requestedOrientation, callback);
630 | }
631 | }
632 |
633 | @Override
634 | public void onHideCustomView() {
635 | if (webChromeClient != null) {
636 | webChromeClient.onHideCustomView();
637 | } else {
638 | super.onHideCustomView();
639 | }
640 | }
641 |
642 | @Override
643 | public boolean onCreateWindow(WebView view, boolean isDialog,
644 | boolean isUserGesture, Message resultMsg) {
645 | if (webChromeClient != null) {
646 | return webChromeClient.onCreateWindow(view, isDialog,
647 | isUserGesture, resultMsg);
648 | }
649 | return super.onCreateWindow(view, isDialog, isUserGesture, resultMsg);
650 | }
651 |
652 | @Override
653 | public void onRequestFocus(WebView view) {
654 | if (webChromeClient != null) {
655 | webChromeClient.onRequestFocus(view);
656 | } else {
657 | super.onRequestFocus(view);
658 | }
659 | }
660 |
661 | @Override
662 | public void onCloseWindow(WebView window) {
663 | if (webChromeClient != null) {
664 | webChromeClient.onCloseWindow(window);
665 | } else {
666 | super.onCloseWindow(window);
667 | }
668 | }
669 |
670 |
671 | @Override
672 | public boolean onJsAlert(WebView view, String url, final String message, final JsResult result) {
673 |
674 | if (!alertBoxBlock) {
675 | result.confirm();
676 | }
677 | if (webChromeClient != null) {
678 | if (webChromeClient.onJsAlert(view, url, message, result)) {
679 | return true;
680 | }
681 | }
682 | Dialog alertDialog = new AlertDialog.Builder(getContext()).
683 | setMessage(message).
684 | setCancelable(false).
685 | setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
686 | @Override
687 | public void onClick(DialogInterface dialog, int which) {
688 | dialog.dismiss();
689 | if (alertBoxBlock) {
690 | result.confirm();
691 | }
692 | }
693 | })
694 | .create();
695 | alertDialog.show();
696 | return true;
697 | }
698 |
699 | @Override
700 | public boolean onJsConfirm(WebView view, String url, String message,
701 | final JsResult result) {
702 | if (!alertBoxBlock) {
703 | result.confirm();
704 | }
705 | if (webChromeClient != null && webChromeClient.onJsConfirm(view, url, message, result)) {
706 | return true;
707 | } else {
708 | DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
709 | @Override
710 | public void onClick(DialogInterface dialog, int which) {
711 | if (alertBoxBlock) {
712 | if (which == Dialog.BUTTON_POSITIVE) {
713 | result.confirm();
714 | } else {
715 | result.cancel();
716 | }
717 | }
718 | }
719 | };
720 | new AlertDialog.Builder(getContext())
721 | .setMessage(message)
722 | .setCancelable(false)
723 | .setPositiveButton(android.R.string.ok, listener)
724 | .setNegativeButton(android.R.string.cancel, listener).show();
725 | return true;
726 |
727 | }
728 |
729 | }
730 |
731 | @Override
732 | public boolean onJsPrompt(WebView view, String url, final String message,
733 | String defaultValue, final JsPromptResult result) {
734 |
735 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN) {
736 | String prefix = "_dsbridge=";
737 | if (message.startsWith(prefix)) {
738 | result.confirm(innerJavascriptInterface.call(message.substring(prefix.length()), defaultValue));
739 | return true;
740 | }
741 | }
742 |
743 | if (!alertBoxBlock) {
744 | result.confirm();
745 | }
746 |
747 | if (webChromeClient != null && webChromeClient.onJsPrompt(view, url, message, defaultValue, result)) {
748 | return true;
749 | } else {
750 | final EditText editText = new EditText(getContext());
751 | editText.setText(defaultValue);
752 | if (defaultValue != null) {
753 | editText.setSelection(defaultValue.length());
754 | }
755 | float dpi = getContext().getResources().getDisplayMetrics().density;
756 | DialogInterface.OnClickListener listener = new DialogInterface.OnClickListener() {
757 | @Override
758 | public void onClick(DialogInterface dialog, int which) {
759 | if (alertBoxBlock) {
760 | if (which == Dialog.BUTTON_POSITIVE) {
761 | result.confirm(editText.getText().toString());
762 | } else {
763 | result.cancel();
764 | }
765 | }
766 | }
767 | };
768 | new AlertDialog.Builder(getContext())
769 | .setTitle(message)
770 | .setView(editText)
771 | .setCancelable(false)
772 | .setPositiveButton(android.R.string.ok, listener)
773 | .setNegativeButton(android.R.string.cancel, listener)
774 | .show();
775 | FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
776 | ViewGroup.LayoutParams.MATCH_PARENT,
777 | ViewGroup.LayoutParams.WRAP_CONTENT);
778 | int t = (int) (dpi * 16);
779 | layoutParams.setMargins(t, 0, t, 0);
780 | layoutParams.gravity = Gravity.CENTER_HORIZONTAL;
781 | editText.setLayoutParams(layoutParams);
782 | int padding = (int) (15 * dpi);
783 | editText.setPadding(padding - (int) (5 * dpi), padding, padding, padding);
784 | return true;
785 | }
786 |
787 | }
788 |
789 | @Override
790 | public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result) {
791 | if (webChromeClient != null) {
792 | return webChromeClient.onJsBeforeUnload(view, url, message, result);
793 | }
794 | return super.onJsBeforeUnload(view, url, message, result);
795 | }
796 |
797 | @Override
798 | public void onExceededDatabaseQuota(String url, String databaseIdentifier, long quota,
799 | long estimatedDatabaseSize,
800 | long totalQuota,
801 | WebStorage.QuotaUpdater quotaUpdater) {
802 | if (webChromeClient != null) {
803 | webChromeClient.onExceededDatabaseQuota(url, databaseIdentifier, quota,
804 | estimatedDatabaseSize, totalQuota, quotaUpdater);
805 | } else {
806 | super.onExceededDatabaseQuota(url, databaseIdentifier, quota,
807 | estimatedDatabaseSize, totalQuota, quotaUpdater);
808 | }
809 | }
810 |
811 | @Override
812 | public void onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater) {
813 | if (webChromeClient != null) {
814 | webChromeClient.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater);
815 | }
816 | super.onReachedMaxAppCacheSize(requiredStorage, quota, quotaUpdater);
817 | }
818 |
819 | @Override
820 | public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissionsCallback callback) {
821 | if (webChromeClient != null) {
822 | webChromeClient.onGeolocationPermissionsShowPrompt(origin, callback);
823 | } else {
824 | super.onGeolocationPermissionsShowPrompt(origin, callback);
825 | }
826 | }
827 |
828 | @Override
829 | public void onGeolocationPermissionsHidePrompt() {
830 | if (webChromeClient != null) {
831 | webChromeClient.onGeolocationPermissionsHidePrompt();
832 | } else {
833 | super.onGeolocationPermissionsHidePrompt();
834 | }
835 | }
836 |
837 |
838 | @Override
839 | public boolean onJsTimeout() {
840 | if (webChromeClient != null) {
841 | return webChromeClient.onJsTimeout();
842 | }
843 | return super.onJsTimeout();
844 | }
845 |
846 |
847 | @Override
848 | public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
849 | if (webChromeClient != null) {
850 | return webChromeClient.onConsoleMessage(consoleMessage);
851 | }
852 | return super.onConsoleMessage(consoleMessage);
853 | }
854 |
855 | @Override
856 | public Bitmap getDefaultVideoPoster() {
857 |
858 | if (webChromeClient != null) {
859 | return webChromeClient.getDefaultVideoPoster();
860 | }
861 | return super.getDefaultVideoPoster();
862 | }
863 |
864 | @Override
865 | public View getVideoLoadingProgressView() {
866 | if (webChromeClient != null) {
867 | return webChromeClient.getVideoLoadingProgressView();
868 | }
869 | return super.getVideoLoadingProgressView();
870 | }
871 |
872 | @Override
873 | public void getVisitedHistory(ValueCallback callback) {
874 | if (webChromeClient != null) {
875 | webChromeClient.getVisitedHistory(callback);
876 | } else {
877 | super.getVisitedHistory(callback);
878 | }
879 | }
880 |
881 | @Override
882 | public void openFileChooser(ValueCallback valueCallback, String s, String s1) {
883 | if (webChromeClient != null) {
884 | webChromeClient.openFileChooser(valueCallback, s, s1);
885 | return;
886 | }
887 | super.openFileChooser(valueCallback, s, s1);
888 | }
889 |
890 | @Override
891 | public boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback,
892 | FileChooserParams fileChooserParams) {
893 | if (webChromeClient != null) {
894 | return webChromeClient.onShowFileChooser(webView, filePathCallback, fileChooserParams);
895 | }
896 | return super.onShowFileChooser(webView, filePathCallback, fileChooserParams);
897 | }
898 |
899 |
900 | @Keep
901 | @TargetApi(Build.VERSION_CODES.HONEYCOMB)
902 | public void openFileChooser(ValueCallback valueCallback, String acceptType) {
903 | if (webChromeClient instanceof FileChooser) {
904 | ((FileChooser) webChromeClient).openFileChooser(valueCallback, acceptType);
905 | }
906 | }
907 |
908 | };
909 |
910 | @Override
911 | public void clearCache(boolean includeDiskFiles) {
912 | super.clearCache(includeDiskFiles);
913 | CookieManager.getInstance().removeAllCookie();
914 | Context context = getContext();
915 | try {
916 | context.deleteDatabase("webview.db");
917 | context.deleteDatabase("webviewCache.db");
918 | } catch (Exception e) {
919 | e.printStackTrace();
920 | }
921 |
922 | File appCacheDir = new File(APP_CACHE_DIRNAME);
923 | File webviewCacheDir = new File(context.getCacheDir()
924 | .getAbsolutePath() + "/webviewCache");
925 |
926 | if (webviewCacheDir.exists()) {
927 | deleteFile(webviewCacheDir);
928 | }
929 |
930 | if (appCacheDir.exists()) {
931 | deleteFile(appCacheDir);
932 | }
933 | }
934 |
935 | public void deleteFile(File file) {
936 | if (file.exists()) {
937 | if (file.isFile()) {
938 | file.delete();
939 | } else if (file.isDirectory()) {
940 | File files[] = file.listFiles();
941 | for (int i = 0; i < files.length; i++) {
942 | deleteFile(files[i]);
943 | }
944 | }
945 | file.delete();
946 | } else {
947 | Log.e("Webview", "delete file no exists " + file.getAbsolutePath());
948 | }
949 | }
950 |
951 | private void runOnMainThread(Runnable runnable) {
952 | if (Looper.getMainLooper() == Looper.myLooper()) {
953 | runnable.run();
954 | return;
955 | }
956 | mainHandler.post(runnable);
957 | }
958 |
959 |
960 | }
961 |
--------------------------------------------------------------------------------
/dsbridge/src/main/java/wendu/dsbridge/OnReturnValue.java:
--------------------------------------------------------------------------------
1 | package wendu.dsbridge;
2 |
3 | /**
4 | * Created by du on 16/12/31.
5 | */
6 |
7 | public interface OnReturnValue {
8 | void onValue( T retValue);
9 | }
10 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/751496032/DSBridge-Android/bfc28694bf78818d3d780fa739ae5f72f12a4da6/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Aug 19 10:25:16 CST 2017
2 |
3 | distributionBase=GRADLE_USER_HOME
4 | distributionPath=wrapper/dists
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
8 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/readme-chs.md:
--------------------------------------------------------------------------------
1 |
2 | # DSBridge for X5
3 |
4 | 
5 |
6 | [](https://jitpack.io/#wendux/DSBridge-Android)  [](https://opensource.org/licenses/mit-license.php) [](https://travis-ci.org/wendux/DSBridge-Android)
7 | 
8 | [](https://github.com/wendux/DSBridge-Android/tree/x5-3.0)
9 |
10 | > 三端易用的现代跨平台的 Javascript bridge, 通过它,你可以在Javascript和原生之间同步或异步的调用彼此的函数.
11 |
12 |
13 |
14 | **本分支为腾讯X5内核分支, minSdkVersion为API14**
15 |
16 | ### 注意
17 |
18 | DSBridge v3.0 是一个里程碑版本,和v2.0相比,有许多变化,需要注意的是v3.0**不兼容**之前版本,但是我们也会继续维护v2.0分支,所以,如果你是v2.0的使用者,请放心继续使用v2.0,如果你是新用户,请使用>=v3.0.
19 |
20 | [DSBridge v3.0.0 更新列表](https://github.com/wendux/DSBridge-Android/issues/31)
21 |
22 | ## 特性
23 |
24 | 1. Android、IOS、Javascript 三端易用,轻量且强大、安全且健壮。
25 |
26 | 2. 同时支持同步调用和异步调用
27 |
28 | 3. 支持以类的方式集中统一管理API
29 |
30 | 4. 支持API命名空间
31 |
32 | 5. 支持调试模式
33 |
34 | 6. 支持API存在性检测
35 |
36 | 7. 支持进度回调:一次调用,多次返回
37 |
38 | 8. 支持Javascript关闭页面事件回调
39 |
40 | 9. 支持Javascript 模态/非模态对话框
41 |
42 | 10. 支持腾讯X5内核
43 |
44 |
45 |
46 | ## 安装
47 |
48 | 1. 添加 JitPack repository 到gradle脚本中
49 |
50 | ```groovy
51 | allprojects {
52 | repositories {
53 | ...
54 | maven { url 'https://jitpack.io' }
55 | }
56 | }
57 | ```
58 |
59 | 2. 添加依赖
60 |
61 | ```groovy
62 | dependencies {
63 | compile 'com.github.wendux:DSBridge-Android:x5-3.0-SNAPSHOT'
64 | }
65 | ```
66 |
67 | ## 示例
68 |
69 | 请参考工程目录下的 `wendu.jsbdemo/` 包。运行 `app` 工程并查看示例交互。
70 |
71 | 如果要在你自己的项目中使用 dsBridge :
72 |
73 | ## 使用
74 |
75 | 1. 新建一个Java类,实现API
76 |
77 | ```java
78 | public class JsApi{
79 | //同步API
80 | @JavascriptInterface
81 | public String testSyn(Object msg) {
82 | return msg + "[syn call]";
83 | }
84 |
85 | //异步API
86 | @JavascriptInterface
87 | public void testAsyn(Object msg, CompletionHandler handler) {
88 | handler.complete(msg+" [ asyn call]");
89 | }
90 | }
91 | ```
92 |
93 | 可以看到,DSBridge正式通过类的方式集中、统一地管理API。由于安全原因,所有Java API 必须有"@JavascriptInterface" 标注。
94 |
95 | 2. 添加API类实例到 DWebView .
96 |
97 | ```javascript
98 | import wendu.dsbridge.DWebView
99 | ...
100 | DWebView dwebView= (DWebView) findViewById(R.id.dwebview);
101 | dwebView.addJavascriptObject(new JsApi(), null);
102 | ```
103 |
104 | 3. 在Javascript中调用原生 (Java/Object-c/swift) API ,并注册一个 javascript API供原生调用.
105 |
106 | - 初始化 dsBridge
107 |
108 | ```javascript
109 | //cdn方式引入初始化代码(中国地区慢,建议下载到本地工程)
110 | //
111 | //npm方式安装初始化代码
112 | //npm install dsbridge@3.1.3
113 | var dsBridge=require("dsbridge")
114 | ```
115 |
116 | - 调用原生API ,并注册一个 javascript API供原生调用.
117 |
118 | ```javascript
119 |
120 | //同步调用
121 | var str=dsBridge.call("testSyn","testSyn");
122 |
123 | //异步调用
124 | dsBridge.call("testAsyn","testAsyn", function (v) {
125 | alert(v);
126 | })
127 |
128 | //注册 javascript API
129 | dsBridge.register('addValue',function(l,r){
130 | return l+r;
131 | })
132 | ```
133 |
134 | 4. 在Java中调用 Javascript API
135 |
136 | ```java
137 | dwebView.callHandler("addValue",new Object[]{3,4},new OnReturnValue(){
138 | @Override
139 | public void onValue(Integer retValue) {
140 | Log.d("jsbridge","call succeed,return value is "+retValue);
141 | }
142 | });
143 | ```
144 |
145 |
146 |
147 |
148 | ## Java API 签名
149 |
150 | 为了兼容IOS,我们约定 Java API 签名如下:
151 |
152 | 1. 同步API.
153 |
154 | **` public any handler(Object msg) `**
155 |
156 | 参数必须是 `Object` 类型,但返回值类型没有限制,可以是任意类型。
157 |
158 | 2. 异步 API.
159 |
160 | **`public void handler(Object arg, CompletionHandler handler)`**
161 |
162 |
163 |
164 | ## 命名空间
165 |
166 | 命名空间可以帮助你更好的管理API,这在API数量多的时候非常实用,比如在混合应用中。DSBridge (>= v3.0.0) 支持你通过命名空间将API分类管理,并且命名空间支持多级的,不同级之间只需用'.' 分隔即可。
167 |
168 |
169 |
170 | ## 调试模式
171 |
172 | 在调试模式时,发生一些错误时,将会以弹窗形式提示,并且原生API如果触发异常将不会被自动捕获,因为在调试阶段应该将问题暴露出来。如果调试模式关闭,错误将不会弹窗,并且会自动捕获API触发的异常,防止crash。强烈建议在开发阶段开启调试模式,可以通过如下代码开启调试模式
173 |
174 | ```java
175 | DWebView.setWebContentsDebuggingEnabled(true)
176 | ```
177 |
178 |
179 |
180 | ## 进度回调
181 |
182 | 通常情况下,调用一个方法结束后会返回一个结果,是一一对应的。但是有时会遇到一次调用需要多次返回的场景,比如在javascript钟调用端上的一个下载文件功能,端上在下载过程中会多次通知javascript进度, 然后javascript将进度信息展示在h5页面上,这是一个典型的一次调用,多次返回的场景,如果使用其它Javascript bridge, 你将会发现要实现这个功能会比较麻烦,而DSBridge本省支持进度回调,你可以非常简单方便的实现一次调用需要多次返回的场景,下面我们实现一个倒计时的例子:
183 |
184 | In Java
185 |
186 | ```java
187 | @JavascriptInterface
188 | public void callProgress(Object args, final CompletionHandler handler) {
189 | new CountDownTimer(11000, 1000) {
190 | int i=10;
191 | @Override
192 | public void onTick(long millisUntilFinished) {
193 | //setProgressData can be called many times util complete be called.
194 | handler.setProgressData((i--));
195 | }
196 | @Override
197 | public void onFinish() {
198 | //complete the js invocation with data;
199 | //handler will invalid when complete is called
200 | handler.complete(0);
201 | }
202 | }.start();
203 | }
204 | ```
205 |
206 | In Javascript
207 |
208 | ```javascript
209 | dsBridge.call("callProgress", function (value) {
210 | document.getElementById("progress").innerText = value
211 | })
212 | ```
213 |
214 | 完整的示例代码请参考demo工程。
215 |
216 |
217 |
218 | ## Javascript 弹出框
219 |
220 | DSBridge已经实现了 Javascript的弹出框函数(alert/confirm/prompt),如果你想自定义它们,通过`WebChromeClient`重写相关函数即可。DSBridge实现的对话框默认设置是模态的,这会挂起UI线程,如果你需要非模态,请参考`dwebview.disableJavascriptDialogBlock(bool disable)` 。
221 |
222 |
223 |
224 | ## 安全
225 |
226 | 在Android 4.2(API17)之前 `webview.addJavascriptInterface` 存在安全漏洞,DSBridge内部在4.2以下的设备上不会使用`webview.addJavascriptInterface`,而是通过其它方式通信,在4.2之后会使用 `webview.addJavascriptInterface` 。同时,为了防止Javascript调用未授权的原生函数,所有Java API 必须有"@JavascriptInterface" 标注,所以您可以放心使用DSBridge。
227 |
228 |
229 |
230 | ## DWebView
231 |
232 | DWebView中,如果在非主线程调用下列方法时,它们内部会自动分发到主线程中执行,你再也无需手动切换。
233 |
234 | ```java
235 | void loadUrl( String url)
236 | void loadUrl( String url, Map additionalHttpHeaders)
237 | void evaluateJavascript(String script)
238 | ```
239 |
240 |
241 |
242 | ## API 列表
243 |
244 | ### Java API
245 |
246 | 在Java中我们把实现了供 javascript调用的 API类的实例 成为 **Java API object**.
247 |
248 | ##### `dwebview.addJavascriptObject(Object object, String namespace)`
249 |
250 | 添加一个Java API object到DWebView ,并为它指定一个命名空间。然后,在 javascript 中就可以通过`bridge.call("namespace.api",...)`来调用Java API object中的原生API了。
251 |
252 | 如果命名空间是空(null或空字符串), 那么这个添加的 Java API object就没有命名空间。在 javascript 通过 `bridge.call("api",...)`调用。
253 |
254 | **示例**:
255 |
256 | In Java
257 |
258 | ```javascript
259 | public class JsEchoApi {
260 | @JavascriptInterface
261 | public Object syn(Object args) throws JSONException {
262 | return args;
263 | }
264 |
265 | @JavascriptInterface
266 | public void asyn(Object args,CompletionHandler handler){
267 | handler.complete(args);
268 | }
269 | }
270 | //namespace is "echo"
271 | dwebView.addJavascriptObject(new JsEchoApi(),"echo");
272 | ```
273 |
274 | In Javascript
275 |
276 | ```javascript
277 | // call echo.syn
278 | var ret=dsBridge.call("echo.syn",{msg:" I am echoSyn call", tag:1})
279 | alert(JSON.stringify(ret))
280 | // call echo.asyn
281 | dsBridge.call("echo.asyn",{msg:" I am echoAsyn call",tag:2},function (ret) {
282 | alert(JSON.stringify(ret));
283 | })
284 | ```
285 |
286 |
287 |
288 | ##### `dwebview.removeJavascriptObject(String namespace)`
289 |
290 | 通过命名空间名称移除相应的Java API object 。
291 |
292 |
293 |
294 | ##### `dwebview.callHandler(String handlerName, Object[] args)`
295 |
296 | ##### `dwebview.callHandler(String handlerName, OnReturnValue handler)`
297 |
298 | ##### `dwebview.callHandler(String handlerName, Object[] args,OnReturnValue handler)`
299 |
300 | 调用 javascript API。`handlerName` 为javascript API 的名称,可以包含命名空间;参数以数组传递,`args`数组中的元素依次对应javascript API的形参; `handler` 用于接收javascript API的返回值,**注意:handler将在主线程中被执行**。
301 |
302 | 示例:
303 |
304 | ```java
305 |
306 | dWebView.callHandler("append",new Object[]{"I","love","you"},new OnReturnValue((){
307 | @Override
308 | public void onValue(String retValue) {
309 | Log.d("jsbridge","call succeed, append string is: "+retValue);
310 | }
311 | });
312 | // call with namespace 'syn', More details to see the Demo project
313 | dWebView.callHandler("syn.getInfo", new OnReturnValue() {
314 | @Override
315 | public void onValue(JSONObject retValue) {
316 | showToast(retValue);
317 | }
318 | });
319 | ```
320 |
321 |
322 |
323 | ##### `dwebview.disableJavascriptDialogBlock(bool disable)`
324 |
325 | **小心使用**. 如果你再javascript中调用弹窗函数(`alert`,` confirm`, 或 `prompt`), 那么APP将会挂起,因为这些弹窗都是**模态**的,会阻塞APP主线程,此时javascript执行流也会阻塞。如果你想避免阻塞,可以通过此API禁止,禁止后,一旦 javascript中调用了这些弹窗函数,APP将弹出**非模态**对话框,并立即返回,( `confirm` 会返回 `true`, `prompt` 返回空字符串)。
326 |
327 | 禁止Javascript对话框阻塞:
328 |
329 | ```javascript
330 | dwebview.disableJavascriptDialogBlock(true);
331 | ```
332 |
333 | 如果你想恢复**模态**对话框,传 `false` 调用即可.
334 |
335 |
336 |
337 | ##### `dwebview.setJavascriptCloseWindowListener(JavascriptCloseWindowListener listener)`
338 |
339 | 当 Javascript中调用`window.close`时,DWebView会触发此监听器,如果未设置,DWebView默认会关闭掉当前Activity.
340 |
341 | Example:
342 |
343 | ```java
344 | dwebview.setJavascriptCloseWindowListener(new DWebView.JavascriptCloseWindowListener() {
345 | @Override
346 | public boolean onClose() {
347 | Log.d("jsbridge","window.close is called in Javascript");
348 | //如果返回false,则阻止DWebView默认处理.
349 | return false;
350 | }
351 | });
352 | ```
353 |
354 |
355 |
356 | ##### `dwebview.hasJavascriptMethod(String handlerName, OnReturnValue existCallback)`
357 |
358 | 检测是否存在指定的 javascript API,`handlerName`可以包含命名空间.
359 |
360 | 示例:
361 |
362 | ```java
363 | dWebView.hasJavascriptMethod("addValue", new OnReturnValue() {
364 | @Override
365 | public void onValue(Boolean retValue) {
366 | showToast(retValue);
367 | }
368 | });
369 | ```
370 |
371 |
372 |
373 | ##### `DWebView.setWebContentsDebuggingEnabled(boolean enabled)`
374 |
375 | 设置调试模式。在调试模式时,发生一些错误时,将会以弹窗形式提示,并且原生API如果触发异常将不会被自动捕获,因为在调试阶段应该将问题暴露出来。如果调试模式关闭,错误将不会弹窗,并且会自动捕获API触发的异常,防止crash。强烈建议在开发阶段开启调试模式。
376 |
377 |
378 |
379 | ### Javascript API
380 |
381 | ##### dsBridge
382 |
383 | "dsBridge" 在初始化之后可用 .
384 |
385 | ##### `dsBridge.call(method,[arg,callback])`
386 |
387 | 同步或异步的调用Java API。
388 |
389 | `method`: Java API 名称, 可以包含命名空间。
390 |
391 | `arg`:传递给Java API 的参数。只能传一个,如果需要多个参数时,可以合并成一个json对象参数。
392 |
393 | `callback(String returnValue)`: 处理Java API的返回结果. 可选参数,**只有异步调用时才需要提供**.
394 |
395 |
396 |
397 | ##### `dsBridge.register(methodName|namespace,function|synApiObject)`
398 |
399 | ##### `dsBridge.registerAsyn(methodName|namespace,function|asyApiObject)`
400 |
401 | 注册同步/异步的Javascript API. 这两个方法都有两种调用形式:
402 |
403 | 1. 注册一个普通的方法,如:
404 |
405 | In Javascript
406 |
407 | ```javascript
408 | dsBridge.register('addValue',function(l,r){
409 | return l+r;
410 | })
411 | dsBridge.registerAsyn('append',function(arg1,arg2,arg3,responseCallback){
412 | responseCallback(arg1+" "+arg2+" "+arg3);
413 | })
414 | ```
415 |
416 | In Java
417 |
418 | ```java
419 | webView.callHandler("addValue",new Object[]{1,6},new OnReturnValue(){
420 | @Override
421 | public void onValue(String retValue) {
422 | Log.d("jsbridge","call succeed,return value is: "+retValue);
423 | }
424 | });
425 |
426 | webView.callHandler("append",new Object[]{"I","love","you"},new OnReturnValue(){
427 | @Override
428 | public void onValue(String retValue) {
429 | Log.d("jsbridge","call succeed, append string is: "+retValue);
430 | }
431 | });
432 | ```
433 |
434 |
435 |
436 | 2. 注册一个对象,指定一个命名空间:
437 |
438 | **In Javascript**
439 |
440 | ```javascript
441 | //namespace test for synchronous calls
442 | dsBridge.register("test",{
443 | tag:"test",
444 | test1:function(){
445 | return this.tag+"1"
446 | },
447 | test2:function(){
448 | return this.tag+"2"
449 | }
450 | })
451 |
452 | //namespace test1 for asynchronous calls
453 | dsBridge.registerAsyn("test1",{
454 | tag:"test1",
455 | test1:function(responseCallback){
456 | return responseCallback(this.tag+"1")
457 | },
458 | test2:function(responseCallback){
459 | return responseCallback(this.tag+"2")
460 | }
461 | })
462 | ```
463 |
464 | > 因为Javascript并不支持函数重载,所以不能在同一个Javascript对象中定义同名的同步函数和异步函数
465 |
466 | **In Java**
467 |
468 | ```java
469 | webView.callHandler("test.test1",new OnReturnValue(){
470 | @Override
471 | public void onValue(String retValue) {
472 | Log.d("jsbridge","Namespace test.test1: "+retValue);
473 | }
474 | });
475 |
476 | webView.callHandler("test1.test1",new OnReturnValue(){
477 | @Override
478 | public void onValue(String retValue) {
479 | Log.d("jsbridge","Namespace test.test1: "+retValue);
480 | }
481 | });
482 | ```
483 |
484 |
485 |
486 |
487 | ##### `dsBridge.hasNativeMethod(handlerName,[type])`
488 |
489 | 检测Java中是否存在名为`handlerName`的API, `handlerName` 可以包含命名空间.
490 |
491 | `type`: 可选参数,`["all"|"syn"|"asyn" ]`, 默认是 "all".
492 |
493 | ```javascript
494 | //检测是否存在一个名为'testAsyn'的API(无论同步还是异步)
495 | dsBridge.hasNativeMethod('testAsyn')
496 | //检测test命名空间下是否存在一个’testAsyn’的API
497 | dsBridge.hasNativeMethod('test.testAsyn')
498 | // 检测是否存在一个名为"testSyn"的异步API
499 | dsBridge.hasNativeMethod('testSyn','asyn') //false
500 | ```
501 |
502 |
503 |
504 | ##### `dsBridge.disableJavascriptDialogBlock(disable)`
505 |
506 | 调用 `dsBridge.disableJavascriptDialogBlock(...)` 和在Java中调用 `dwebview.disableJavascriptDialogBlock(...)` 作用一样.
507 |
508 | 示例:
509 |
510 | ```javascript
511 | //disable
512 | dsBridge.disableJavascriptDialogBlock()
513 | //enable
514 | dsBridge.disableJavascriptDialogBlock(false)
515 | ```
516 |
517 |
518 |
519 | ## 和 fly.js一起使用
520 |
521 | 当dsBridge遇见 [Fly.js](https://github.com/wendux/fly) 时,将会打开一个新的世界。[fly.js传送门](https://github.com/wendux/fly)
522 |
523 | 正如我们所知,在浏览器中,ajax请求受同源策略限制,不能跨域请求资源。然而, [Fly.js](https://github.com/wendux/fly) 有一个强大的功能就是支持请求重定向:将ajax请求通过任何Javascript bridge重定向到端上,并且 [Fly.js](https://github.com/wendux/fly) 官方已经提供的 dsBridge 的 adapter, 可以非常方便的协同dsBridge一起使用。由于端上没有同源策略的限制,所以 fly.js可以请求任何域的资源。
524 |
525 | 另一个典型的使用场景是在混合APP中,由于[Fly.js](https://github.com/wendux/fly) 可以将所有ajax请求转发到端上,所以,开发者就可以在端上进行统一的请求管理、证书校验、cookie管理、访问控制等。
526 |
527 | 具体的示例请查看demo.
528 |
529 | ## 最后
530 |
531 | 如果你瞎换 DSBridge, 欢迎star,以便更多的人知道它, 谢谢 !
532 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 | # DSBridge for X5
3 |
4 | 此项目是基于开源项目`DSBridge-Android`上修改的,主要升级了SDK以及X5内核版本,处理仓库地址失效问题。
5 |
6 | [HarmonyOS版本](https://github.com/751496032/DSBridge-HarmonyOS)
7 |
8 |
9 |
10 | 
11 |
12 | [](https://jitpack.io/#wendux/DSBridge-Android)  [](https://opensource.org/licenses/mit-license.php) [](https://travis-ci.org/wendux/DSBridge-Android)
13 | 
14 | [](https://github.com/wendux/DSBridge-Android/tree/x5-3.0)
15 |
16 | >Modern cross-platform JavaScript bridge, through which you can invoke each other's functions synchronously or asynchronously between JavaScript and native applications.
17 |
18 |
19 |
20 | **This branch is for x5 webcore of Tencent, minSdkVersion is API14**
21 |
22 | Chinese documentation [中文文档](https://github.com/wendux/DSBridge-Android/blob/x5-3.0/readme-chs.md)
23 | DSBridge-IOS:https://github.com/wendux/DSBridge-IOS
24 |
25 | ### Notice
26 |
27 | DSBridge v3.0 is a milestone version. Compared with v2.0, we have made a lot of changes. Note that v3.0 is **incompatible** with v2.0, but v2.0 will continue to maintain. If you are a new user, use >=v3.0.
28 |
29 | [DSBridge v3.0.0 change list](https://github.com/wendux/DSBridge-Android/issues/31)
30 |
31 | ## Features
32 |
33 | 1. The three ends of Android, IOS and Javascript are easy to use, light and powerful, safe and strong
34 | 2. Both synchronous and asynchronous calls are supported
35 | 3. Support **API Object**, which centrally implements APIs in a Java Class or a Javascript object
36 | 4. Support API namespace
37 | 5. Support debug mode
38 | 6. Support the test of whether API exists
39 | 7. Support **Progress Callback**: one call, multiple returns
40 | 8. Support event listener for Javascript to close the page
41 | 9. Support Modal and Modeless popup box for javascript
42 | 10. Support the X5 webcore of Tencent
43 |
44 | ## Installation
45 |
46 | 1. Add the JitPack repository to your build file
47 |
48 | ```groovy
49 | allprojects {
50 | repositories {
51 | ...
52 | maven { url 'https://jitpack.io' }
53 | }
54 | }
55 | ```
56 |
57 | 2. Add the dependency
58 |
59 | ```groovy
60 | dependencies {
61 | implementation 'com.github.751496032:DSBridge-Android:v1.0.0'
62 | }
63 | ```
64 |
65 | ## Examples
66 |
67 | See the `wendu.jsbdemo/` package. run the `app` project and to see it in action.
68 |
69 | To use dsBridge in your own project:
70 |
71 | ## Usage
72 |
73 | 1. Implement APIs in a Java class
74 |
75 | ```java
76 | public class JsApi{
77 | //for synchronous invocation
78 | @JavascriptInterface
79 | public String testSyn(Object msg) {
80 | return msg + "[syn call]";
81 | }
82 |
83 | //for asynchronous invocation
84 | @JavascriptInterface
85 | public void testAsyn(Object msg, CompletionHandler handler) {
86 | handler.complete(msg+" [ asyn call]");
87 | }
88 | }
89 | ```
90 |
91 | For security reason, Java APIs must be with "@JavascriptInterface" annotation .
92 |
93 | 2. Add API object to DWebView .
94 |
95 | ```javascript
96 | import wendu.dsbridge.DWebView
97 | ...
98 | DWebView dwebView= (DWebView) findViewById(R.id.dwebview);
99 | dwebView.addJavascriptObject(new JsApi(), null);
100 | ```
101 |
102 | 3. Call Native (Java/Object-c/swift) API in Javascript, and register javascript API.
103 |
104 | - Init dsBridge
105 |
106 | ```javascript
107 | //cdn
108 | //
109 | //npm
110 | //npm install dsbridge@3.1.1
111 | var dsBridge=require("dsbridge")
112 | ```
113 |
114 | - Call Native API and register a javascript API for Native invocation.
115 |
116 | ```javascript
117 |
118 | //Call synchronously
119 | var str=dsBridge.call("testSyn","testSyn");
120 |
121 | //Call asynchronously
122 | dsBridge.call("testAsyn","testAsyn", function (v) {
123 | alert(v);
124 | })
125 |
126 | //Register javascript API for Native
127 | dsBridge.register('addValue',function(l,r){
128 | return l+r;
129 | })
130 | ```
131 |
132 | 4. Call Javascript API in java
133 |
134 | ```java
135 | dwebView.callHandler("addValue",new Object[]{3,4},new OnReturnValue(){
136 | @Override
137 | public void onValue(Integer retValue) {
138 | Log.d("jsbridge","call succeed,return value is "+retValue);
139 | }
140 | });
141 | ```
142 |
143 |
144 |
145 | ## Java API signature
146 |
147 | In order to be compatible with IOS , we make the following convention on Java API signature:
148 |
149 | 1. For synchronous API.
150 |
151 | **` public any handler(Object msg) `**
152 |
153 | The argument type must be Object, and the type of return value is not limited.
154 |
155 | 2. For asynchronous API.
156 |
157 | **`public void handler(Object arg, CompletionHandler handler)`**
158 |
159 | ## Namespace
160 |
161 | Namespaces can help you better manage your APIs, which is very useful in hybrid applications, because these applications have a large number of APIs. DSBridge (>= v3.0.0) allows you to classify API with namespace. And the namespace can be multilevel, between different levels with '.' division.
162 |
163 | ## Debug mode
164 |
165 | In debug mode, some errors will be prompted by a popup dialog , and the exception caused by the native APIs will not be captured to expose problems. We recommend that the debug mode be opened at the development stage. You can open debug mode :
166 |
167 | ```java
168 | DWebView.setWebContentsDebuggingEnabled(true)
169 | ```
170 |
171 |
172 |
173 | ## Progress Callback
174 |
175 | Normally, when a API is called to end, it returns a result, which corresponds one by one. But sometimes a call need to repeatedly return multipule times, Suppose that on the Native side, there is a API to download the file, in the process of downloading, it will send the progress information to Javascript many times, then Javascript will display the progress information on the H5 page. Oh...You will find it is difficult to achieve this function. Fortunately, DSBridge supports **Progress Callback**. You can be very simple and convenient to implement a call that needs to be returned many times. Here's an example of a countdown:
176 |
177 | In Java
178 |
179 | ```java
180 | @JavascriptInterface
181 | public void callProgress(Object args, final CompletionHandler handler) {
182 | new CountDownTimer(11000, 1000) {
183 | int i=10;
184 | @Override
185 | public void onTick(long millisUntilFinished) {
186 | //setProgressData can be called many times util complete be called.
187 | handler.setProgressData((i--));
188 | }
189 | @Override
190 | public void onFinish() {
191 | //complete the js invocation with data;
192 | //handler will be invalid when complete is called
193 | handler.complete(0);
194 | }
195 | }.start();
196 | }
197 | ```
198 |
199 | In Javascript
200 |
201 | ```javascript
202 | dsBridge.call("callProgress", function (value) {
203 | document.getElementById("progress").innerText = value
204 | })
205 | ```
206 |
207 | For the complete sample code, please refer to the demo project.
208 |
209 |
210 |
211 | ## Javascript popup box
212 |
213 | For Javascript popup box functions (alert/confirm/prompt), DSBridge has implemented them all by default, if you want to custom them, override the corresponding callback in WebChromeClient . The default dialog box implemented by DSBridge is modal. This will block the UI thread. If you need modeless, please refer to `dwebview.disableJavascriptDialogBlock (bool disable)`.
214 |
215 |
216 |
217 | ## Security
218 |
219 | Before Android 4.2 (API 17), `webview.addJavascriptInterface` has security vulnerabilities, and DSBridge doesn't use it under 4.2 of the devices. Meanwhile, in order to prevent Javascript from calling unauthorized native functions, all Java APIs must be annotated with "@JavascriptInterface" , so you can use DSBridge safely.
220 |
221 |
222 |
223 | ## DWebView
224 |
225 | In DWebview, the following functions will execute in main thread automatically, you need not to switch thread by yourself.
226 |
227 | ```java
228 | void loadUrl( String url)
229 | void loadUrl(final String url, Map additionalHttpHeaders)
230 | void evaluateJavascript(String script)
231 | ```
232 |
233 |
234 |
235 | ## API Reference
236 |
237 | ### Java API
238 |
239 | In Java, the object that implements the javascript interfaces is called **Java API object**.
240 |
241 | ##### `dwebview.addJavascriptObject(Object object, String namespace)`
242 |
243 | Add the Java API object with supplied namespace into DWebView. The javascript can then call Java APIs with `bridge.call("namespace.api",...)`.
244 |
245 | If the namespace is empty, the Java API object have no namespace. The javascript can call Java APIs with `bridge.call("api",...)`.
246 |
247 | Example:
248 |
249 | In Java
250 |
251 | ```javascript
252 | public class JsEchoApi {
253 | @JavascriptInterface
254 | public Object syn(Object args) throws JSONException {
255 | return args;
256 | }
257 |
258 | @JavascriptInterface
259 | public void asyn(Object args,CompletionHandler handler){
260 | handler.complete(args);
261 | }
262 | }
263 | //namespace is "echo"
264 | dwebView.addJavascriptObject(new JsEchoApi(),"echo");
265 | ```
266 |
267 | In Javascript
268 |
269 | ```javascript
270 | // call echo.syn
271 | var ret=dsBridge.call("echo.syn",{msg:" I am echoSyn call", tag:1})
272 | alert(JSON.stringify(ret))
273 | // call echo.asyn
274 | dsBridge.call("echo.asyn",{msg:" I am echoAsyn call",tag:2},function (ret) {
275 | alert(JSON.stringify(ret));
276 | })
277 | ```
278 |
279 |
280 |
281 | ##### `dwebview.removeJavascriptObject(String namespace)`
282 |
283 | Remove the Java API object with supplied namespace.
284 |
285 |
286 |
287 | ##### `dwebview.callHandler(String handlerName, Object[] args)`
288 |
289 | ##### `dwebview.callHandler(String handlerName, OnReturnValue handler)`
290 |
291 | ##### `dwebview.callHandler(String handlerName, Object[] args,OnReturnValue handler)`
292 |
293 | Call the javascript API. If a `handler` is given, the javascript handler can respond. the `handlerName` can contain the namespace. **The handler will be called in main thread**.
294 |
295 | Example:
296 |
297 | ```java
298 |
299 | dWebView.callHandler("append",new Object[]{"I","love","you"},new OnReturnValue((){
300 | @Override
301 | public void onValue(String retValue) {
302 | Log.d("jsbridge","call succeed, append string is: "+retValue);
303 | }
304 | });
305 | // call with namespace 'syn', More details to see the Demo project
306 | dWebView.callHandler("syn.getInfo", new OnReturnValue() {
307 | @Override
308 | public void onValue(JSONObject retValue) {
309 | showToast(retValue);
310 | }
311 | });
312 | ```
313 |
314 |
315 |
316 | ##### `dwebview.disableJavascriptDialogBlock(bool disable)`
317 |
318 | BE CAREFUL to use. if you call any of the javascript popup box functions (`alert`,` confirm`, and `prompt`), the app will hang, and the javascript execution flow will be blocked. if you don't want to block the javascript execution flow, call this method, the popup box functions will return immediately( `confirm` return `true`, and the `prompt` return empty string).
319 |
320 | Example:
321 |
322 | ```javascript
323 | dwebview.disableJavascriptDialogBlock(true);
324 | ```
325 |
326 | if you want to enable the block, just calling this method with the argument value `false` .
327 |
328 |
329 |
330 | ##### `dwebview.setJavascriptCloseWindowListener(JavascriptCloseWindowListener listener)`
331 |
332 | DWebView calls `listener.onClose` when Javascript calls `window.close`. the default handler is closing the current active activity. you can provide a listener to add your hanlder .
333 |
334 | Example:
335 |
336 | ```java
337 | dwebview.setJavascriptCloseWindowListener(new DWebView.JavascriptCloseWindowListener() {
338 | @Override
339 | public boolean onClose() {
340 | Log.d("jsbridge","window.close is called in Javascript");
341 | //If return false,the default handler will be prevented.
342 | return false;
343 | }
344 | });
345 | ```
346 |
347 |
348 |
349 | ##### `dwebview.hasJavascriptMethod(String handlerName, OnReturnValue existCallback)`
350 |
351 | Test whether the handler exist in javascript.
352 |
353 | Example:
354 |
355 | ```java
356 | dWebView.hasJavascriptMethod("addValue", new OnReturnValue() {
357 | @Override
358 | public void onValue(Boolean retValue) {
359 | showToast(retValue);
360 | }
361 | });
362 | ```
363 |
364 |
365 |
366 | ##### `DWebView.setWebContentsDebuggingEnabled(boolean enabled)`
367 |
368 | Set debug mode. if in debug mode, some errors will be prompted by a popup dialog , and the exception caused by the native APIs will not be captured to expose problems. We recommend that the debug mode be opened at the development stage.
369 |
370 |
371 |
372 | ### Javascript API
373 |
374 | ##### dsBridge
375 |
376 | "dsBridge" is accessed after dsBridge Initialization .
377 |
378 |
379 |
380 | ##### `dsBridge.call(method,[arg,callback])`
381 |
382 | Call Java api synchronously and asynchronously。
383 |
384 | `method`: Java API name, can contain the namespace。
385 |
386 | `arg`: argument, Only one allowed, if you expect multiple parameters, you can pass them with a json object.
387 |
388 | `callback(String returnValue)`: callback to handle the result. **only asynchronous invocation required**.
389 |
390 |
391 |
392 | ##### `dsBridge.register(methodName|namespace,function|synApiObject)`
393 |
394 | ##### `dsBridge.registerAsyn(methodName|namespace,function|asyApiObject)`
395 |
396 | Register javascript synchronous and asynchronous API for Native invocation. There are two types of invocation
397 |
398 | 1. Just register a method. For example:
399 |
400 | In Javascript
401 |
402 | ```javascript
403 | dsBridge.register('addValue',function(l,r){
404 | return l+r;
405 | })
406 | dsBridge.registerAsyn('append',function(arg1,arg2,arg3,responseCallback){
407 | responseCallback(arg1+" "+arg2+" "+arg3);
408 | })
409 | ```
410 |
411 | In Java
412 |
413 | ```java
414 | webView.callHandler("addValue",new Object[]{1,6},new OnReturnValue(){
415 | @Override
416 | public void onValue(String retValue) {
417 | Log.d("jsbridge","call succeed,return value is: "+retValue);
418 | }
419 | });
420 |
421 | webView.callHandler("append",new Object[]{"I","love","you"},new OnReturnValue(){
422 | @Override
423 | public void onValue(String retValue) {
424 | Log.d("jsbridge","call succeed, append string is: "+retValue);
425 | }
426 | });
427 | ```
428 |
429 |
430 |
431 | 2. Register a Javascript API object with supplied namespace. For example:
432 |
433 | **In Javascript**
434 |
435 | ```java
436 | //namespace test for synchronous
437 | dsBridge.register("test",{
438 | tag:"test",
439 | test1:function(){
440 | return this.tag+"1"
441 | },
442 | test2:function(){
443 | return this.tag+"2"
444 | }
445 | })
446 |
447 | //namespace test1 for asynchronous calls
448 | dsBridge.registerAsyn("test1",{
449 | tag:"test1",
450 | test1:function(responseCallback){
451 | return responseCallback(this.tag+"1")
452 | },
453 | test2:function(responseCallback){
454 | return responseCallback(this.tag+"2")
455 | }
456 | })
457 | ```
458 |
459 | > Because JavaScript does not support function overloading, it is not possible to define asynchronous function and sync function of the same name。
460 | >
461 |
462 | **In Java**
463 |
464 | ```java
465 | webView.callHandler("test.test1",new OnReturnValue(){
466 | @Override
467 | public void onValue(String retValue) {
468 | Log.d("jsbridge","Namespace test.test1: "+retValue);
469 | }
470 | });
471 |
472 | webView.callHandler("test1.test1",new OnReturnValue(){
473 | @Override
474 | public void onValue(String retValue) {
475 | Log.d("jsbridge","Namespace test.test1: "+retValue);
476 | }
477 | });
478 | ```
479 |
480 |
481 |
482 |
483 | ##### `dsBridge.hasNativeMethod(handlerName,[type])`
484 |
485 | Test whether the handler exist in Java, the `handlerName` can contain the namespace.
486 |
487 | `type`: optional`["all"|"syn"|"asyn" ]`, default is "all".
488 |
489 | ```javascript
490 | dsBridge.hasNativeMethod('testAsyn')
491 | //test namespace method
492 | dsBridge.hasNativeMethod('test.testAsyn')
493 | // test if exist a asynchronous function that named "testSyn"
494 | dsBridge.hasNativeMethod('testSyn','asyn') //false
495 | ```
496 |
497 |
498 |
499 | ##### `dsBridge.disableJavascriptDialogBlock(disable)`
500 |
501 | Calling `dsBridge.disableJavascriptDialogBlock(...)` has the same effect as calling `dwebview.disableJavascriptDialogBlock(...)` in Java.
502 |
503 | Example:
504 |
505 | ```javascript
506 | //disable
507 | dsBridge.disableJavascriptDialogBlock()
508 | //enable
509 | dsBridge.disableJavascriptDialogBlock(false)
510 | ```
511 |
512 |
513 |
514 | ## Work with fly.js
515 |
516 | As we all know, In browser, AJax request are restricted by same-origin policy, so the request cannot be initiated across the domain. However, [Fly.js](https://github.com/wendux/fly) supports forwarding the http request to Native through any Javascript bridge, And fly.js has already provide the dsBridge adapter.Because the Native side has no the same-origin policy restriction, fly.js can request any resource from any domain.
517 |
518 | Another typical scene is in the hybrid App, [Fly.js](https://github.com/wendux/fly) will forward all requests to Native, then, the unified request management, cookie management, certificate verification, request filtering and so on are carried out on Native.
519 |
520 | For the complete sample code, please refer to the demo project.
521 |
522 | ## Finally
523 |
524 | If you like DSBridge, please star to let more people know it , Thank you !
525 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':dsbridge'
2 |
--------------------------------------------------------------------------------