diff --git a/app/build.gradle b/app/build.gradle index 7f92a92..111e6d0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,6 +23,7 @@ android { } } dependencies { + implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.9.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' diff --git a/app/libs/xms-wearable-lib_1.4_release.aar b/app/libs/xms-wearable-lib_1.4_release.aar new file mode 100644 index 0000000..3291866 Binary files /dev/null and b/app/libs/xms-wearable-lib_1.4_release.aar differ diff --git a/app/src/main/java/com/dshwatch/app/MainActivity.java b/app/src/main/java/com/dshwatch/app/MainActivity.java index 682a0e3..44832d0 100644 --- a/app/src/main/java/com/dshwatch/app/MainActivity.java +++ b/app/src/main/java/com/dshwatch/app/MainActivity.java @@ -13,7 +13,8 @@ import org.json.JSONObject; public class MainActivity extends AppCompatActivity { private TextView status; - private ServerClient client; + private ServerClient serverClient; + private WatchBridge watchBridge; @Override protected void onCreate(Bundle savedInstanceState) { @@ -22,30 +23,33 @@ public class MainActivity extends AppCompatActivity { status = findViewById(R.id.status_text); - client = new ServerClient(new ServerClient.Listener() { + // 服务器客户端 + serverClient = new ServerClient(new ServerClient.Listener() { @Override public void onConnectionChanged(boolean connected) { - runOnUiThread(() -> status.setText(connected ? "已连接(空壳)" : "连接断开")); + runOnUiThread(() -> status.setText(connected ? "服务器已连接" : "服务器断开")); } @Override public void onHello(JSONObject hello) { - // 连接建立后拉一次状态 - client.sendRequest("harness.status", new JSONObject()); + serverClient.sendRequest("harness.status", new JSONObject()); } @Override public void onStatus(JSONObject s) { - runOnUiThread(() -> { - if (s != null) { - status.setText("状态: " + s.optString("model") + " / " + s.optString("cwd")); - } - }); + if (s != null) { + runOnUiThread(() -> status.setText("状态: " + s.optString("model") + " / " + s.optString("cwd"))); + } + if (watchBridge != null) { + watchBridge.sendToWatch(s.toString()); + } } @Override public void onEvent(JSONObject event) { - // 空壳:后续写入事件流 + if (watchBridge != null) { + watchBridge.sendToWatch(event.toString()); + } } @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); settings.setOnClickListener(new View.OnClickListener() { @Override @@ -73,7 +107,8 @@ public class MainActivity extends AppCompatActivity { public void onClick(View v) { status.setText("连接中..."); // 空壳:默认连本机模拟器 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 protected void onDestroy() { super.onDestroy(); - if (client != null) client.close(); + if (serverClient != null) serverClient.close(); } } diff --git a/app/src/main/java/com/dshwatch/app/WatchBridge.java b/app/src/main/java/com/dshwatch/app/WatchBridge.java index 11ec0b8..c80df31 100644 --- a/app/src/main/java/com/dshwatch/app/WatchBridge.java +++ b/app/src/main/java/com/dshwatch/app/WatchBridge.java @@ -1,15 +1,73 @@ 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 互联桥(空壳)。 - * 后续接入小米穿戴 SDK:Wearable / MessageApi / sendMessage / addListener, - * 用于把手表(DSHWatch) 与手机端双向转发。 + * 手表 BLE 互联桥(使用小米穿戴 xms-wearable SDK)。 + * 负责:发现手表节点、注册消息监听、把 JSON 发给手表、把收到的消息回传。 + * 与 ServerClient 联动即可实现 手机(Server) <-> 手表(Watch) 双向转发。 */ -public interface WatchBridge { - /** 连接手表(小米穿戴服务) */ - void connect(); - /** 发送消息给手表 */ - void sendToWatch(String json); - /** 接收来自手表的消息 */ - String onWatchMessage(String json); +public class WatchBridge { + + public interface Listener { + void onWatchConnected(boolean connected, String nodeId); + void onWatchMessage(String json); + void onError(String message); + } + + 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()))); + } }