package com.example.heart_oxygen_monitoring_system;
import android.Manifest;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import androidx.activity.EdgeToEdge;
import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.google.gson.Gson;
import com.google.gson.annotations.SerializedName;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.http.Body;
import retrofit2.http.Header;
import retrofit2.http.POST;
public class MainActivity extends AppCompatActivity {
// UI组件
private TextView tvConnectionStatus;
private TextView tvHeartRate;
private TextView tvSpO2;
private TextView tvTemperature;
private Button btnScan;
private EditText etQuestion;
private Button btnAskAI;
private TextView tvAIResponse;
// 蓝牙相关
private BluetoothHelper bluetoothHelper;
private BluetoothAdapter bluetoothAdapter;
private BluetoothSocket bluetoothSocket;
private ConnectedThread connectedThread;
private final ArrayList<BluetoothDevice> deviceList = new ArrayList<>();
// AI相关
private ApiService apiService;
private int currentHeartRate = 0;
private int currentSpO2 = 0;
private float currentTemperature = 0.0f;
//权限和请求码
private static final int REQUEST_ENABLE_BT = 1;
private static final int REQUEST_BLUETOOTH_PERMISSIONS = 2;
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static final String TAG = "MainActivity";
private ActivityResultLauncher<Intent> bluetoothEnableLauncher;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_main);
// 初始化结果监听器
bluetoothEnableLauncher = registerForActivityResult(
new ActivityResultContracts.StartActivityForResult(),
result -> {
if (result.getResultCode() == RESULT_OK) {
// 用户启用了蓝牙
startDeviceDiscovery();
} else {
Toast.makeText(this, "蓝牙未启用", Toast.LENGTH_SHORT).show();
}
});
initViews();
initBluetooth();
setupButtonListeners();
initAIService();
}
private void initViews() {
tvConnectionStatus = findViewById(R.id.tvConnectionStatus);
tvHeartRate = findViewById(R.id.tvHeartRate);
tvSpO2 = findViewById(R.id.tvSpO2);
tvTemperature = findViewById(R.id.tvTemperature);
btnScan = findViewById(R.id.btnScan);
etQuestion = findViewById(R.id.etQuestion);
btnAskAI = findViewById(R.id.btnAskAI);
tvAIResponse = findViewById(R.id.tvAIResponse);
updateBluetoothStatus(false);
resetDataDisplay();
}
private void resetDataDisplay() {
tvHeartRate.setText("--");
tvSpO2.setText("--");
tvTemperature.setText("--");
}
private void initBluetooth() {
bluetoothHelper = new BluetoothHelper(this);
bluetoothAdapter = bluetoothHelper.getBluetoothAdapter();
if (!bluetoothHelper.isBluetoothSupported()) {
Toast.makeText(this, "设备不支持蓝牙", Toast.LENGTH_SHORT).show();
return;
}
// 注册广播接收器
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(deviceReceiver, filter);
}
private void initAIService() {
// 创建HTTP日志拦截器
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
// 创建OkHttpClient
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build();
// 创建Retrofit实例
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.88.105:8880/") // 替换为你的RAGFlow服务器地址
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build();
// 创建API服务
apiService = retrofit.create(ApiService.class);
}
private void setupButtonListeners() {
btnScan.setOnClickListener(v -> {
if (checkPermissions()) {
if (!bluetoothHelper.isBluetoothEnabled()) {
requestEnableBluetooth();
} else {
startDeviceDiscovery();
}
}
});
btnAskAI.setOnClickListener(v -> {
String question = etQuestion.getText().toString().trim();
if (!question.isEmpty()) {
askAI(question);
} else {
Toast.makeText(this, "请输入问题", Toast.LENGTH_SHORT).show();
}
});
}
private void requestEnableBluetooth() {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
bluetoothEnableLauncher.launch(enableBtIntent);
}
private void startDeviceDiscovery() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
deviceList.clear();
if (bluetoothAdapter.isDiscovering()) {
bluetoothAdapter.cancelDiscovery();
}
if (bluetoothAdapter.startDiscovery()) {
Toast.makeText(this, "正在搜索STM32设备...", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "搜索启动失败", Toast.LENGTH_SHORT).show();
}
}
private final BroadcastReceiver deviceReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (ActivityCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) {
return;
}
// 只添加名称包含"STM32"的设备
assert device != null;
if (device.getName() != null && device.getName().contains("STM32")) {
if (!deviceList.contains(device)) {
deviceList.add(device);
showDeviceSelectionDialog();
}
}
}
}
};
private void showDeviceSelectionDialog() {
if (deviceList.isEmpty()) return;
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("选择STM32设备");
ArrayList<String> deviceNames = new ArrayList<>();
for (BluetoothDevice device : deviceList) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT)
!= PackageManager.PERMISSION_GRANTED) {
continue;
}
deviceNames.add(device.getName() + "\n" + device.getAddress());
}
builder.setItems(deviceNames.toArray(new String[0]), (dialog, which) -> connectToDevice(deviceList.get(which)));
builder.setNegativeButton("取消", null);
builder.show();
}
private void connectToDevice(BluetoothDevice device) {
try {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT)
!= PackageManager.PERMISSION_GRANTED) {
return;
}
bluetoothSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
new Thread(() -> {
try {
bluetoothSocket.connect();
runOnUiThread(() -> {
updateBluetoothStatus(true);
Toast.makeText(MainActivity.this,
"已连接到: " + device.getName(),
Toast.LENGTH_SHORT).show();
});
// 启动数据接收线程
connectedThread = new ConnectedThread(bluetoothSocket);
connectedThread.start();
} catch (IOException e) {
runOnUiThread(() -> {
updateBluetoothStatus(false);
Toast.makeText(MainActivity.this,
"连接失败: " + e.getMessage(),
Toast.LENGTH_SHORT).show();
});
}
}).start();
} catch (IOException e) {
Toast.makeText(this, "创建连接失败", Toast.LENGTH_SHORT).show();
}
}
private class ConnectedThread extends Thread {
private final InputStream mmInStream;
private final byte[] mmBuffer = new byte[1024];
public ConnectedThread(BluetoothSocket socket) {
InputStream tmpIn = null;
try {
tmpIn = socket.getInputStream();
} catch (IOException e) {
Log.e(TAG, "获取输入流失败", e);
}
mmInStream = tmpIn;
}
public void run() {
StringBuilder receivedData = new StringBuilder();
while (!Thread.interrupted()) {
try {
int bytes = mmInStream.read(mmBuffer);
String chunk = new String(mmBuffer, 0, bytes);
receivedData.append(chunk);
// 检查是否收到完整数据包
if (chunk.contains("\n")) {
final String completeData = receivedData.toString().trim();
receivedData.setLength(0);
new Handler(Looper.getMainLooper()).post(() ->
processSTM32Data(completeData));
}
} catch (IOException e) {
Log.e(TAG, "连接断开", e);
runOnUiThread(() -> {
updateBluetoothStatus(false);
Toast.makeText(MainActivity.this,
"与设备断开连接", Toast.LENGTH_SHORT).show();
});
break;
}
}
}
}
private void processSTM32Data(String rawData) {
// 示例数据格式: "HR:75,SpO2:98,Temp:36.5"
try {
String[] pairs = rawData.split(",");
for (String pair : pairs) {
String[] keyValue = pair.split(":");
if (keyValue.length == 2) {
String key = keyValue[0].trim();
String value = keyValue[1].trim();
switch (key) {
case "HR":
updateHeartRate(value);
break;
case "SpO2":
updateOxygenLevel(value);
break;
case "Temp":
updateTemperature(value);
break;
}
}
}
} catch (Exception e) {
Log.e(TAG, "数据解析错误: " + rawData, e);
}
}
private void updateHeartRate(String value) {
tvHeartRate.setText(value);
try {
currentHeartRate = Integer.parseInt(value);
tvHeartRate.setTextColor(currentHeartRate < 60 || currentHeartRate > 100 ? Color.RED :
ContextCompat.getColor(this, R.color.primary));
} catch (NumberFormatException e) {
tvHeartRate.setTextColor(ContextCompat.getColor(this, R.color.primary));
}
}
private void updateOxygenLevel(String value) {
tvSpO2.setText(value);
try {
currentSpO2 = Integer.parseInt(value);
tvSpO2.setTextColor(currentSpO2 < 90 ? Color.RED :
ContextCompat.getColor(this, R.color.primary));
} catch (NumberFormatException e) {
tvSpO2.setTextColor(ContextCompat.getColor(this, R.color.primary));
}
}
private void updateTemperature(String value) {
tvTemperature.setText(value);
try {
currentTemperature = Float.parseFloat(value);
} catch (NumberFormatException e) {
// 忽略解析错误
}
}
private void updateBluetoothStatus(boolean isConnected) {
if (isConnected) {
tvConnectionStatus.setText("蓝牙状态: 已连接");
tvConnectionStatus.setTextColor(ContextCompat.getColor(this, R.color.green));
btnScan.setVisibility(View.GONE);
} else {
tvConnectionStatus.setText("蓝牙状态: 未连接");
tvConnectionStatus.setTextColor(ContextCompat.getColor(this, R.color.red));
btnScan.setVisibility(View.VISIBLE);
resetDataDisplay();
}
}
private void askAI(String question) {
// 显示加载中
tvAIResponse.setText("思考中...");
// 构建请求
ChatRequest.Message userMessage = new ChatRequest.Message("user", question);
List<ChatRequest.Message> messages = new ArrayList<>();
messages.add(userMessage);
ChatRequest request = new ChatRequest();
request.setModel("deepseek-r1:1.5b"); // 与你RAGFlow中配置的模型一致
request.setMessages(messages);
request.setStream(false);
// 异步调用
Call<ApiResponse> call = apiService.sendMessage("Bearer your_api_key_if_any", request);
call.enqueue(new Callback<ApiResponse>() {
@SuppressLint("SetTextI18n")
@Override
public void onResponse(@NonNull Call<ApiResponse> call, @NonNull Response<ApiResponse> response) {
if (response.isSuccessful() && response.body() != null) {
// 提取AI回复内容
String aiResponse = response.body().getChoices().get(0).getMessage().getContent();
tvAIResponse.setText(aiResponse);
} else {
tvAIResponse.setText("抱歉,请求失败: " + response.message());
}
}
@SuppressLint("SetTextI18n")
@Override
public void onFailure(@NonNull Call<ApiResponse> call, @NonNull Throwable t) {
tvAIResponse.setText("网络错误: " + t.getMessage());
}
});
}
private void generateHealthAdvice() {
@SuppressLint("DefaultLocale") String healthQuery = String.format(
"我的心率是%d bpm,血氧是%d%%,体温是%.1f°C。请分析我的健康状况并提供简要建议。",
currentHeartRate, currentSpO2, currentTemperature
);
askAI(healthQuery);
}
private boolean checkPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (checkSelfPermission(Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED ||
checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.ACCESS_FINE_LOCATION
}, REQUEST_BLUETOOTH_PERMISSIONS);
return false;
}
} else {
if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{
Manifest.permission.ACCESS_FINE_LOCATION
}, REQUEST_BLUETOOTH_PERMISSIONS);
return false;
}
}
return true;
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_BLUETOOTH_PERMISSIONS) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
startDeviceDiscovery();
} else {
Toast.makeText(this, "需要权限才能使用蓝牙功能", Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == RESULT_OK) {
startDeviceDiscovery();
} else {
Toast.makeText(this, "蓝牙未启用", Toast.LENGTH_SHORT).show();
}
}
}
@SuppressLint("MissingPermission")
@Override
protected void onDestroy() {
super.onDestroy();
// 取消设备发现
if (bluetoothAdapter != null && bluetoothAdapter.isDiscovering()) {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN)
== PackageManager.PERMISSION_GRANTED) {
bluetoothAdapter.cancelDiscovery();
}
}
// 注销广播接收器
try {
unregisterReceiver(deviceReceiver);
} catch (IllegalArgumentException e) {
Log.e(TAG, "接收器未注册");
}
// 关闭连接
if (connectedThread != null) {
connectedThread.interrupt();
}
if (bluetoothSocket != null) {
try {
bluetoothSocket.close();
} catch (IOException e) {
Log.e(TAG, "关闭socket失败", e);
}
}
}
// API请求和响应数据模型
public static class ChatRequest {
private String model;
private List<Message> messages;
private boolean stream = false;
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public List<Message> getMessages() {
return messages;
}
public void setMessages(List<Message> messages) {
this.messages = messages;
}
public boolean isStream() {
return stream;
}
public void setStream(boolean stream) {
this.stream = stream;
}
public static class Message {
private String role;
private String content;
public Message(String role, String content) {
this.role = role;
this.content = content;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
}
public static class ApiResponse {
private String id;
private List<Choice> choices;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public List<Choice> getChoices() {
return choices;
}
public void setChoices(List<Choice> choices) {
this.choices = choices;
}
public static class Choice {
private Message message;
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
public static class Message {
private String role;
private String content;
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
}
}
// API服务接口
public interface ApiService {
@POST("/api/v1/chat/completions") // 根据RAGFlow实际API端点调整
Call<ApiResponse> sendMessage(
@Header("Authorization") String auth,
@Body ChatRequest request
);
}
}
根据报错信息给我修改一下
FATAL EXCEPTION: main
Process: com.example.heart_oxygen_monitoring_system, PID: 1806
java.lang.NullPointerException: Attempt to invoke interface method 'java.lang.Object java.util.List.get(int)' on a null object reference
at com.example.heart_oxygen_monitoring_system.MainActivity$2.onResponse(MainActivity.java:430)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1.lambda$onResponse$0$retrofit2-DefaultCallAdapterFactory$ExecutorCallbackCall$1(DefaultCallAdapterFactory.java:89)
at retrofit2.DefaultCallAdapterFactory$ExecutorCallbackCall$1$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
at android.os.Handler.handleCallback(Handler.java:973)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loopOnce(Looper.java:282)
at android.os.Looper.loop(Looper.java:387)
at android.app.ActivityThread.main(ActivityThread.java:9500)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1005)
最新发布