rename dirs
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package pub.doric.devkit;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry;
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||
*/
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class ExampleInstrumentedTest {
|
||||
@Test
|
||||
public void useAppContext() {
|
||||
// Context of the app under test.
|
||||
Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
|
||||
|
||||
assertEquals("pub.doric.devkit.test", appContext.getPackageName());
|
||||
}
|
||||
}
|
10
doric-android/devkit/src/main/AndroidManifest.xml
Normal file
10
doric-android/devkit/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="pub.doric.devkit">
|
||||
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
|
||||
<application>
|
||||
<activity android:name=".ui.ScanQRCodeActivity" />
|
||||
<activity android:name=".ui.DemoDebugActivity" />
|
||||
</application>
|
||||
</manifest>
|
@@ -0,0 +1,44 @@
|
||||
package pub.doric.devkit;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class DevKit implements IDevKit {
|
||||
|
||||
public static boolean isRunningInEmulator = false;
|
||||
public static String ip = "";
|
||||
private static Gson gson = new Gson();
|
||||
|
||||
private static class Inner {
|
||||
private static final DevKit sInstance = new DevKit();
|
||||
}
|
||||
|
||||
private DevKit() {
|
||||
}
|
||||
|
||||
public static DevKit getInstance() {
|
||||
return Inner.sInstance;
|
||||
}
|
||||
|
||||
|
||||
private WSClient wsClient;
|
||||
|
||||
@Override
|
||||
public void connectDevKit(String url) {
|
||||
wsClient = new WSClient(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendDevCommand(IDevKit.Command command, JsonObject jsonObject) {
|
||||
JsonObject result = new JsonObject();
|
||||
result.addProperty("cmd", command.toString());
|
||||
result.add("data", jsonObject);
|
||||
wsClient.send(gson.toJson(result));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disconnectDevKit() {
|
||||
wsClient.close();
|
||||
wsClient = null;
|
||||
}
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
package pub.doric.devkit;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.DoricNativeDriver;
|
||||
|
||||
public class DoricContextDebuggable {
|
||||
private DoricContext doricContext;
|
||||
private DoricDebugDriver doricDebugDriver;
|
||||
|
||||
public DoricContextDebuggable(DoricContext doricContext) {
|
||||
this.doricContext = doricContext;
|
||||
}
|
||||
|
||||
public void startDebug() {
|
||||
doricDebugDriver = new DoricDebugDriver(new IStatusCallback() {
|
||||
@Override
|
||||
public void start() {
|
||||
doricContext.setDriver(doricDebugDriver);
|
||||
doricContext.reInit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void stopDebug() {
|
||||
doricDebugDriver.destroy();
|
||||
doricContext.setDriver(DoricNativeDriver.getInstance());
|
||||
doricContext.reInit();
|
||||
}
|
||||
}
|
@@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.devkit;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import pub.doric.DoricRegistry;
|
||||
import pub.doric.IDoricDriver;
|
||||
import pub.doric.async.AsyncCall;
|
||||
import pub.doric.async.AsyncResult;
|
||||
import pub.doric.engine.DoricJSEngine;
|
||||
import pub.doric.utils.DoricConstant;
|
||||
import pub.doric.utils.DoricLog;
|
||||
import pub.doric.utils.ThreadMode;
|
||||
|
||||
/**
|
||||
* @Description: Doric
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-07-18
|
||||
*/
|
||||
public class DoricDebugDriver implements IDoricDriver {
|
||||
private final DoricJSEngine doricJSEngine;
|
||||
private final ExecutorService mBridgeExecutor;
|
||||
private final Handler mUIHandler;
|
||||
private final Handler mJSHandler;
|
||||
|
||||
|
||||
public DoricDebugDriver(IStatusCallback statusCallback) {
|
||||
doricJSEngine = new DoricDebugJSEngine(statusCallback);
|
||||
mBridgeExecutor = Executors.newCachedThreadPool();
|
||||
mUIHandler = new Handler(Looper.getMainLooper());
|
||||
mJSHandler = doricJSEngine.getJSHandler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncResult<JSDecoder> invokeContextEntityMethod(final String contextId, final String method, final Object... args) {
|
||||
final Object[] nArgs = new Object[args.length + 2];
|
||||
nArgs[0] = contextId;
|
||||
nArgs[1] = method;
|
||||
if (args.length > 0) {
|
||||
System.arraycopy(args, 0, nArgs, 2, args.length);
|
||||
}
|
||||
return invokeDoricMethod(DoricConstant.DORIC_CONTEXT_INVOKE, nArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncResult<JSDecoder> invokeDoricMethod(final String method, final Object... args) {
|
||||
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<JSDecoder>() {
|
||||
@Override
|
||||
public JSDecoder call() {
|
||||
try {
|
||||
return doricJSEngine.invokeDoricMethod(method, args);
|
||||
} catch (Exception e) {
|
||||
DoricLog.e("invokeDoricMethod(%s,...),error is %s", method, e.getLocalizedMessage());
|
||||
return new JSDecoder(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> AsyncResult<T> asyncCall(Callable<T> callable, ThreadMode threadMode) {
|
||||
switch (threadMode) {
|
||||
case JS:
|
||||
return AsyncCall.ensureRunInHandler(mJSHandler, callable);
|
||||
case UI:
|
||||
return AsyncCall.ensureRunInHandler(mUIHandler, callable);
|
||||
case INDEPENDENT:
|
||||
default:
|
||||
return AsyncCall.ensureRunInExecutor(mBridgeExecutor, callable);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncResult<Boolean> createContext(final String contextId, final String script, final String source) {
|
||||
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() {
|
||||
try {
|
||||
doricJSEngine.prepareContext(contextId, script, source);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
DoricLog.e("createContext %s error is %s", source, e.getLocalizedMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public AsyncResult<Boolean> destroyContext(final String contextId) {
|
||||
return AsyncCall.ensureRunInHandler(mJSHandler, new Callable<Boolean>() {
|
||||
@Override
|
||||
public Boolean call() {
|
||||
try {
|
||||
doricJSEngine.destroyContext(contextId);
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
DoricLog.e("destroyContext %s error is %s", contextId, e.getLocalizedMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public DoricRegistry getRegistry() {
|
||||
return doricJSEngine.getRegistry();
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
doricJSEngine.teardown();
|
||||
mBridgeExecutor.shutdown();
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package pub.doric.devkit;
|
||||
|
||||
import pub.doric.devkit.remote.DoricRemoteJSExecutor;
|
||||
import pub.doric.engine.DoricJSEngine;
|
||||
|
||||
public class DoricDebugJSEngine extends DoricJSEngine {
|
||||
|
||||
private IStatusCallback statusCallback;
|
||||
|
||||
public DoricDebugJSEngine(IStatusCallback statusCallback) {
|
||||
super();
|
||||
this.statusCallback = statusCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initJSEngine() {
|
||||
mDoricJSE = new DoricRemoteJSExecutor(statusCallback);
|
||||
}
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package pub.doric.devkit;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public class DoricDev {
|
||||
public static void connectDevKit(String url) {
|
||||
DevKit.getInstance().connectDevKit(url);
|
||||
}
|
||||
|
||||
public static void sendDevCommand(IDevKit.Command command, JsonObject jsonObject) {
|
||||
DevKit.getInstance().sendDevCommand(command, jsonObject);
|
||||
}
|
||||
|
||||
public static void disconnectDevKit() {
|
||||
DevKit.getInstance().disconnectDevKit();
|
||||
}
|
||||
}
|
@@ -0,0 +1,16 @@
|
||||
package pub.doric.devkit;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
public interface IDevKit {
|
||||
|
||||
enum Command {
|
||||
DEBUG, HOT_RELOAD
|
||||
}
|
||||
|
||||
void connectDevKit(String url);
|
||||
|
||||
void sendDevCommand(IDevKit.Command command, JsonObject jsonObject);
|
||||
|
||||
void disconnectDevKit();
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package pub.doric.devkit;
|
||||
|
||||
public interface IStatusCallback {
|
||||
void start();
|
||||
}
|
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.devkit;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.AssetManager;
|
||||
import android.net.Uri;
|
||||
import android.util.Log;
|
||||
import android.webkit.MimeTypeMap;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSONBuilder;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Collection;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import fi.iki.elonen.NanoHTTPD;
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.DoricContextManager;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doricdemo
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-08-03
|
||||
*/
|
||||
public class LocalServer extends NanoHTTPD {
|
||||
private final Context context;
|
||||
private Map<String, APICommand> commandMap = new HashMap<>();
|
||||
|
||||
public LocalServer(Context context, int port) {
|
||||
super(port);
|
||||
this.context = context;
|
||||
commandMap.put("allContexts", new APICommand() {
|
||||
@Override
|
||||
public String name() {
|
||||
return "allContexts";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exec(IHTTPSession session) {
|
||||
Collection<DoricContext> ret = DoricContextManager.aliveContexts();
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
for (DoricContext doricContext : ret) {
|
||||
JSONBuilder jsonBuilder = new JSONBuilder();
|
||||
jsonBuilder.put("source", doricContext.getSource());
|
||||
jsonBuilder.put("id", doricContext.getContextId());
|
||||
jsonArray.put(jsonBuilder.toJSONObject());
|
||||
}
|
||||
return jsonArray;
|
||||
}
|
||||
});
|
||||
commandMap.put("context", new APICommand() {
|
||||
@Override
|
||||
public String name() {
|
||||
return "context";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exec(IHTTPSession session) {
|
||||
String id = session.getParms().get("id");
|
||||
DoricContext doricContext = DoricContextManager.getContext(id);
|
||||
if (doricContext != null) {
|
||||
return new JSONBuilder()
|
||||
.put("id", doricContext.getContextId())
|
||||
.put("source", doricContext.getSource())
|
||||
.put("script", doricContext.getScript())
|
||||
.toJSONObject();
|
||||
}
|
||||
return "{}";
|
||||
}
|
||||
});
|
||||
commandMap.put("reload", new APICommand() {
|
||||
@Override
|
||||
public String name() {
|
||||
return "reload";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object exec(IHTTPSession session) {
|
||||
Map<String, String> files = new HashMap<>();
|
||||
try {
|
||||
session.parseBody(files);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
String id = session.getParms().get("id");
|
||||
DoricContext doricContext = DoricContextManager.getContext(id);
|
||||
if (doricContext != null) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(files.get("postData"));
|
||||
doricContext.reload(jsonObject.optString("script"));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "success";
|
||||
}
|
||||
return "fail";
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private static String getIpAddressString() {
|
||||
try {
|
||||
for (Enumeration<NetworkInterface> enNetI = NetworkInterface
|
||||
.getNetworkInterfaces(); enNetI.hasMoreElements(); ) {
|
||||
NetworkInterface netI = enNetI.nextElement();
|
||||
for (Enumeration<InetAddress> enumIpAddr = netI
|
||||
.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
|
||||
InetAddress inetAddress = enumIpAddr.nextElement();
|
||||
if (inetAddress instanceof Inet4Address && !inetAddress.isLoopbackAddress()) {
|
||||
return inetAddress.getHostAddress();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return "0.0.0.0";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws IOException {
|
||||
super.start();
|
||||
Log.d("Debugger", String.format("Open http://%s:8910/index.html to start debug", getIpAddressString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response serve(IHTTPSession session) {
|
||||
Uri uri = Uri.parse(session.getUri());
|
||||
List<String> segments = uri.getPathSegments();
|
||||
if (segments.size() > 1 && "api".equals(segments.get(0))) {
|
||||
String cmd = segments.get(1);
|
||||
APICommand apiCommand = commandMap.get(cmd);
|
||||
if (apiCommand != null) {
|
||||
Object ret = apiCommand.exec(session);
|
||||
return NanoHTTPD.newFixedLengthResponse(Response.Status.OK, "application/json", ret.toString());
|
||||
}
|
||||
}
|
||||
String fileName = session.getUri().substring(1);
|
||||
AssetManager assetManager = context.getAssets();
|
||||
try {
|
||||
InputStream inputStream = assetManager.open("debugger/" + fileName);
|
||||
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(fileName.substring(fileName.lastIndexOf(".") + 1));
|
||||
return NanoHTTPD.newFixedLengthResponse(Response.Status.OK, mimeType, inputStream, inputStream.available());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return NanoHTTPD.newFixedLengthResponse(Response.Status.OK, "text/html", "HelloWorld");
|
||||
}
|
||||
|
||||
public interface APICommand {
|
||||
String name();
|
||||
|
||||
Object exec(IHTTPSession session);
|
||||
}
|
||||
}
|
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.devkit;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.net.ConnectException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
import pub.doric.devkit.event.ConnectExceptionEvent;
|
||||
import pub.doric.devkit.event.EOFExceptionEvent;
|
||||
import pub.doric.devkit.event.EnterDebugEvent;
|
||||
import pub.doric.devkit.event.OpenEvent;
|
||||
import pub.doric.devkit.event.ReloadEvent;
|
||||
import pub.doric.devkit.ui.DevPanel;
|
||||
|
||||
/**
|
||||
* @Description: com.github.penfeizhou.doric.dev
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-08-03
|
||||
*/
|
||||
public class WSClient extends WebSocketListener {
|
||||
private final WebSocket webSocket;
|
||||
|
||||
public WSClient(String url) {
|
||||
OkHttpClient okHttpClient = new OkHttpClient
|
||||
.Builder()
|
||||
.readTimeout(10, TimeUnit.SECONDS)
|
||||
.writeTimeout(10, TimeUnit.SECONDS)
|
||||
.build();
|
||||
Request request = new Request.Builder().url(url).build();
|
||||
webSocket = okHttpClient.newWebSocket(request, this);
|
||||
}
|
||||
|
||||
public void close() {
|
||||
webSocket.close(-1, "Close");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
super.onOpen(webSocket, response);
|
||||
|
||||
DevPanel.isDevConnected = true;
|
||||
EventBus.getDefault().post(new OpenEvent());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(WebSocket webSocket, String text) {
|
||||
super.onMessage(webSocket, text);
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(text);
|
||||
String cmd = jsonObject.optString("cmd");
|
||||
switch (cmd) {
|
||||
case "RELOAD": {
|
||||
String source = jsonObject.optString("source");
|
||||
String script = jsonObject.optString("script");
|
||||
EventBus.getDefault().post(new ReloadEvent(source, script));
|
||||
}
|
||||
break;
|
||||
case "SWITCH_TO_DEBUG": {
|
||||
EventBus.getDefault().post(new EnterDebugEvent());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosing(WebSocket webSocket, int code, String reason) {
|
||||
super.onClosing(webSocket, code, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(WebSocket webSocket, int code, String reason) {
|
||||
super.onClosed(webSocket, code, reason);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
||||
super.onFailure(webSocket, t, response);
|
||||
|
||||
if (t instanceof EOFException) {
|
||||
DevPanel.isDevConnected = false;
|
||||
EventBus.getDefault().post(new EOFExceptionEvent());
|
||||
} else if (t instanceof ConnectException) {
|
||||
DevPanel.isDevConnected = false;
|
||||
EventBus.getDefault().post(new ConnectExceptionEvent());
|
||||
}
|
||||
}
|
||||
|
||||
public void send(String command) {
|
||||
webSocket.send(command);
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
package pub.doric.devkit.event;
|
||||
|
||||
public class ConnectExceptionEvent {
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
package pub.doric.devkit.event;
|
||||
|
||||
public class EOFExceptionEvent {
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
package pub.doric.devkit.event;
|
||||
|
||||
public class EnterDebugEvent {
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
package pub.doric.devkit.event;
|
||||
|
||||
public class OpenEvent {
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
package pub.doric.devkit.event;
|
||||
|
||||
public class QuitDebugEvent {
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package pub.doric.devkit.event;
|
||||
|
||||
public class ReloadEvent {
|
||||
public String source;
|
||||
public String script;
|
||||
|
||||
public ReloadEvent(String source, String script) {
|
||||
this.source = source;
|
||||
this.script = script;
|
||||
}
|
||||
}
|
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.devkit.remote;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSRuntimeException;
|
||||
import com.github.pengfeizhou.jscore.JavaFunction;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import pub.doric.devkit.IStatusCallback;
|
||||
import pub.doric.engine.IDoricJSE;
|
||||
|
||||
public class DoricRemoteJSExecutor implements IDoricJSE {
|
||||
|
||||
private final RemoteJSExecutor mRemoteJSExecutor;
|
||||
|
||||
public DoricRemoteJSExecutor(IStatusCallback statusCallback) {
|
||||
this.mRemoteJSExecutor = new RemoteJSExecutor(statusCallback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String loadJS(String script, String source) throws JSRuntimeException {
|
||||
return mRemoteJSExecutor.loadJS(script, source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSDecoder evaluateJS(String script, String source, boolean hashKey) throws JSRuntimeException {
|
||||
return mRemoteJSExecutor.evaluateJS(script, source, hashKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectGlobalJSFunction(String name, JavaFunction javaFunction) {
|
||||
mRemoteJSExecutor.injectGlobalJSFunction(name, javaFunction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void injectGlobalJSObject(String name, JavaValue javaValue) {
|
||||
mRemoteJSExecutor.injectGlobalJSObject(name, javaValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JSDecoder invokeMethod(String objectName, String functionName, JavaValue[] javaValues, boolean hashKey) throws JSRuntimeException {
|
||||
return mRemoteJSExecutor.invokeMethod(objectName, functionName, javaValues, hashKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teardown() {
|
||||
mRemoteJSExecutor.destroy();
|
||||
}
|
||||
}
|
@@ -0,0 +1,148 @@
|
||||
package pub.doric.devkit.remote;
|
||||
|
||||
import com.github.pengfeizhou.jscore.JSDecoder;
|
||||
import com.github.pengfeizhou.jscore.JSONBuilder;
|
||||
import com.github.pengfeizhou.jscore.JavaFunction;
|
||||
import com.github.pengfeizhou.jscore.JavaValue;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.EOFException;
|
||||
import java.net.ConnectException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.WebSocket;
|
||||
import okhttp3.WebSocketListener;
|
||||
import pub.doric.devkit.DevKit;
|
||||
import pub.doric.devkit.IStatusCallback;
|
||||
import pub.doric.devkit.event.QuitDebugEvent;
|
||||
|
||||
public class RemoteJSExecutor {
|
||||
private final WebSocket webSocket;
|
||||
private final Map<String, JavaFunction> globalFunctions = new HashMap<>();
|
||||
private JSDecoder temp;
|
||||
|
||||
public RemoteJSExecutor(final IStatusCallback statusCallback) {
|
||||
OkHttpClient okHttpClient = new OkHttpClient
|
||||
.Builder()
|
||||
.readTimeout(10, TimeUnit.SECONDS)
|
||||
.writeTimeout(10, TimeUnit.SECONDS)
|
||||
.build();
|
||||
final Request request = new Request.Builder().url("ws://" + DevKit.ip + ":2080").build();
|
||||
|
||||
final Thread current = Thread.currentThread();
|
||||
webSocket = okHttpClient.newWebSocket(request, new WebSocketListener() {
|
||||
@Override
|
||||
public void onOpen(WebSocket webSocket, Response response) {
|
||||
LockSupport.unpark(current);
|
||||
statusCallback.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
|
||||
if (t instanceof ConnectException) {
|
||||
// 连接remote异常
|
||||
LockSupport.unpark(current);
|
||||
throw new RuntimeException("remote js executor cannot connect");
|
||||
} else if (t instanceof EOFException) {
|
||||
// 被远端强制断开
|
||||
System.out.println("remote js executor eof");
|
||||
|
||||
LockSupport.unpark(current);
|
||||
EventBus.getDefault().post(new QuitDebugEvent());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMessage(@NotNull WebSocket webSocket, @NotNull String text) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(text);
|
||||
String cmd = jsonObject.optString("cmd");
|
||||
switch (cmd) {
|
||||
case "injectGlobalJSFunction": {
|
||||
String name = jsonObject.optString("name");
|
||||
JSONArray arguments = jsonObject.optJSONArray("arguments");
|
||||
assert arguments != null;
|
||||
JSDecoder[] decoders = new JSDecoder[arguments.length()];
|
||||
for (int i = 0; i < arguments.length(); i++) {
|
||||
Object o = arguments.get(i);
|
||||
decoders[i] = new JSDecoder(new ValueBuilder(o).build());
|
||||
}
|
||||
globalFunctions.get(name).exec(decoders);
|
||||
}
|
||||
|
||||
break;
|
||||
case "invokeMethod": {
|
||||
try {
|
||||
Object result = jsonObject.opt("result");
|
||||
ValueBuilder vb = new ValueBuilder(result);
|
||||
temp = new JSDecoder(vb.build());
|
||||
System.out.println(result);
|
||||
} catch (Exception ex) {
|
||||
ex.printStackTrace();
|
||||
} finally {
|
||||
LockSupport.unpark(current);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
LockSupport.park(current);
|
||||
}
|
||||
|
||||
public String loadJS(String script, String source) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public JSDecoder evaluateJS(String script, String source, boolean hashKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void injectGlobalJSFunction(String name, JavaFunction javaFunction) {
|
||||
globalFunctions.put(name, javaFunction);
|
||||
webSocket.send(new JSONBuilder().put("cmd", "injectGlobalJSFunction")
|
||||
.put("name", name).toString()
|
||||
);
|
||||
}
|
||||
|
||||
public void injectGlobalJSObject(String name, JavaValue javaValue) {
|
||||
}
|
||||
|
||||
public JSDecoder invokeMethod(String objectName, String functionName, JavaValue[] javaValues, boolean hashKey) {
|
||||
JSONArray jsonArray = new JSONArray();
|
||||
for (JavaValue javaValue : javaValues) {
|
||||
jsonArray.put(new JSONBuilder()
|
||||
.put("type", javaValue.getType())
|
||||
.put("value", javaValue.getValue())
|
||||
.toJSONObject());
|
||||
}
|
||||
webSocket.send(new JSONBuilder()
|
||||
.put("cmd", "invokeMethod")
|
||||
.put("objectName", objectName)
|
||||
.put("functionName", functionName)
|
||||
.put("javaValues", jsonArray)
|
||||
.put("hashKey", hashKey)
|
||||
.toString());
|
||||
|
||||
LockSupport.park(Thread.currentThread());
|
||||
return temp;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
webSocket.close(1000, "destroy");
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
package pub.doric.devkit.remote;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Created by pengfei.zhou on 2018/4/17.
|
||||
*/
|
||||
public class ValueBuilder {
|
||||
private final Object val;
|
||||
|
||||
private void writeBoolean(ByteArrayOutputStream output, boolean b) {
|
||||
output.write((byte) (b ? 1 : 0));
|
||||
}
|
||||
|
||||
private void writeInt(ByteArrayOutputStream output, int i) {
|
||||
output.write((byte) (i >>> 24));
|
||||
output.write((byte) (i >>> 16));
|
||||
output.write((byte) (i >>> 8));
|
||||
output.write((byte) i);
|
||||
}
|
||||
|
||||
private void writeDouble(ByteArrayOutputStream output, double d) {
|
||||
long l = Double.doubleToRawLongBits(d);
|
||||
output.write((byte) (l >>> 56));
|
||||
output.write((byte) (l >>> 48));
|
||||
output.write((byte) (l >>> 40));
|
||||
output.write((byte) (l >>> 32));
|
||||
output.write((byte) (l >>> 24));
|
||||
output.write((byte) (l >>> 16));
|
||||
output.write((byte) (l >>> 8));
|
||||
output.write((byte) l);
|
||||
}
|
||||
|
||||
private void writeString(ByteArrayOutputStream output, String S) {
|
||||
byte[] buf;
|
||||
try {
|
||||
buf = S.getBytes("UTF-8");
|
||||
} catch (Exception e) {
|
||||
buf = new byte[0];
|
||||
}
|
||||
int i = buf.length;
|
||||
writeInt(output, i);
|
||||
output.write(buf, 0, i);
|
||||
}
|
||||
|
||||
|
||||
private void write(ByteArrayOutputStream output, Object O) {
|
||||
if (O instanceof Number) {
|
||||
output.write((byte) 'D');
|
||||
writeDouble(output, Double.valueOf(String.valueOf(O)));
|
||||
} else if (O instanceof String) {
|
||||
output.write((byte) 'S');
|
||||
writeString(output, (String) O);
|
||||
} else if (O instanceof Boolean) {
|
||||
output.write((byte) 'B');
|
||||
writeBoolean(output, (Boolean) O);
|
||||
} else if (O instanceof JSONObject) {
|
||||
output.write((byte) 'O');
|
||||
//writeBoolean(output, (Boolean) O);
|
||||
Iterator<String> iterator = ((JSONObject) O).keys();
|
||||
while (iterator.hasNext()) {
|
||||
String key = iterator.next();
|
||||
write(output, key);
|
||||
write(output, ((JSONObject) O).opt(key));
|
||||
}
|
||||
output.write((byte) 'N');
|
||||
} else if (O instanceof JSONArray) {
|
||||
output.write((byte) 'A');
|
||||
writeInt(output, ((JSONArray) O).length());
|
||||
for (int i = 0; i < ((JSONArray) O).length(); i++) {
|
||||
write(output, ((JSONArray) O).opt(i));
|
||||
}
|
||||
} else {
|
||||
output.write((byte) 'N');
|
||||
}
|
||||
}
|
||||
|
||||
public ValueBuilder(Object o) {
|
||||
this.val = o;
|
||||
}
|
||||
|
||||
public byte[] build() {
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
write(outputStream, val);
|
||||
return outputStream.toByteArray();
|
||||
}
|
||||
}
|
@@ -0,0 +1,84 @@
|
||||
package pub.doric.devkit.ui;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.DialogFragment;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.DoricContextManager;
|
||||
import pub.doric.devkit.BuildConfig;
|
||||
import pub.doric.devkit.DoricDev;
|
||||
import pub.doric.devkit.IDevKit;
|
||||
import pub.doric.devkit.R;
|
||||
|
||||
public class DebugContextPanel extends DialogFragment {
|
||||
|
||||
public DebugContextPanel() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState
|
||||
) {
|
||||
return inflater.inflate(R.layout.layout_debug_context, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dialog onCreateDialog(Bundle savedInstanceState) {
|
||||
Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
Dialog dialog = getDialog();
|
||||
if (dialog != null) {
|
||||
dialog.getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
}
|
||||
|
||||
LinearLayout container = getView().findViewById(R.id.container);
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
|
||||
for (final DoricContext doricContext : DoricContextManager.aliveContexts()) {
|
||||
View cell = inflater.inflate(R.layout.layout_debug_context_cell, container, false);
|
||||
|
||||
TextView contextIdTextView = cell.findViewById(R.id.context_id_text_view);
|
||||
contextIdTextView.setText(doricContext.getContextId());
|
||||
|
||||
TextView sourceTextView = cell.findViewById(R.id.source_text_view);
|
||||
sourceTextView.setText(doricContext.getSource());
|
||||
|
||||
cell.findViewById(R.id.debug_text_view).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
JsonObject jsonObject = new JsonObject();
|
||||
jsonObject.addProperty("contextId", doricContext.getContextId());
|
||||
jsonObject.addProperty("projectHome", BuildConfig.PROJECT_HOME);
|
||||
jsonObject.addProperty("source", doricContext.getSource().replace(".js", ".ts"));
|
||||
DoricDev.sendDevCommand(IDevKit.Command.DEBUG, jsonObject);
|
||||
dismissAllowingStateLoss();
|
||||
}
|
||||
});
|
||||
|
||||
container.addView(cell);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright [2019] [Doric.Pub]
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package pub.doric.devkit.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import pub.doric.DoricContext;
|
||||
import pub.doric.DoricContextManager;
|
||||
import pub.doric.DoricPanel;
|
||||
import pub.doric.devkit.DoricContextDebuggable;
|
||||
import pub.doric.devkit.event.EnterDebugEvent;
|
||||
import pub.doric.devkit.event.QuitDebugEvent;
|
||||
import pub.doric.devkit.event.ReloadEvent;
|
||||
import pub.doric.devkit.util.SensorManagerHelper;
|
||||
import pub.doric.utils.DoricUtils;
|
||||
|
||||
/**
|
||||
* @Description: pub.doric.demo
|
||||
* @Author: pengfei.zhou
|
||||
* @CreateDate: 2019-11-19
|
||||
*/
|
||||
public class DemoDebugActivity extends AppCompatActivity {
|
||||
private DoricContext doricContext;
|
||||
private SensorManagerHelper sensorHelper;
|
||||
private DoricContextDebuggable doricContextDebuggable;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
String source = getIntent().getStringExtra("source");
|
||||
DoricPanel doricPanel = new DoricPanel(this);
|
||||
addContentView(doricPanel, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
doricPanel.config(DoricUtils.readAssetFile("demo/" + source), source);
|
||||
doricContext = doricPanel.getDoricContext();
|
||||
doricContextDebuggable = new DoricContextDebuggable(doricContext);
|
||||
sensorHelper = new SensorManagerHelper(this);
|
||||
sensorHelper.setOnShakeListener(new SensorManagerHelper.OnShakeListener() {
|
||||
@Override
|
||||
public void onShake() {
|
||||
Fragment devPanel = getSupportFragmentManager().findFragmentByTag("DevPanel");
|
||||
if (devPanel != null && devPanel.isAdded()) {
|
||||
return;
|
||||
}
|
||||
new DevPanel().show(getSupportFragmentManager(), "DevPanel");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
EventBus.getDefault().unregister(this);
|
||||
sensorHelper.stop();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEnterDebugEvent(EnterDebugEvent enterDebugEvent) {
|
||||
doricContextDebuggable.startDebug();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onReloadEvent(ReloadEvent reloadEvent) {
|
||||
for (DoricContext context : DoricContextManager.aliveContexts()) {
|
||||
if (reloadEvent.source.contains(context.getSource())) {
|
||||
context.reload(reloadEvent.script);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onQuitDebugEvent(QuitDebugEvent quitDebugEvent) {
|
||||
doricContextDebuggable.stopDebug();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
||||
if (KeyEvent.KEYCODE_MENU == event.getKeyCode()) {
|
||||
new DevPanel().show(getSupportFragmentManager(), "DevPanel");
|
||||
}
|
||||
return super.onKeyDown(keyCode, event);
|
||||
}
|
||||
}
|
@@ -0,0 +1,136 @@
|
||||
package pub.doric.devkit.ui;
|
||||
|
||||
import android.Manifest;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
|
||||
import com.lahm.library.EasyProtectorLib;
|
||||
import com.lahm.library.EmulatorCheckCallback;
|
||||
import com.tbruyelle.rxpermissions2.RxPermissions;
|
||||
|
||||
import org.greenrobot.eventbus.EventBus;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
|
||||
import io.reactivex.disposables.Disposable;
|
||||
import io.reactivex.functions.Consumer;
|
||||
import pub.doric.devkit.DevKit;
|
||||
import pub.doric.devkit.DoricDev;
|
||||
import pub.doric.devkit.R;
|
||||
import pub.doric.devkit.event.ConnectExceptionEvent;
|
||||
import pub.doric.devkit.event.EOFExceptionEvent;
|
||||
import pub.doric.devkit.event.OpenEvent;
|
||||
|
||||
public class DevPanel extends BottomSheetDialogFragment {
|
||||
|
||||
public static boolean isDevConnected = false;
|
||||
|
||||
public DevPanel() {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState
|
||||
) {
|
||||
return inflater.inflate(R.layout.layout_dev, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
|
||||
updateUI();
|
||||
|
||||
getView().findViewById(R.id.connect_dev_kit_text_view).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (DevKit.isRunningInEmulator) {
|
||||
DevKit.ip = "10.0.2.2";
|
||||
DoricDev.connectDevKit("ws://" + DevKit.ip + ":7777");
|
||||
} else {
|
||||
final RxPermissions rxPermissions = new RxPermissions(DevPanel.this);
|
||||
Disposable disposable = rxPermissions
|
||||
.request(Manifest.permission.CAMERA)
|
||||
.subscribe(new Consumer<Boolean>() {
|
||||
@Override
|
||||
public void accept(Boolean grant) throws Exception {
|
||||
if (grant) {
|
||||
Intent intent = new Intent(getContext(), ScanQRCodeActivity.class);
|
||||
getContext().startActivity(intent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getView().findViewById(R.id.debug_text_view).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
DebugContextPanel debugContextPanel = new DebugContextPanel();
|
||||
debugContextPanel.show(getActivity().getSupportFragmentManager(), "DebugContextPanel");
|
||||
dismissAllowingStateLoss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
EventBus.getDefault().register(this);
|
||||
DevKit.isRunningInEmulator = EasyProtectorLib.checkIsRunningInEmulator(context, new EmulatorCheckCallback() {
|
||||
@Override
|
||||
public void findEmulator(String emulatorInfo) {
|
||||
System.out.println(emulatorInfo);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetach() {
|
||||
super.onDetach();
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onOpenEvent(OpenEvent openEvent) {
|
||||
updateUI();
|
||||
Toast.makeText(getContext(), "dev kit connected", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onEOFEvent(EOFExceptionEvent eofExceptionEvent) {
|
||||
updateUI();
|
||||
Toast.makeText(getContext(), "dev kit eof exception", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
public void onConnectExceptionEvent(ConnectExceptionEvent connectExceptionEvent) {
|
||||
updateUI();
|
||||
Toast.makeText(getContext(), "dev kit connection exception", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
if (isDevConnected) {
|
||||
getView().findViewById(R.id.connect_dev_kit_text_view).setVisibility(View.GONE);
|
||||
getView().findViewById(R.id.debug_text_view).setVisibility(View.VISIBLE);
|
||||
getView().findViewById(R.id.hot_reload_text_view).setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
getView().findViewById(R.id.connect_dev_kit_text_view).setVisibility(View.VISIBLE);
|
||||
getView().findViewById(R.id.debug_text_view).setVisibility(View.GONE);
|
||||
getView().findViewById(R.id.hot_reload_text_view).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,79 @@
|
||||
package pub.doric.devkit.ui;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import cn.bingoogolapple.qrcode.core.QRCodeView;
|
||||
import cn.bingoogolapple.qrcode.zbar.ZBarView;
|
||||
import pub.doric.devkit.DevKit;
|
||||
import pub.doric.devkit.DoricDev;
|
||||
import pub.doric.devkit.R;
|
||||
|
||||
public class ScanQRCodeActivity extends AppCompatActivity implements QRCodeView.Delegate {
|
||||
|
||||
private ZBarView mZbarView;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.layout_scan_qrcode);
|
||||
mZbarView = findViewById(R.id.zbar_view);
|
||||
mZbarView.setDelegate(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
|
||||
mZbarView.startCamera();
|
||||
mZbarView.startSpotAndShowRect();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
|
||||
mZbarView.stopCamera();
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
mZbarView.onDestroy();
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScanQRCodeSuccess(String result) {
|
||||
setTitle("扫描结果为:" + result);
|
||||
DevKit.ip = result;
|
||||
Toast.makeText(this, "dev kit connecting to " + result, Toast.LENGTH_LONG).show();
|
||||
DoricDev.connectDevKit("ws://" + result + ":7777");
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCameraAmbientBrightnessChanged(boolean isDark) {
|
||||
String tipText = mZbarView.getScanBoxView().getTipText();
|
||||
String ambientBrightnessTip = "\n环境过暗,请打开闪光灯";
|
||||
if (isDark) {
|
||||
if (!tipText.contains(ambientBrightnessTip)) {
|
||||
mZbarView.getScanBoxView().setTipText(tipText + ambientBrightnessTip);
|
||||
}
|
||||
} else {
|
||||
if (tipText.contains(ambientBrightnessTip)) {
|
||||
tipText = tipText.substring(0, tipText.indexOf(ambientBrightnessTip));
|
||||
mZbarView.getScanBoxView().setTipText(tipText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScanQRCodeOpenCameraError() {
|
||||
System.out.println();
|
||||
}
|
||||
}
|
@@ -0,0 +1,113 @@
|
||||
package pub.doric.devkit.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.Sensor;
|
||||
import android.hardware.SensorEvent;
|
||||
import android.hardware.SensorEventListener;
|
||||
import android.hardware.SensorManager;
|
||||
|
||||
public class SensorManagerHelper implements SensorEventListener {
|
||||
|
||||
// 速度阈值,当摇晃速度达到这值后产生作用
|
||||
private final int SPEED_SHRESHOLD = 5000;
|
||||
// 两次检测的时间间隔
|
||||
private final int UPTATE_INTERVAL_TIME = 50;
|
||||
// 传感器管理器
|
||||
private SensorManager sensorManager;
|
||||
// 传感器
|
||||
private Sensor sensor;
|
||||
// 重力感应监听器
|
||||
private OnShakeListener onShakeListener;
|
||||
// 上下文对象context
|
||||
private Context context;
|
||||
// 手机上一个位置时重力感应坐标
|
||||
private float lastX;
|
||||
private float lastY;
|
||||
private float lastZ;
|
||||
// 上次检测时间
|
||||
private long lastUpdateTime;
|
||||
|
||||
public SensorManagerHelper(Context context) {
|
||||
this.context = context;
|
||||
start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 开始检测
|
||||
*/
|
||||
public void start() {
|
||||
// 获得传感器管理器
|
||||
sensorManager = (SensorManager) context
|
||||
.getSystemService(Context.SENSOR_SERVICE);
|
||||
if (sensorManager != null) {
|
||||
// 获得重力传感器
|
||||
sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
|
||||
}
|
||||
// 注册
|
||||
if (sensor != null) {
|
||||
sensorManager.registerListener(this, sensor,
|
||||
SensorManager.SENSOR_DELAY_GAME);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止检测
|
||||
*/
|
||||
public void stop() {
|
||||
sensorManager.unregisterListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 摇晃监听接口
|
||||
*/
|
||||
public interface OnShakeListener {
|
||||
void onShake();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置重力感应监听器
|
||||
*/
|
||||
public void setOnShakeListener(OnShakeListener listener) {
|
||||
onShakeListener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAccuracyChanged(Sensor sensor, int accuracy) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 重力感应器感应获得变化数据
|
||||
* android.hardware.SensorEventListener#onSensorChanged(android.hardware
|
||||
* .SensorEvent)
|
||||
*/
|
||||
@Override
|
||||
public void onSensorChanged(SensorEvent event) {
|
||||
// 现在检测时间
|
||||
long currentUpdateTime = System.currentTimeMillis();
|
||||
// 两次检测的时间间隔
|
||||
long timeInterval = currentUpdateTime - lastUpdateTime;
|
||||
// 判断是否达到了检测时间间隔
|
||||
if (timeInterval < UPTATE_INTERVAL_TIME) return;
|
||||
// 现在的时间变成last时间
|
||||
lastUpdateTime = currentUpdateTime;
|
||||
// 获得x,y,z坐标
|
||||
float x = event.values[0];
|
||||
float y = event.values[1];
|
||||
float z = event.values[2];
|
||||
// 获得x,y,z的变化值
|
||||
float deltaX = x - lastX;
|
||||
float deltaY = y - lastY;
|
||||
float deltaZ = z - lastZ;
|
||||
// 将现在的坐标变成last坐标
|
||||
lastX = x;
|
||||
lastY = y;
|
||||
lastZ = z;
|
||||
double speed = Math.sqrt(deltaX * deltaX + deltaY * deltaY + deltaZ
|
||||
* deltaZ)
|
||||
/ timeInterval * 10000;
|
||||
// 达到速度阀值,发出提示
|
||||
if (speed >= SPEED_SHRESHOLD) {
|
||||
onShakeListener.onShake();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/context_id_text_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:text="{Context Id}"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/source_text_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_weight="3"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:text="{Source}"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/debug_text_view"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="60dp"
|
||||
android:layout_weight="1"
|
||||
android:background="#ff0000"
|
||||
android:gravity="center"
|
||||
android:singleLine="true"
|
||||
android:text="Debug"
|
||||
android:textColor="#ffffff"
|
||||
android:textSize="20sp" />
|
||||
</LinearLayout>
|
34
doric-android/devkit/src/main/res/layout/layout_dev.xml
Normal file
34
doric-android/devkit/src/main/res/layout/layout_dev.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:divider="@drawable/divider"
|
||||
android:orientation="vertical"
|
||||
android:showDividers="middle">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/connect_dev_kit_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:gravity="center"
|
||||
android:text="Dev Kit"
|
||||
android:textSize="20sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/debug_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:gravity="center"
|
||||
android:text="Debug via Visual Studio Code"
|
||||
android:textSize="20sp"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/hot_reload_text_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:gravity="center"
|
||||
android:text="Enable Hot Reload"
|
||||
android:textSize="20sp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
@@ -0,0 +1,22 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<cn.bingoogolapple.qrcode.zbar.ZBarView
|
||||
android:id="@+id/zbar_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:qrcv_animTime="1000"
|
||||
app:qrcv_borderColor="@android:color/white"
|
||||
app:qrcv_borderSize="1dp"
|
||||
app:qrcv_cornerColor="@android:color/white"
|
||||
app:qrcv_cornerLength="20dp"
|
||||
app:qrcv_cornerSize="3dp"
|
||||
app:qrcv_isShowDefaultScanLineDrawable="true"
|
||||
app:qrcv_maskColor="#33FFFFFF"
|
||||
app:qrcv_rectWidth="200dp"
|
||||
app:qrcv_scanLineColor="@android:color/white"
|
||||
app:qrcv_topOffset="90dp" />
|
||||
</LinearLayout>
|
3
doric-android/devkit/src/main/res/values/strings.xml
Normal file
3
doric-android/devkit/src/main/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<string name="app_name">devkit</string>
|
||||
</resources>
|
Reference in New Issue
Block a user