android: webview shell mode
This commit is contained in:
		@@ -25,7 +25,7 @@ android {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
afterEvaluate {
 | 
					afterEvaluate {
 | 
				
			||||||
    buildJSBundle.exec()
 | 
					    //buildJSBundle.exec()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
task buildJSBundle(type: Exec) {
 | 
					task buildJSBundle(type: Exec) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -83,7 +83,8 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time
 | 
				
			|||||||
                initJSEngine();
 | 
					                initJSEngine();
 | 
				
			||||||
                injectGlobal();
 | 
					                injectGlobal();
 | 
				
			||||||
                initDoricRuntime();
 | 
					                initDoricRuntime();
 | 
				
			||||||
                if (mDoricJSE instanceof DoricWebViewJSExecutor) {
 | 
					                if (mDoricJSE instanceof DoricWebViewJSExecutor
 | 
				
			||||||
 | 
					                        || mDoricJSE instanceof DoricWebShellJSExecutor) {
 | 
				
			||||||
                    mDoricJSE.loadJS("_prepared();", "");
 | 
					                    mDoricJSE.loadJS("_prepared();", "");
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                initialized = true;
 | 
					                initialized = true;
 | 
				
			||||||
@@ -103,7 +104,7 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time
 | 
				
			|||||||
            mDoricJSE = new DoricNativeJSExecutor();
 | 
					            mDoricJSE = new DoricNativeJSExecutor();
 | 
				
			||||||
        } catch (Throwable e) {
 | 
					        } catch (Throwable e) {
 | 
				
			||||||
            mDoricJSE = new DoricWebShellJSExecutor(Doric.application());
 | 
					            mDoricJSE = new DoricWebShellJSExecutor(Doric.application());
 | 
				
			||||||
            loadBuiltinJS("doric-web.js");
 | 
					            //loadBuiltinJS("doric-web.js");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,8 +25,11 @@ import android.webkit.ConsoleMessage;
 | 
				
			|||||||
import android.webkit.JavascriptInterface;
 | 
					import android.webkit.JavascriptInterface;
 | 
				
			||||||
import android.webkit.JsResult;
 | 
					import android.webkit.JsResult;
 | 
				
			||||||
import android.webkit.WebChromeClient;
 | 
					import android.webkit.WebChromeClient;
 | 
				
			||||||
 | 
					import android.webkit.WebResourceRequest;
 | 
				
			||||||
 | 
					import android.webkit.WebResourceResponse;
 | 
				
			||||||
import android.webkit.WebSettings;
 | 
					import android.webkit.WebSettings;
 | 
				
			||||||
import android.webkit.WebView;
 | 
					import android.webkit.WebView;
 | 
				
			||||||
 | 
					import android.webkit.WebViewClient;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.github.pengfeizhou.jscore.JSDecoder;
 | 
					import com.github.pengfeizhou.jscore.JSDecoder;
 | 
				
			||||||
import com.github.pengfeizhou.jscore.JSONBuilder;
 | 
					import com.github.pengfeizhou.jscore.JSONBuilder;
 | 
				
			||||||
@@ -38,12 +41,20 @@ import org.json.JSONArray;
 | 
				
			|||||||
import org.json.JSONException;
 | 
					import org.json.JSONException;
 | 
				
			||||||
import org.json.JSONObject;
 | 
					import org.json.JSONObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.ByteArrayInputStream;
 | 
				
			||||||
 | 
					import java.io.InputStream;
 | 
				
			||||||
import java.util.HashMap;
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.HashSet;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					import java.util.Set;
 | 
				
			||||||
 | 
					import java.util.concurrent.atomic.AtomicInteger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.annotation.Nullable;
 | 
				
			||||||
 | 
					import androidx.annotation.RequiresApi;
 | 
				
			||||||
import pub.doric.BuildConfig;
 | 
					import pub.doric.BuildConfig;
 | 
				
			||||||
import pub.doric.async.SettableFuture;
 | 
					import pub.doric.async.SettableFuture;
 | 
				
			||||||
import pub.doric.utils.DoricLog;
 | 
					import pub.doric.utils.DoricLog;
 | 
				
			||||||
 | 
					import pub.doric.utils.DoricUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -128,6 +139,7 @@ public class DoricWebShellJSExecutor implements IDoricJSE {
 | 
				
			|||||||
        @JavascriptInterface
 | 
					        @JavascriptInterface
 | 
				
			||||||
        public void ready() {
 | 
					        public void ready() {
 | 
				
			||||||
            DoricLog.d("Ready");
 | 
					            DoricLog.d("Ready");
 | 
				
			||||||
 | 
					            readyFuture.set(true);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        @JavascriptInterface
 | 
					        @JavascriptInterface
 | 
				
			||||||
@@ -188,54 +200,154 @@ public class DoricWebShellJSExecutor implements IDoricJSE {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @SuppressLint({"JavascriptInterface", "SetJavaScriptEnabled"})
 | 
					    private interface ResourceInterceptor {
 | 
				
			||||||
 | 
					        boolean filter(String url);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        WebResourceResponse onIntercept(String url);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final SettableFuture<Boolean> readyFuture;
 | 
				
			||||||
 | 
					    private final Set<ResourceInterceptor> resourceInterceptors = new HashSet<>();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private class DoricWebViewClient extends WebViewClient {
 | 
				
			||||||
 | 
					        @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 | 
				
			||||||
 | 
					        @Nullable
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
 | 
				
			||||||
 | 
					            String url = request.getUrl().toString();
 | 
				
			||||||
 | 
					            for (ResourceInterceptor interceptor : resourceInterceptors) {
 | 
				
			||||||
 | 
					                if (interceptor.filter(url)) {
 | 
				
			||||||
 | 
					                    WebResourceResponse webResourceResponse = interceptor.onIntercept(url);
 | 
				
			||||||
 | 
					                    if (webResourceResponse != null) {
 | 
				
			||||||
 | 
					                        return webResourceResponse;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Nullable
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
 | 
				
			||||||
 | 
					            for (ResourceInterceptor interceptor : resourceInterceptors) {
 | 
				
			||||||
 | 
					                if (interceptor.filter(url)) {
 | 
				
			||||||
 | 
					                    WebResourceResponse webResourceResponse = interceptor.onIntercept(url);
 | 
				
			||||||
 | 
					                    if (webResourceResponse != null) {
 | 
				
			||||||
 | 
					                        return webResourceResponse;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final String shellUrl = "http://shell.doric/";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private final Map<String, String> loadingScripts = new HashMap<>();
 | 
				
			||||||
 | 
					    private final AtomicInteger scriptId = new AtomicInteger(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @SuppressLint({"JavascriptInterface", "SetJavaScriptEnabled", "AddJavascriptInterface"})
 | 
				
			||||||
    public DoricWebShellJSExecutor(final Context context) {
 | 
					    public DoricWebShellJSExecutor(final Context context) {
 | 
				
			||||||
 | 
					        resourceInterceptors.add(new ResourceInterceptor() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public boolean filter(String url) {
 | 
				
			||||||
 | 
					                return url.startsWith(shellUrl + "doric-web.html");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public WebResourceResponse onIntercept(String url) {
 | 
				
			||||||
 | 
					                String content = DoricUtils.readAssetFile("doric-web.html");
 | 
				
			||||||
 | 
					                InputStream inputStream = new ByteArrayInputStream(content.getBytes());
 | 
				
			||||||
 | 
					                return new WebResourceResponse("text/html", "utf-8", inputStream);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        resourceInterceptors.add(new ResourceInterceptor() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public boolean filter(String url) {
 | 
				
			||||||
 | 
					                return url.startsWith(shellUrl + "doric-web.js");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public WebResourceResponse onIntercept(String url) {
 | 
				
			||||||
 | 
					                String content = DoricUtils.readAssetFile("doric-web.js");
 | 
				
			||||||
 | 
					                InputStream inputStream = new ByteArrayInputStream(content.getBytes());
 | 
				
			||||||
 | 
					                return new WebResourceResponse("text/javascript", "utf-8", inputStream);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        resourceInterceptors.add(new ResourceInterceptor() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public boolean filter(String url) {
 | 
				
			||||||
 | 
					                return url.startsWith(shellUrl + "script/");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public WebResourceResponse onIntercept(String url) {
 | 
				
			||||||
 | 
					                String script = loadingScripts.remove(url);
 | 
				
			||||||
 | 
					                if (TextUtils.isEmpty(script)) {
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                assert script != null;
 | 
				
			||||||
 | 
					                InputStream inputStream = new ByteArrayInputStream(script.getBytes());
 | 
				
			||||||
 | 
					                return new WebResourceResponse("text/javascript", "utf-8", inputStream);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        readyFuture = new SettableFuture<>();
 | 
				
			||||||
        HandlerThread webViewHandlerThread = new HandlerThread("DoricWebViewJSExecutor");
 | 
					        HandlerThread webViewHandlerThread = new HandlerThread("DoricWebViewJSExecutor");
 | 
				
			||||||
        webViewHandlerThread.start();
 | 
					        webViewHandlerThread.start();
 | 
				
			||||||
        this.handler = new Handler(webViewHandlerThread.getLooper());
 | 
					        this.handler = new Handler(webViewHandlerThread.getLooper());
 | 
				
			||||||
        handler.post(new Runnable() {
 | 
					        this.handler.post(new Runnable() {
 | 
				
			||||||
            @Override
 | 
					            @Override
 | 
				
			||||||
            public void run() {
 | 
					            public void run() {
 | 
				
			||||||
                webView = new WebView(context.getApplicationContext());
 | 
					                webView = new WebView(context.getApplicationContext());
 | 
				
			||||||
                WebSettings webSettings = webView.getSettings();
 | 
					 | 
				
			||||||
                webSettings.setJavaScriptEnabled(true);
 | 
					 | 
				
			||||||
                webView.setWebChromeClient(new DoricWebChromeClient());
 | 
					 | 
				
			||||||
                webView.loadUrl("about:blank");
 | 
					 | 
				
			||||||
                WebViewCallback webViewCallback = new WebViewCallback();
 | 
					 | 
				
			||||||
                webView.addJavascriptInterface(webViewCallback, "NativeClient");
 | 
					 | 
				
			||||||
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && BuildConfig.DEBUG) {
 | 
					                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && BuildConfig.DEBUG) {
 | 
				
			||||||
                    WebView.setWebContentsDebuggingEnabled(true);
 | 
					                    WebView.setWebContentsDebuggingEnabled(true);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					                WebSettings webSettings = webView.getSettings();
 | 
				
			||||||
 | 
					                webSettings.setJavaScriptEnabled(true);
 | 
				
			||||||
 | 
					                webView.setWebChromeClient(new DoricWebChromeClient());
 | 
				
			||||||
 | 
					                webView.setWebViewClient(new DoricWebViewClient());
 | 
				
			||||||
 | 
					                WebViewCallback webViewCallback = new WebViewCallback();
 | 
				
			||||||
 | 
					                webView.addJavascriptInterface(webViewCallback, "NativeClient");
 | 
				
			||||||
 | 
					                webView.loadUrl(shellUrl + "doric-web.html");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        readyFuture.get();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void execJS(final String script) {
 | 
				
			||||||
 | 
					        this.handler.post(new Runnable() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void run() {
 | 
				
			||||||
 | 
					                webView.loadUrl(String.format("javascript:%s", script));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public String loadJS(final String script, String source) {
 | 
					    public String loadJS(final String script, String source) {
 | 
				
			||||||
        handler.post(new Runnable() {
 | 
					        String uniqueId = String.valueOf(scriptId.incrementAndGet());
 | 
				
			||||||
            @Override
 | 
					        String url = shellUrl + "script/" + uniqueId;
 | 
				
			||||||
            public void run() {
 | 
					        loadingScripts.put(url, script);
 | 
				
			||||||
                webView.evaluateJavascript(script, null);
 | 
					        execJS(String.format("javascript:addScriptElement('%s','%s')", uniqueId, url));
 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public JSDecoder evaluateJS(String script, String source, boolean hashKey) throws JSRuntimeException {
 | 
					    public JSDecoder evaluateJS(final String script, String source, boolean hashKey) throws JSRuntimeException {
 | 
				
			||||||
        loadJS(script, source);
 | 
					        execJS(script);
 | 
				
			||||||
        return null;
 | 
					        return null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void injectGlobalJSFunction(String name, JavaFunction javaFunction) {
 | 
					    public void injectGlobalJSFunction(String name, JavaFunction javaFunction) {
 | 
				
			||||||
        globalFunctions.put(name, javaFunction);
 | 
					        globalFunctions.put(name, javaFunction);
 | 
				
			||||||
        loadJS(String.format("__injectGlobalFunction('%s')", name), "");
 | 
					        execJS(String.format("__injectGlobalFunction('%s')", name));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void injectGlobalJSObject(String name, JavaValue javaValue) {
 | 
					    public void injectGlobalJSObject(String name, JavaValue javaValue) {
 | 
				
			||||||
        loadJS(String.format("__injectGlobalObject('%s','%s')", name, javaValue.getValue()), "");
 | 
					        execJS(String.format("__injectGlobalObject('%s','%s')", name, javaValue.getValue()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
@@ -251,7 +363,7 @@ public class DoricWebShellJSExecutor implements IDoricJSE {
 | 
				
			|||||||
                objectName,
 | 
					                objectName,
 | 
				
			||||||
                functionName,
 | 
					                functionName,
 | 
				
			||||||
                jsonArray.toString());
 | 
					                jsonArray.toString());
 | 
				
			||||||
        loadJS(script, "");
 | 
					        execJS(script);
 | 
				
			||||||
        String result = returnFuture.get();
 | 
					        String result = returnFuture.get();
 | 
				
			||||||
        returnFuture = null;
 | 
					        returnFuture = null;
 | 
				
			||||||
        if (!TextUtils.isEmpty(result)) {
 | 
					        if (!TextUtils.isEmpty(result)) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,11 +2,12 @@
 | 
				
			|||||||
<html>
 | 
					<html>
 | 
				
			||||||
  <head>
 | 
					  <head>
 | 
				
			||||||
    <meta charset="UTF-8" />
 | 
					    <meta charset="UTF-8" />
 | 
				
			||||||
    <title>这是一个HTML5的网页</title>
 | 
					    <title>Web shell for doric</title>
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body>
 | 
					  <body>
 | 
				
			||||||
    <p>Hello HTML5</p>
 | 
					    <p>Hello HTML5</p>
 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
 | 
					  <script type="text/javascript" src="http://shell.doric/doric-web.js"></script>
 | 
				
			||||||
  <script type="text/javascript">
 | 
					  <script type="text/javascript">
 | 
				
			||||||
    function addScriptElement(scriptId, source) {
 | 
					    function addScriptElement(scriptId, source) {
 | 
				
			||||||
      const scriptElement = document.createElement("script");
 | 
					      const scriptElement = document.createElement("script");
 | 
				
			||||||
@@ -15,9 +16,12 @@
 | 
				
			|||||||
      scriptElement.src = source;
 | 
					      scriptElement.src = source;
 | 
				
			||||||
      document.body.appendChild(scriptElement);
 | 
					      document.body.appendChild(scriptElement);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    function removeScriptElement(scriptId) {
 | 
					    function removeScriptElement(scriptId) {
 | 
				
			||||||
      const scriptElement = document.getElementById(scriptId);
 | 
					      const scriptElement = document.getElementById(scriptId);
 | 
				
			||||||
      document.body.removeChild(scriptElement);
 | 
					      document.body.removeChild(scriptElement);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    NativeClient.ready();
 | 
				
			||||||
  </script>
 | 
					  </script>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,9 +2,25 @@
 | 
				
			|||||||
<html>
 | 
					<html>
 | 
				
			||||||
  <head>
 | 
					  <head>
 | 
				
			||||||
    <meta charset="UTF-8" />
 | 
					    <meta charset="UTF-8" />
 | 
				
			||||||
    <title>这是一个HTML5的网页</title>
 | 
					    <title>Web shell for doric</title>
 | 
				
			||||||
  </head>
 | 
					  </head>
 | 
				
			||||||
  <body>
 | 
					  <body>
 | 
				
			||||||
    <p>Hello HTML5</p>
 | 
					    <p>Hello HTML5</p>
 | 
				
			||||||
  </body>
 | 
					  </body>
 | 
				
			||||||
 | 
					  <script type="text/javascript">
 | 
				
			||||||
 | 
					    function addScriptElement(scriptId, source) {
 | 
				
			||||||
 | 
					      const scriptElement = document.createElement("script");
 | 
				
			||||||
 | 
					      scriptElement.type = "text/javascript";
 | 
				
			||||||
 | 
					      scriptElement.id = scriptId;
 | 
				
			||||||
 | 
					      scriptElement.src = source;
 | 
				
			||||||
 | 
					      document.body.appendChild(scriptElement);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    function removeScriptElement(scriptId) {
 | 
				
			||||||
 | 
					      const scriptElement = document.getElementById(scriptId);
 | 
				
			||||||
 | 
					      document.body.removeChild(scriptElement);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    NativeClient.ready();
 | 
				
			||||||
 | 
					  </script>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user