feat: 控制指令消息改为protobuf二进制

This commit is contained in:
2026-07-15 17:03:58 +08:00
parent 92ad90cbeb
commit da1730ce19
21 changed files with 658 additions and 147 deletions

View File

@@ -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);

View File

@@ -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() {

View File

@@ -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);
}
}
});

View 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;
// 按键KEYkey_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;
}