feat: 控制指令消息改为protobuf二进制
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'com.google.protobuf'
|
||||
}
|
||||
|
||||
def releaseTime() {
|
||||
@@ -124,6 +125,19 @@ android {
|
||||
|
||||
}
|
||||
|
||||
protobuf {
|
||||
protoc {
|
||||
artifact = 'com.google.protobuf:protoc:3.25.1'
|
||||
}
|
||||
generateProtoTasks {
|
||||
all().each { task ->
|
||||
task.builtins {
|
||||
java {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.11.0'
|
||||
@@ -137,6 +151,8 @@ dependencies {
|
||||
// implementation 'org.webrtc:google-webrtc:1.0.32006'
|
||||
// OkHttp for WebSocket
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
|
||||
// Gson for JSON
|
||||
// Gson for JSON(信令消息仍使用 JSON)
|
||||
implementation 'com.google.code.gson:gson:2.10.1'
|
||||
// Protobuf(DataChannel 控制指令二进制)
|
||||
implementation 'com.google.protobuf:protobuf-java:3.25.1'
|
||||
}
|
||||
|
||||
@@ -2,17 +2,18 @@ package com.ttstd.controlled.input;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
import com.ttstd.control.Action;
|
||||
import com.ttstd.control.ControlMessage;
|
||||
|
||||
/**
|
||||
* 处理控制端发来的输入指令(触摸、按键)。
|
||||
* 解析指令并委托给 {@link InputExecutor} 接口执行。
|
||||
* 指令以 protobuf 二进制({@link ControlMessage})通过 DataChannel 传输,
|
||||
* 解析后委托给 {@link InputExecutor} 接口执行。
|
||||
*/
|
||||
public class InputCommandHandler {
|
||||
|
||||
private static final String TAG = "InputCommandHandler";
|
||||
private final Gson gson = new Gson();
|
||||
private final int screenWidth;
|
||||
private final int screenHeight;
|
||||
private final InputExecutor inputExecutor;
|
||||
@@ -24,47 +25,47 @@ public class InputCommandHandler {
|
||||
this.inputExecutor = new SystemInputUtils();
|
||||
}
|
||||
|
||||
public void handleCommand(String commandJson) {
|
||||
public void handleCommand(byte[] data) {
|
||||
try {
|
||||
JsonObject command = gson.fromJson(commandJson, JsonObject.class);
|
||||
String action = command.get("action").getAsString();
|
||||
ControlMessage command = ControlMessage.parseFrom(data);
|
||||
Action action = command.getAction();
|
||||
|
||||
switch (action) {
|
||||
case "TOUCH":
|
||||
case TOUCH:
|
||||
handleTouch(command);
|
||||
break;
|
||||
case "KEY":
|
||||
case KEY:
|
||||
handleKey(command);
|
||||
break;
|
||||
case "SWIPE":
|
||||
case SWIPE:
|
||||
handleSwipe(command);
|
||||
break;
|
||||
case "LONG_PRESS":
|
||||
case LONG_PRESS:
|
||||
handleLongPress(command);
|
||||
break;
|
||||
case "MOTION_EVENT":
|
||||
case MOTION_EVENT:
|
||||
handleMotionEvent(command);
|
||||
break;
|
||||
default:
|
||||
Log.w(TAG, "Unknown action: " + action);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error handling command: " + e.getMessage(), e);
|
||||
} catch (InvalidProtocolBufferException e) {
|
||||
Log.e(TAG, "Error parsing command: " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleMotionEvent(JsonObject command) {
|
||||
int action = command.get("motionAction").getAsInt();
|
||||
float relX = command.get("x").getAsFloat();
|
||||
float relY = command.get("y").getAsFloat();
|
||||
private void handleMotionEvent(ControlMessage command) {
|
||||
int action = command.getMotionAction();
|
||||
float relX = (float) command.getX();
|
||||
float relY = (float) command.getY();
|
||||
int x = (int) (relX * screenWidth);
|
||||
int y = (int) (relY * screenHeight);
|
||||
inputExecutor.injectMotionEvent(action, x, y);
|
||||
}
|
||||
|
||||
private void handleTouch(JsonObject command) {
|
||||
float relX = command.get("x").getAsFloat();
|
||||
float relY = command.get("y").getAsFloat();
|
||||
private void handleTouch(ControlMessage command) {
|
||||
float relX = (float) command.getX();
|
||||
float relY = (float) command.getY();
|
||||
int x = (int) (relX * screenWidth);
|
||||
int y = (int) (relY * screenHeight);
|
||||
|
||||
@@ -72,18 +73,18 @@ public class InputCommandHandler {
|
||||
inputExecutor.injectTap(x, y);
|
||||
}
|
||||
|
||||
private void handleKey(JsonObject command) {
|
||||
int keyCode = command.get("keyCode").getAsInt();
|
||||
private void handleKey(ControlMessage command) {
|
||||
int keyCode = command.getKeyCode();
|
||||
Log.d(TAG, "Key press: " + keyCode);
|
||||
inputExecutor.injectKeyEvent(keyCode);
|
||||
}
|
||||
|
||||
private void handleSwipe(JsonObject command) {
|
||||
float relX1 = command.get("x1").getAsFloat();
|
||||
float relY1 = command.get("y1").getAsFloat();
|
||||
float relX2 = command.get("x2").getAsFloat();
|
||||
float relY2 = command.get("y2").getAsFloat();
|
||||
long duration = command.has("duration") ? command.get("duration").getAsLong() : 300;
|
||||
private void handleSwipe(ControlMessage command) {
|
||||
float relX1 = (float) command.getX1();
|
||||
float relY1 = (float) command.getY1();
|
||||
float relX2 = (float) command.getX2();
|
||||
float relY2 = (float) command.getY2();
|
||||
long duration = command.getDuration();
|
||||
|
||||
int x1 = (int) (relX1 * screenWidth);
|
||||
int y1 = (int) (relY1 * screenHeight);
|
||||
@@ -94,9 +95,9 @@ public class InputCommandHandler {
|
||||
inputExecutor.injectSwipe(x1, y1, x2, y2, duration);
|
||||
}
|
||||
|
||||
private void handleLongPress(JsonObject command) {
|
||||
float relX = command.get("x").getAsFloat();
|
||||
float relY = command.get("y").getAsFloat();
|
||||
private void handleLongPress(ControlMessage command) {
|
||||
float relX = (float) command.getX();
|
||||
float relY = (float) command.getY();
|
||||
int x = (int) (relX * screenWidth);
|
||||
int y = (int) (relY * screenHeight);
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ public class ScreenCaptureService extends Service {
|
||||
// 初始化 WebRTC
|
||||
webRtcClient = new WebRtcClient(this, wsClient, deviceId);
|
||||
webRtcClient.initialize(eglBase);
|
||||
webRtcClient.setInputCallback(commandJson -> inputHandler.handleCommand(commandJson));
|
||||
webRtcClient.setInputCallback(commandBytes -> inputHandler.handleCommand(commandBytes));
|
||||
|
||||
// 初始化屏幕捕获
|
||||
screenCapturer = new ScreenCapturerAndroid(resultData, new MediaProjection.Callback() {
|
||||
|
||||
@@ -59,7 +59,7 @@ public class WebRtcClient {
|
||||
private InputCommandCallback inputCallback;
|
||||
|
||||
public interface InputCommandCallback {
|
||||
void onCommandReceived(String commandJson);
|
||||
void onCommandReceived(byte[] data);
|
||||
}
|
||||
|
||||
public WebRtcClient(Context context, WebSocketClient wsClient, String deviceId) {
|
||||
@@ -316,10 +316,9 @@ public class WebRtcClient {
|
||||
public void onMessage(DataChannel.Buffer buffer) {
|
||||
byte[] data = new byte[buffer.data.remaining()];
|
||||
buffer.data.get(data);
|
||||
String message = new String(data, StandardCharsets.UTF_8);
|
||||
Log.d(TAG, "DataChannel message: " + message);
|
||||
Log.d(TAG, "DataChannel message received, bytes=" + data.length);
|
||||
if (inputCallback != null) {
|
||||
inputCallback.onCommandReceived(message);
|
||||
inputCallback.onCommandReceived(data);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
41
WebRTCControlled/app/src/main/proto/control_message.proto
Normal file
41
WebRTCControlled/app/src/main/proto/control_message.proto
Normal file
@@ -0,0 +1,41 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package com.ttstd.control;
|
||||
|
||||
option java_package = "com.ttstd.control";
|
||||
option java_multiple_files = true;
|
||||
|
||||
// 控制指令类型,对应原 JSON 字段 action。
|
||||
enum Action {
|
||||
ACTION_UNKNOWN = 0;
|
||||
TOUCH = 1;
|
||||
SWIPE = 2;
|
||||
KEY = 3;
|
||||
LONG_PRESS = 4;
|
||||
MOTION_EVENT = 5;
|
||||
}
|
||||
|
||||
// 通过 RTCDataChannel 传输的控制指令(protobuf 二进制)。
|
||||
// 坐标 x/y/x1/y1/x2/y2 均为相对屏幕的百分比,取值范围 0.0 ~ 1.0。
|
||||
message ControlMessage {
|
||||
// 指令类型
|
||||
Action action = 1;
|
||||
|
||||
// 单点坐标(TOUCH / LONG_PRESS / MOTION_EVENT)
|
||||
double x = 2;
|
||||
double y = 3;
|
||||
|
||||
// 滑动起止坐标(SWIPE)
|
||||
double x1 = 4;
|
||||
double y1 = 5;
|
||||
double x2 = 6;
|
||||
double y2 = 7;
|
||||
int64 duration = 8;
|
||||
|
||||
// 按键(KEY):key_action 0=按下 1=抬起(对应 Android KeyEvent ACTION_DOWN/UP)
|
||||
int32 key_code = 9;
|
||||
int32 key_action = 10;
|
||||
|
||||
// 原始动作(MOTION_EVENT):对应 Android MotionEvent ACTION_*(0=DOWN 1=UP 2=MOVE)
|
||||
int32 motion_action = 11;
|
||||
}
|
||||
Reference in New Issue
Block a user