feat: add Xiaomi Wearable SDK AAR + real WatchBridge and watch<->server relay

This commit is contained in:
2026-09-01 18:53:58 +08:00
parent 41a9f81c0e
commit 42ad836e95
4 changed files with 117 additions and 23 deletions
+1
View File
@@ -23,6 +23,7 @@ android {
} }
} }
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0' implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4' implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
Binary file not shown.
@@ -13,7 +13,8 @@ import org.json.JSONObject;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private TextView status; private TextView status;
private ServerClient client; private ServerClient serverClient;
private WatchBridge watchBridge;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@@ -22,30 +23,33 @@ public class MainActivity extends AppCompatActivity {
status = findViewById(R.id.status_text); status = findViewById(R.id.status_text);
client = new ServerClient(new ServerClient.Listener() { // 服务器客户端
serverClient = new ServerClient(new ServerClient.Listener() {
@Override @Override
public void onConnectionChanged(boolean connected) { public void onConnectionChanged(boolean connected) {
runOnUiThread(() -> status.setText(connected ? "已连接(空壳)" : "连接断开")); runOnUiThread(() -> status.setText(connected ? "服务器已连接" : "服务器断开"));
} }
@Override @Override
public void onHello(JSONObject hello) { public void onHello(JSONObject hello) {
// 连接建立后拉一次状态 serverClient.sendRequest("harness.status", new JSONObject());
client.sendRequest("harness.status", new JSONObject());
} }
@Override @Override
public void onStatus(JSONObject s) { public void onStatus(JSONObject s) {
runOnUiThread(() -> {
if (s != null) { if (s != null) {
status.setText("状态: " + s.optString("model") + " / " + s.optString("cwd")); runOnUiThread(() -> status.setText("状态: " + s.optString("model") + " / " + s.optString("cwd")));
}
if (watchBridge != null) {
watchBridge.sendToWatch(s.toString());
} }
});
} }
@Override @Override
public void onEvent(JSONObject event) { public void onEvent(JSONObject event) {
// 空壳:后续写入事件流 if (watchBridge != null) {
watchBridge.sendToWatch(event.toString());
}
} }
@Override @Override
@@ -59,6 +63,36 @@ public class MainActivity extends AppCompatActivity {
} }
}); });
// 手表互联桥
watchBridge = new WatchBridge(this, new WatchBridge.Listener() {
@Override
public void onWatchConnected(boolean connected, String nodeId) {
runOnUiThread(() -> status.setText(connected ? "手表已连接: " + nodeId : "未找到手表"));
}
@Override
public void onWatchMessage(String json) {
try {
JSONObject msg = new JSONObject(json);
String type = msg.optString("type", "");
if ("command".equals(type) && "prompt".equals(msg.optString("kind", ""))) {
JSONObject payload = msg.optJSONObject("payload");
String content = payload != null ? payload.optString("content", "") : "";
JSONObject params = new JSONObject();
params.put("content", content);
serverClient.sendRequest("harness.prompt", params);
}
} catch (Exception e) {
// ignore malformed
}
}
@Override
public void onError(String message) {
runOnUiThread(() -> status.setText("WatchBridge: " + message));
}
});
Button settings = findViewById(R.id.btn_settings); Button settings = findViewById(R.id.btn_settings);
settings.setOnClickListener(new View.OnClickListener() { settings.setOnClickListener(new View.OnClickListener() {
@Override @Override
@@ -73,7 +107,8 @@ public class MainActivity extends AppCompatActivity {
public void onClick(View v) { public void onClick(View v) {
status.setText("连接中..."); status.setText("连接中...");
// 空壳:默认连本机模拟器 server;真实地址/用户/Token 从设置读取 // 空壳:默认连本机模拟器 server;真实地址/用户/Token 从设置读取
client.connect("ws://10.0.2.2:8787/ws", "dshwatch", "change-me"); serverClient.connect("ws://10.0.2.2:8787/ws", "dshwatch", "change-me");
watchBridge.connect();
} }
}); });
} }
@@ -81,6 +116,6 @@ public class MainActivity extends AppCompatActivity {
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
if (client != null) client.close(); if (serverClient != null) serverClient.close();
} }
} }
@@ -1,15 +1,73 @@
package com.dshwatch.app; package com.dshwatch.app;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import com.xiaomi.xms.wearable.Wearable;
import com.xiaomi.xms.wearable.message.MessageApi;
import com.xiaomi.xms.wearable.message.OnMessageReceivedListener;
import com.xiaomi.xms.wearable.node.Node;
import com.xiaomi.xms.wearable.node.NodeApi;
/** /**
* 手表 BLE 互联桥(空壳)。 * 手表 BLE 互联桥(使用小米穿戴 xms-wearable SDK)。
* 后续接入小米穿戴 SDK:Wearable / MessageApi / sendMessage / addListener, * 负责:发现手表节点、注册消息监听、把 JSON 发给手表、把收到的消息回传。
* 用于把手表(DSHWatch) 与手机端双向转发。 * 与 ServerClient 联动即可实现 手机(Server) <-> 手表(Watch) 双向转发。
*/ */
public interface WatchBridge { public class WatchBridge {
/** 连接手表(小米穿戴服务) */
void connect(); public interface Listener {
/** 发送消息给手表 */ void onWatchConnected(boolean connected, String nodeId);
void sendToWatch(String json); void onWatchMessage(String json);
/** 接收来自手表的消息 */ void onError(String message);
String onWatchMessage(String json); }
private final Context context;
private final Listener listener;
private final Handler main = new Handler(Looper.getMainLooper());
private final NodeApi nodeApi;
private final MessageApi messageApi;
private Node node;
public WatchBridge(Context context, Listener listener) {
this.context = context.getApplicationContext();
this.listener = listener;
this.nodeApi = Wearable.getNodeApi(this.context);
this.messageApi = Wearable.getMessageApi(this.context);
}
public void connect() {
nodeApi.getConnectedNodes()
.addOnSuccessListener(nodes -> {
if (nodes != null && nodes.size() > 0) {
node = nodes.get(0);
main.post(() -> listener.onWatchConnected(true, node.getId()));
registerListener();
} else {
main.post(() -> listener.onWatchConnected(false, null));
}
})
.addOnFailureListener(e -> main.post(() -> listener.onError("getConnectedNodes: " + e.getMessage())));
}
private void registerListener() {
OnMessageReceivedListener l = new OnMessageReceivedListener() {
@Override
public void onMessageReceived(String nodeId, byte[] message) {
final String json = new String(message);
main.post(() -> listener.onWatchMessage(json));
}
};
messageApi.addListener(node.getId(), l)
.addOnSuccessListener(ignore -> { })
.addOnFailureListener(e -> main.post(() -> listener.onError("addListener: " + e.getMessage())));
}
public void sendToWatch(String json) {
if (node == null) return;
messageApi.sendMessage(node.getId(), json.getBytes())
.addOnSuccessListener(ignore -> { })
.addOnFailureListener(e -> main.post(() -> listener.onError("sendMessage: " + e.getMessage())));
}
} }