app/src/main/AndroidManifest.xml
@@ -55,6 +55,8 @@ /> <activity android:name=".activity.TestActivity" android:screenOrientation="portrait" /> <activity android:name=".activity.SystemSettingsActivity" android:screenOrientation="portrait" /> </application> app/src/main/java/com/shlb/comb/activity/AdvancedSettingActivity.java
@@ -37,6 +37,8 @@ private TextView tv_sensor_status; private StringBuilder logBuilder = new StringBuilder(); private String currentExecutingCmd = ""; private View currentOperatingButton; @Override protected void contentView() { @@ -96,18 +98,26 @@ int id = v.getId(); String action = ""; if (id == R.id.btn_mainboard_read) { currentOperatingButton = btn_mainboard_read; updateButtonState(currentOperatingButton, false); action = "读å主æ¿çæ¬"; currentExecutingCmd = CMD.READ_BOARD_VERSION; sendCmdWithCrc(CMD.READ_BOARD_VERSION); } else if (id == R.id.btn_display_read) { currentOperatingButton = btn_display_read; updateButtonState(currentOperatingButton, false); action = "读åä¼ æå¨çæ¬"; currentExecutingCmd = CMD.READ_SENSOR_VERSION; sendCmdWithCrc(CMD.READ_SENSOR_VERSION); } else if (id == R.id.btn_start_addressing) { currentOperatingButton = btn_start_addressing; updateButtonState(currentOperatingButton, false); action = "å¼å§ç¼å"; currentExecutingCmd = CMD.WRITE_START_ADDRESS; sendCmdWithCrc(CMD.WRITE_START_ADDRESS); } else if (id == R.id.btn_end_addressing) { currentOperatingButton = btn_end_addressing; updateButtonState(currentOperatingButton, false); action = "ç»æç¼å"; currentExecutingCmd = CMD.WRITE_END_ADDRESS; sendCmdWithCrc(CMD.WRITE_END_ADDRESS); @@ -265,6 +275,24 @@ } } private void updateButtonState(View btn, boolean enable) { if (btn == null) return; btn.setEnabled(enable); if (enable) { btn.getBackground().clearColorFilter(); } else { btn.getBackground().setColorFilter(android.graphics.Color.GRAY, android.graphics.PorterDuff.Mode.MULTIPLY); } } private void restoreCurrentButton() { if (currentOperatingButton != null) { updateButtonState(currentOperatingButton, true); currentOperatingButton = null; } } @Subscribe(threadMode = ThreadMode.MAIN) public void onEvent(UpdateEvent event) { if (event.getType() == UpdateEvent.Type.CONN_STATU) { @@ -284,12 +312,14 @@ // åå ¥æä»¤çè¿å (A55A06å¼å¤´) if (hex.startsWith("A55A06") && hex.length() >= 12) { restoreCurrentButton(); parseWriteResponse(hex); return; } // 读åæä»¤çè¿å (A55A03å¼å¤´) if (hex.startsWith("A55A03") && hex.length() >= 8) { restoreCurrentButton(); parseReadResponse(hex); } } app/src/main/java/com/shlb/comb/activity/HomeActivity.java
@@ -121,7 +121,7 @@ } }); } if (iv_right != null) iv_right.setVisibility(View.VISIBLE); if (iv_right != null) iv_right.setVisibility(View.GONE); } }); @@ -176,14 +176,37 @@ }); // é«çº§è®¾ç½® findViewById(R.id.menu_advanced_settings).setOnClickListener(v -> { View menuAdvancedSettings = findViewById(R.id.menu_advanced_settings); menuAdvancedSettings.setVisibility(View.GONE); menuAdvancedSettings.setOnClickListener(v -> { drawerLayout.closeDrawer(GravityCompat.START); startActivity(new Intent(this, AdvancedSettingActivity.class)); }); // ç³»ç»è®¾ç½® View menuSystemSettings = findViewById(R.id.menu_system_settings); menuSystemSettings.setVisibility(View.GONE); menuSystemSettings.setOnClickListener(v -> { drawerLayout.closeDrawer(GravityCompat.START); startActivity(new Intent(this, SystemSettingsActivity.class)); }); // é¡¶é¨combç¹å»äºä»¶ findViewById(R.id.tv_menu_title).setOnClickListener(new View.OnClickListener() { long lastClickTime = 0; @Override public void onClick(View v) { long currentTime = System.currentTimeMillis(); if (currentTime - lastClickTime < 500) { showPasswordDialog(); } lastClickTime = currentTime; } }); } private void updateDrawerSelection(int selectedId) { int[] menuIds = {R.id.menu_monitor, R.id.menu_devices, R.id.menu_current_device, R.id.menu_settings, R.id.menu_advanced_settings}; int[] menuIds = {R.id.menu_monitor, R.id.menu_devices, R.id.menu_current_device, R.id.menu_settings, R.id.menu_advanced_settings, R.id.menu_system_settings}; for (int id : menuIds) { View view = findViewById(id); if (view != null) { @@ -237,7 +260,7 @@ } }); } if (iv_right != null) iv_right.setVisibility(View.VISIBLE); if (iv_right != null) iv_right.setVisibility(View.GONE); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); if (deviceListFragment == null) { @@ -352,4 +375,23 @@ finish(); } } private void showPasswordDialog() { final QMUIDialog.EditTextDialogBuilder builder = new QMUIDialog.EditTextDialogBuilder(this); builder.setTitle("请è¾å ¥å¯ç ") .setPlaceholder("请è¾å ¥å¯ç ") .setInputType(android.text.InputType.TYPE_CLASS_NUMBER | android.text.InputType.TYPE_NUMBER_VARIATION_PASSWORD) .addAction("åæ¶", (dialog, index) -> dialog.dismiss()) .addAction("ç¡®å®", (dialog, index) -> { String text = builder.getEditText().getText().toString(); if ("0880".equals(text)) { findViewById(R.id.menu_advanced_settings).setVisibility(View.VISIBLE); findViewById(R.id.menu_system_settings).setVisibility(View.VISIBLE); Toast("å·²æ¾ç¤ºéè设置"); dialog.dismiss(); } else { Toast("å¯ç é误"); } }) .create(com.qmuiteam.qmui.R.style.QMUI_Dialog).show(); } } app/src/main/java/com/shlb/comb/activity/ParameterSettingActivity.java
@@ -36,35 +36,43 @@ public class ParameterSettingActivity extends BaseActivity { // éå¼è®¾å®ç¸å ³æ§ä»¶ private Spinner sp_threshold_mode; private EditText et_threshold_value; private Button btn_threshold_read; private Button btn_threshold_write; // çµæåº¦è®¾å®ç¸å ³æ§ä»¶ private Spinner sp_sensitivity_mode; private EditText et_sensitivity_value; private Button btn_sensitivity_read; private Button btn_sensitivity_write; // èªæ£åå¤ä½æé® private Button btn_start_check; private Button btn_end_check; private Button btn_reset_mainboard; private Button btn_reset_sensor; // æ¥å¿åç¶ææ¾ç¤º private TextView tv_log; private TextView tv_right_text; private androidx.cardview.widget.CardView cvLog; // New components // èªæ£çæµç½æ ¼åæ¥å¿æ§ä»¶ private RecyclerView rvGrid; private QMUIRoundButton btnReadData; private QMUIRoundButton btnClearLog; private ScrollView svLog; // æ°æ®ç¸å ³ private GridAdapter mAdapter; private List<BoxStatus> boxStatusList = new ArrayList<>(); private StringBuilder logBuilder = new StringBuilder(); private boolean isSelfCheckMode = false; private String pendingSettingCmd = ""; private boolean isSelfCheckMode = false; // æ è®°å½åæ¯å¦å¤äºèªæ£æ¨¡å¼ private String pendingSettingCmd = ""; // è®°å½å¾ å¤ççè®¾å®æä»¤ private Button currentOperatingButton; // å½åæ£å¨æä½çæé® private String pendingToastMessage = null; // å¾ æ¾ç¤ºçToastæ¶æ¯ private SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss", Locale.getDefault()); @@ -85,12 +93,12 @@ sp_threshold_mode = findViewById(R.id.sp_threshold_mode); et_threshold_value = findViewById(R.id.et_threshold_value); btn_threshold_read = findViewById(R.id.btn_threshold_read); btn_threshold_write = findViewById(R.id.btn_threshold_write); sp_sensitivity_mode = findViewById(R.id.sp_sensitivity_mode); et_sensitivity_value = findViewById(R.id.et_sensitivity_value); btn_sensitivity_read = findViewById(R.id.btn_sensitivity_read); btn_sensitivity_write = findViewById(R.id.btn_sensitivity_write); btn_start_check = findViewById(R.id.btn_start_check); @@ -99,15 +107,15 @@ btn_reset_sensor = findViewById(R.id.btn_reset_sensor); tv_log = findViewById(R.id.tv_log); cvLog = findViewById(R.id.cv_log); tv_right_text = findViewById(R.id.tv_right_text); // Init new components // åå§åèªæ£çæµç¸å ³æ§ä»¶ rvGrid = findViewById(R.id.rv_grid); // btnReadData was removed from layout btnClearLog = findViewById(R.id.btn_clear_log); svLog = findViewById(R.id.sv_log); // Log setup // 设置æ¥å¿æ»å¨è§å¾ç触æ¸çå¬ï¼é²æ¢æ»å¨å²çª if (svLog != null) { svLog.setOnTouchListener((v, event) -> { v.getParent().requestDisallowInterceptTouchEvent(true); @@ -118,6 +126,7 @@ }); } // åå§åæ¥å¿å 容 if (logBuilder.length() > 0) { tv_log.setText(Html.fromHtml(logBuilder.toString())); } else { @@ -130,25 +139,36 @@ protected void initData() { updateBluetoothStatus(); // Initialize box status list // åå§å30ä¸ªæ ¼åçç¶æå表 boxStatusList.clear(); for (int i = 1; i <= 30; i++) { boxStatusList.add(new BoxStatus(i)); } // Grid Setup // è®¾ç½®ç½æ ¼å¸å±ï¼3è¡10å rvGrid.setLayoutManager(new GridLayoutManager(this, 10)); mAdapter = new GridAdapter(); rvGrid.setAdapter(mAdapter); // 妿èçå·²è¿æ¥ï¼è¿å ¥è®¾å®æ¨¡å¼ if (BleGlobalManager.getInstance().isConnected()) { sendCmdWithCrc(CMD.ENTER_SETTING); // sendCmdWithCrc(CMD.READ_DATA); } } @Override protected void onResume() { super.onResume(); // æ ¹æ®ç³»ç»è®¾ç½®æ§å¶æ¥å¿æ¾ç¤º boolean isCmdLogEnabled = com.blankj.utilcode.util.SPUtils.getInstance().getBoolean("cmd_log_enabled", false); if (cvLog != null) { cvLog.setVisibility(isCmdLogEnabled ? View.VISIBLE : View.GONE); } } @Override protected void onDestroy() { // 页é¢éæ¯æ¶éåºè®¾å®æ¨¡å¼ if (BleGlobalManager.getInstance().isConnected()) { sendCmdWithCrc(CMD.EXIT_SETTING); } @@ -162,27 +182,37 @@ public void onClick(View v) { int id = v.getId(); String action = ""; if (id == R.id.btn_threshold_read) { action = "读åéå¼"; Toast.makeText(ParameterSettingActivity.this, "该åè½ææªå¼æ¾", Toast.LENGTH_SHORT).show(); } else if (id == R.id.btn_threshold_write) { if (id == R.id.btn_threshold_write) { handleThresholdWrite(); } else if (id == R.id.btn_sensitivity_read) { action = "读åçµæåº¦"; Toast.makeText(ParameterSettingActivity.this, "该åè½ææªå¼æ¾", Toast.LENGTH_SHORT).show(); } else if (id == R.id.btn_sensitivity_write) { handleSensitivityWrite(); } else if (id == R.id.btn_start_check) { action = "å¼å§èªæ£"; sendCmdWithCrc(CMD.WRITE_START_CHECK); isSelfCheckMode = true; if (BleGlobalManager.getInstance().isConnected()) { currentOperatingButton = btn_start_check; updateButtonState(currentOperatingButton, false); action = "å¼å§èªæ£"; setAllGridGray(); sendCmdWithCrc(CMD.WRITE_START_CHECK); isSelfCheckMode = true; } else { Toast.makeText(ParameterSettingActivity.this, "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); } } else if (id == R.id.btn_end_check) { action = "ç»æèªæ£"; sendCmdWithCrc(CMD.WRITE_END_CHECK); isSelfCheckMode = false; if (BleGlobalManager.getInstance().isConnected()) { currentOperatingButton = btn_end_check; updateButtonState(currentOperatingButton, false); action = "ç»æèªæ£"; setAllGridGray(); sendCmdWithCrc(CMD.WRITE_END_CHECK); isSelfCheckMode = false; } else { Toast.makeText(ParameterSettingActivity.this, "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); } } else if (id == R.id.btn_reset_mainboard) { currentOperatingButton = btn_reset_mainboard; showResetConfirmDialog("ç¡®å®è¦æ§è¡ä¸»æ¿å¤ä½åï¼", CMD.WRITE_BOARD_RESET); } else if (id == R.id.btn_reset_sensor) { currentOperatingButton = btn_reset_sensor; showResetConfirmDialog("ç¡®å®è¦æ§è¡ä¼ æå¨å¤ä½åï¼", CMD.WRITE_SENSOR_RESET); } @@ -193,14 +223,59 @@ } }; btn_threshold_read.setOnClickListener(listener); btn_threshold_write.setOnClickListener(listener); btn_sensitivity_read.setOnClickListener(listener); btn_sensitivity_write.setOnClickListener(listener); btn_start_check.setOnClickListener(listener); btn_end_check.setOnClickListener(listener); btn_reset_mainboard.setOnClickListener(listener); btn_reset_sensor.setOnClickListener(listener); // é弿¨¡å¼éæ©çå¬ï¼æ§å¶è¾å ¥æ¡æ¯å¦å¯ç¨ sp_threshold_mode.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) { if (position == 0) { // ä¸é®è®¾å®ï¼éèè¾å ¥æ¡ (使ç¨INVISIBLEä¿çå ä½ï¼é²æ¢æé®ç§»ä½) et_threshold_value.setVisibility(View.INVISIBLE); et_threshold_value.setText(""); } else { // åå±è®¾å®ï¼æ¾ç¤ºè¾å ¥æ¡ et_threshold_value.setVisibility(View.VISIBLE); int maxLayer = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 30); et_threshold_value.setHint("1-" + maxLayer); } } @Override public void onNothingSelected(android.widget.AdapterView<?> parent) { } }); // çµæåº¦æ¨¡å¼éæ©çå¬ï¼æ§å¶è¾å ¥æ¡æ¯å¦å¯ç¨ sp_sensitivity_mode.setOnItemSelectedListener(new android.widget.AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(android.widget.AdapterView<?> parent, View view, int position, long id) { // 0: æ¹éå¢å , 1: æ¹éåå°, 2: åå±å¢å , 3: åå±åå° if (position == 0 || position == 1) { // æ¹éæä½ï¼éèè¾å ¥æ¡ et_sensitivity_value.setVisibility(View.INVISIBLE); et_sensitivity_value.setText(""); } else { // å屿ä½ï¼æ¾ç¤ºè¾å ¥æ¡ et_sensitivity_value.setVisibility(View.VISIBLE); int maxLayer = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 30); et_sensitivity_value.setHint("1-" + maxLayer); } } @Override public void onNothingSelected(android.widget.AdapterView<?> parent) { } }); btnClearLog.setOnClickListener(v -> { logBuilder.setLength(0); @@ -212,7 +287,7 @@ private String getCmdDescription(String hex, Boolean isSent) { if (hex == null) return ""; // Special handling for 3B (Enter/Exit Setting) // è¿å ¥/éåºè®¾å®æ¨¡å¼æä»¤ç¹æ®å¤ç if (hex.startsWith(CMD.ENTER_SETTING.substring(0, 8))) { if (hex.startsWith(CMD.ENTER_SETTING)) return "-è¿å ¥è®¾å®"; if (hex.startsWith(CMD.EXIT_SETTING)) return "-éåºè®¾å®"; @@ -249,7 +324,7 @@ String displayMsg = msg; String cmdDesc = ""; // å¦ææ¯ hex æä»¤ (纯 0-9 A-F a-f)ï¼å ç©ºæ ¼æ ¼å¼å // 妿æ¯åå è¿å¶æä»¤ï¼æ¯ä¸¤ä½å ä¸ä¸ªç©ºæ ¼æ ¼å¼åæ¾ç¤º if (msg.matches("^[0-9A-Fa-f]+$")) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < msg.length(); i += 2) { @@ -267,8 +342,8 @@ String logLine; if (isSent != null) { String color = isSent ? "#1890ff" : "#05aa87"; // Blue for sent, Green for received // æ¼æ¥å°åç¼åé¢: "åé-读ç«å·: " æ "æ¶å°-读ç«å·: " // åéæä»¤ç¨èè²ï¼æ¥æ¶æ°æ®ç¨ç»¿è² String color = isSent ? "#1890ff" : "#05aa87"; String prefix = (isSent ? "åé" : "æ¶å°") + cmdDesc + ": "; logLine = time + " <font color='" + color + "'>" + prefix + displayMsg + "</font><br>"; } else { @@ -278,7 +353,7 @@ logBuilder.append(logLine); // Update UI // æ´æ°æ¥å¿æ¾ç¤ºå¹¶èªå¨æ»å¨å°åºé¨ if (tv_log != null) { tv_log.setText(Html.fromHtml(logBuilder.toString())); if (svLog != null) { @@ -287,7 +362,7 @@ } } // Kept for backward compatibility if needed, but redirected to appendLog // å ¼å®¹æ§ä»£ç çæ¥å¿æ¹æ³ private void addLog(String message) { appendLog(message); } @@ -298,7 +373,7 @@ updateBluetoothStatus(); } else if (event.getType() == UpdateEvent.Type.DEVICE_INFO) { String hex = event.getMsg(); appendLog(hex, false); // false for received (Green) appendLog(hex, false); // falseè¡¨ç¤ºæ¥æ¶çæ°æ®ï¼æ¾ç¤ºä¸ºç»¿è² parseAndRefresh(hex); } } @@ -306,11 +381,13 @@ private void parseAndRefresh(String hex) { if (hex == null) return; // åå ¥æä»¤çè¿å (A55A06å¼å¤´) if (hex.startsWith("A55A06") && hex.length() >= 12) { // å¤çåå ¥æä»¤çè¿åï¼A55A06å¼å¤´ï¼ if (hex.startsWith(CMD.WRITE_SUFFIX) && hex.length() >= 12) { // æåç¶æåèï¼01表示æå String statusHex = hex.substring(10, 12); boolean isSuccess = "01".equals(statusHex); // å¤çè¿å ¥è®¾å®æ¨¡å¼çè¿å if (hex.startsWith(CMD.ENTER_SETTING.substring(0, 8))) { if (isSuccess) { appendLog("è¿å ¥è®¾å®æ¨¡å¼æå"); @@ -320,57 +397,108 @@ return; } // å¤çéåºè®¾å®æ¨¡å¼çè¿å if (hex.startsWith(CMD.EXIT_SETTING.substring(0, 8))) { appendLog("éåºè®¾å®æ¨¡å¼æå"); return; } // å¤çéå¼è®¾å®çè¿å if (hex.startsWith(CMD.WRITE_THRESHOLD_SETTING.substring(0, 8))) { if (isSuccess) { appendLog("éå¼åå ¥æå"); Toast.makeText(this, "éå¼åå ¥æå", Toast.LENGTH_SHORT).show(); pendingToastMessage = "éå¼åå ¥æå"; // éå¼åå ¥æååï¼çå¾ 500毫ç§åè¯»åæ°æ® appendLog("500毫ç§å读åéå¼è®¾å®ç»æ..."); new android.os.Handler().postDelayed(() -> { sendCmdWithCrc(CMD.READ_DATA); }, 500); } else { restoreCurrentButton(); appendLog("éå¼åå ¥å¤±è´¥"); Toast.makeText(this, "éå¼åå ¥å¤±è´¥", Toast.LENGTH_SHORT).show(); } } else if (hex.startsWith(CMD.WRITE_SENSITIVITY_SETTING.substring(0, 8))) { // å¤ççµæåº¦è®¾å®çè¿å if (isSuccess) { appendLog("çµæåº¦åå ¥æå"); Toast.makeText(this, "çµæåº¦åå ¥æå", Toast.LENGTH_SHORT).show(); pendingToastMessage = "çµæåº¦åå ¥æå"; // çµæåº¦åå ¥æååï¼çå¾ 500毫ç§åè¯»åæ°æ® appendLog("500毫ç§å读åçµæåº¦è®¾å®ç»æ..."); new android.os.Handler().postDelayed(() -> { sendCmdWithCrc(CMD.READ_DATA); }, 500); } else { restoreCurrentButton(); appendLog("çµæåº¦åå ¥å¤±è´¥"); Toast.makeText(this, "çµæåº¦åå ¥å¤±è´¥", Toast.LENGTH_SHORT).show(); } } else if (hex.startsWith(CMD.WRITE_BOARD_RESET.substring(0, 8))) { } else if (hex.startsWith(CMD.WRITE_BOARD_RESET.substring(0, 8))) { // å¤ç主æ¿/ä¼ æå¨å¤ä½çè¿å // 夿æ¯ä¸»æ¿å¤ä½è¿æ¯ä¼ æå¨å¤ä½ boolean isBoardReset = pendingSettingCmd.equals(CMD.WRITE_BOARD_RESET); String name = isBoardReset ? "主æ¿å¤ä½" : "ä¼ æå¨å¤ä½"; if (isSuccess) { appendLog(name + "æå"); Toast.makeText(this, name + "æå", Toast.LENGTH_SHORT).show(); // 妿æ¯ä¸»æ¿å¤ä½æåï¼æç¤ºå³å°è¿å并跳转å°å®æ¶çæ§é¡µé¢ if (isBoardReset) { Toast.makeText(this, "å¤ä½æåå³å°è¿å", Toast.LENGTH_SHORT).show(); appendLog("å¤ä½æåï¼2ç§åè¿å宿¶çæ§é¡µé¢..."); // 2ç§åè¿åå°å®æ¶çæ§é¡µé¢ new android.os.Handler().postDelayed(() -> { finish(); // å ³éå½å页é¢ï¼è¿åå°å®æ¶çæ§é¡µé¢ }, 2000); } else { // ä¼ æå¨å¤ä½æåï¼æç¤ºå¹¶å¨500毫ç§åè¯»åæ°æ® Toast.makeText(this, name + "æå", Toast.LENGTH_SHORT).show(); // 500毫ç§åè¯»åæ°æ®ï¼éè¿ç°æçparseSelfCheckDataè§£æ new android.os.Handler().postDelayed(() -> { sendCmdWithCrc(CMD.READ_DATA); }, 500); } } else { restoreCurrentButton(); appendLog(name + "失败"); Toast.makeText(this, name + "失败", Toast.LENGTH_SHORT).show(); } } } // Check for Self Check command response // å¤çèªæ£æä»¤çè¿åç»æ // å¼å§èªæ£åç»æèªæ£çæä»¤åç¼ç¸åï¼é½æ¯ CMD.WRITE_START_CHECK çå8ä½ if (hex.startsWith(CMD.WRITE_START_CHECK.substring(0, 8)) && hex.length() >= 12) { // æåç¶æåèï¼ç¬¬11-12ä½ï¼ï¼01表示æåï¼å ¶ä»è¡¨ç¤ºå¤±è´¥ String statusHex = hex.substring(10, 12); boolean isSuccess = "01".equals(statusHex); // æ ¹æ®å½å模å¼å¤ææä½åç§° String opName = isSelfCheckMode ? "å¼å§èªæ£" : "ç»æèªæ£"; if (isSuccess) { appendLog(opName + "æå"); Toast.makeText(this, opName + "æå", Toast.LENGTH_SHORT).show(); pendingToastMessage = opName + "æå"; // If success and isSelfCheckMode, wait 5s then read data // æ ¹æ®ä¸åçèªæ£é¶æ®µï¼å»¶è¿ä¸åæ¶é´åè¯»åæ°æ® if (isSelfCheckMode) { appendLog("5ç§å读åèªæ£ç»æ..."); // å¼å§èªæ£æååï¼çå¾ å±æ° * 1000 毫ç§åè¯»åæ°æ® int layerCount = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 3); long delay = layerCount * 1000L; Toast.makeText(this, "å¼å§èªæ£ï¼è¯·çå¾ " + layerCount + "ç§", Toast.LENGTH_SHORT).show(); // è¿ä¸ªæ¶é´æ¯ä¸ºäºè®©ä¼ æå¨å®æèªæ£å¨ä½ appendLog(delay + "毫ç§å读åèªæ£ç»æ..."); new android.os.Handler().postDelayed(() -> { sendCmdWithCrc(CMD.READ_DATA); }, 5000); }, delay); } else { // ç»æèªæ£æååï¼çå¾ 500毫ç§åè¯»åæ°æ® // è¿ä¸ªæ¶é´æ¯ä¸ºäºè®©è®¾å¤ç¶æç¨³å®åå读åæç»ç»æ appendLog("500毫ç§å读åèªæ£ç»æ..."); new android.os.Handler().postDelayed(() -> { sendCmdWithCrc(CMD.READ_DATA); }, 500); } } else { appendLog(opName + "失败"); @@ -378,69 +506,54 @@ } } // Handle READ_DATA response for Self Check results // A55A0301... // å¤çè¯»åæ°æ®æä»¤çè¿åç»æï¼ç¨äºæ¾ç¤ºèªæ£ç»æ // æ°æ®æ ¼å¼ï¼A55A0301... è³å°éè¦26ä½åå è¿å¶å符 if (hex.startsWith(CMD.READ_DATA.substring(0, 8)) && hex.length() >= 26) { restoreCurrentButton(); parseSelfCheckData(hex); if (pendingToastMessage != null) { Toast.makeText(this, pendingToastMessage, Toast.LENGTH_SHORT).show(); pendingToastMessage = null; } } } /** * è§£æèªæ£æ°æ®å¹¶æ´æ°æ ¼åç¶æ * @param hex æ¥æ¶å°çåå è¿å¶æ°æ® */ private void parseSelfCheckData(String hex) { try { // 11th hex -> index 10-18 (8 chars) // æå第11-18ä½ï¼8个å符ï¼ä½ä¸ºç¬¬ä¸é¨åæ°æ® String part1Hex = hex.substring(10, 18); long part1Bits = Long.parseLong(part1Hex, 16); // 18th hex -> index 18-26 (8 chars) // æå第19-26ä½ï¼8个å符ï¼ä½ä¸ºç¬¬äºé¨åæ°æ® String part2Hex = hex.substring(18, 26); long part2Bits = Long.parseLong(part2Hex, 16); appendLog("èªæ£æ°æ®è§£æ: " + part1Hex + ", " + part2Hex); boolean hasChange = false; // éåæææ ¼åï¼æ ¹æ®ä½æ°æ®å¤æèªæ£ç»æ for (BoxStatus box : boxStatusList) { int bitIndex = box.id - 1; int bitIndex = box.id - 1; // æ ¼åIDä»1å¼å§ï¼ä½ç´¢å¼ä»0å¼å§ if (bitIndex >= 0 && bitIndex < 32) { // Determine success based on both bits // Assuming both need to be 1, or following specific logic. // Based on user request "11th and 18th hex", we check both. // Usually part1 is Glass (Detection), part2 is Online. // We'll assume success means bit is set in both (Online and Detected?) // OR maybe just Online? // Let's stick to combining them to be safe as user mentioned both. // But if Self Check is just checking if it's working, maybe Online is enough? // Let's use: Success = (part1 & 1) && (part2 & 1) ? // Let's use bitwise AND of the two parts for the result. // æå对åºä½çå¼ boolean bit1 = ((part1Bits >> bitIndex) & 1) == 1; boolean bit2 = ((part2Bits >> bitIndex) & 1) == 1; // If user meant "11th OR 18th", or "11th is this, 18th is that". // Given "Success/Fail", I'll assume both must be valid. // However, in normal operation: // Glass=0, Online=1 -> Empty box, but sensor working. // Glass=1, Online=1 -> Full box, sensor working. // Online=0 -> Sensor broken/offline. // If "Self Check" puts the sensor in a state where it should report "1" for Glass? // If so, then we expect Glass=1 AND Online=1. // If "Self Check" just checks health, then Glass might be 0. // "ç¡®å®åªäºæ ¼åèªæ£æå" (Determine which grids passed self-check). // If I use bit1 && bit2: // If a sensor is working but empty (Glass=0), it fails self-check? // This implies Self Check expects a "1" signal. // This is common in self-checks (force a signal). // 两个ä½é½ä¸º1è¡¨ç¤ºèªæ£æåï¼å¦å失败 if (bit1 && bit2) { box.checkResult = 1; // Green box.checkResult = 1; // æåï¼æ¾ç¤ºç»¿è² } else { box.checkResult = 0; // Red box.checkResult = 0; // å¤±è´¥ï¼æ¾ç¤ºçº¢è² } hasChange = true; } } // å¦æææ°æ®ååï¼å·æ°çé¢ if (hasChange && mAdapter != null) { mAdapter.notifyDataSetChanged(); appendLog("èªæ£ç»æå·²æ´æ°"); @@ -452,7 +565,10 @@ } } /** * åé带CRCæ ¡éªçæä»¤ * @param cmd è¦åéçæä»¤ï¼ä¸å«CRCï¼ */ private void sendCmdWithCrc(String cmd) { if (!BleGlobalManager.getInstance().isConnected()) { Toast.makeText(this, "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); @@ -462,42 +578,50 @@ byte[] cmdBytes = BleGlobalManager.hexStringToBytes(cmd); if (cmdBytes != null) { // 计ç®CRCæ ¡éªç String crc = CRCutil.getCRC(cmdBytes); // Pad CRC to 4 chars if needed // CRCè¡¥é½å°4ä½ while (crc.length() < 4) { crc = "0" + crc; } String fullCmd = cmd + crc.toUpperCase(); appendLog(fullCmd, true); // true for sent (Blue) appendLog(fullCmd, true); // true表示åéçæä»¤ï¼æ¾ç¤ºä¸ºèè² BleGlobalManager.getInstance().sendCmd(fullCmd); } else { appendLog("é误: æä»¤è½¬æ¢å¤±è´¥"); } } /** * å¤çéå¼è®¾å®åå ¥ */ private void handleThresholdWrite() { int position = sp_threshold_mode.getSelectedItemPosition(); String inputValue = et_threshold_value.getText().toString().trim(); String modeHex = ""; String valueHex = ""; if (position == 0) { // ä¸é®è®¾å® if (position == 0) { // ä¸é®è®¾å® modeHex = "00"; valueHex = "00"; } else if (position == 1) { // åå±è®¾å® } else if (position == 1) { // åå±è®¾å® modeHex = "01"; int maxLayer = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 30); if (inputValue.isEmpty()) { Toast.makeText(this, "请è¾å ¥éå¼(1-30)", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "请è¾å ¥éå¼(1-" + maxLayer + ")", Toast.LENGTH_SHORT).show(); return; } try { int val = Integer.parseInt(inputValue); if (val < 1 || val > 30) { Toast.makeText(this, "éå¼èå´1-30", Toast.LENGTH_SHORT).show(); if (val < 1 || val > maxLayer) { Toast.makeText(this, "éå¼èå´1-" + maxLayer, Toast.LENGTH_SHORT).show(); return; } valueHex = String.format("%02X", val); // åå±è®¾å®æ¶ï¼è¾å ¥åæ°éè¦å1 valueHex = String.format("%02X", val - 1); } catch (NumberFormatException e) { Toast.makeText(this, "è¾å ¥æ ¼å¼é误", Toast.LENGTH_SHORT).show(); return; @@ -506,19 +630,40 @@ return; } // Construct pending command: CMD (32) + Len (02) + Mode + Value // CMD.WRITE_THRESHOLD_SETTING is "A55A063202" // æé æä»¤ï¼CMD.WRITE_THRESHOLD_SETTING + æ¨¡å¼ + å¼ pendingSettingCmd = CMD.WRITE_THRESHOLD_SETTING + modeHex + valueHex; sendCmdWithCrc(pendingSettingCmd); if (BleGlobalManager.getInstance().isConnected()) { currentOperatingButton = btn_threshold_write; updateButtonState(currentOperatingButton, false); setAllGridGray(); sendCmdWithCrc(pendingSettingCmd); } else { Toast.makeText(this, "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); } } /** * å°æææ ¼å设置为ç°è²ï¼çå¾ ç¶æï¼ */ private void setAllGridGray() { for (BoxStatus box : boxStatusList) { box.checkResult = 2; // 2表示çå¾ ç¶æï¼æ¾ç¤ºç°è² } if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } } /** * å¤ççµæåº¦è®¾å®åå ¥ */ private void handleSensitivityWrite() { int position = sp_sensitivity_mode.getSelectedItemPosition(); String inputValue = et_sensitivity_value.getText().toString().trim(); String modeHex = ""; String valueHex = ""; // 0: æ¹éå¢å (00), 1: æ¹éåå°(01), 2: åå±å¢å (02), 3: åå±åå°(03) // 模å¼ï¼0=æ¹éå¢å (00), 1=æ¹éåå°(01), 2=åå±å¢å (02), 3=åå±åå°(03) if (position == 0) { modeHex = "00"; valueHex = "00"; @@ -533,28 +678,37 @@ return; } // å屿ä½éè¦è¾å ¥å ·ä½æ°å¼ if (position == 2 || position == 3) { int maxLayer = com.blankj.utilcode.util.SPUtils.getInstance().getInt("layer_count", 30); if (inputValue.isEmpty()) { Toast.makeText(this, "请è¾å ¥æ°å¼(1-30)", Toast.LENGTH_SHORT).show(); Toast.makeText(this, "请è¾å ¥æ°å¼(1-" + maxLayer + ")", Toast.LENGTH_SHORT).show(); return; } try { int val = Integer.parseInt(inputValue); if (val < 1 || val > 30) { Toast.makeText(this, "æ°å¼èå´1-30", Toast.LENGTH_SHORT).show(); if (val < 1 || val > maxLayer) { Toast.makeText(this, "æ°å¼èå´1-" + maxLayer, Toast.LENGTH_SHORT).show(); return; } valueHex = String.format("%02X", val); // åå±è®¾å®æ¶ï¼è¾å ¥åæ°éè¦å1 valueHex = String.format("%02X", val - 1); } catch (NumberFormatException e) { Toast.makeText(this, "è¾å ¥æ ¼å¼é误", Toast.LENGTH_SHORT).show(); return; } } // Construct pending command: CMD (33) + Len (02) + Mode + Value // CMD.WRITE_SENSITIVITY_SETTING is "A55A063302" // æé æä»¤ï¼CMD.WRITE_SENSITIVITY_SETTING + æ¨¡å¼ + å¼ pendingSettingCmd = CMD.WRITE_SENSITIVITY_SETTING + modeHex + valueHex; sendCmdWithCrc(pendingSettingCmd); if (BleGlobalManager.getInstance().isConnected()) { currentOperatingButton = btn_sensitivity_write; updateButtonState(currentOperatingButton, false); setAllGridGray(); sendCmdWithCrc(pendingSettingCmd); } else { Toast.makeText(this, "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); } } private void updateBluetoothStatus() { @@ -580,20 +734,25 @@ .setPositiveButton("ç¡®å®", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { pendingSettingCmd = cmd; sendCmdWithCrc(cmd); if (BleGlobalManager.getInstance().isConnected()) { updateButtonState(currentOperatingButton, false); pendingSettingCmd = cmd; sendCmdWithCrc(cmd); } else { Toast.makeText(ParameterSettingActivity.this, "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); } } }) .setNegativeButton("åæ¶", null) .show(); } // Inner classes /** * æ ¼åç¶ææ°æ®ç±» */ private static class BoxStatus { int id; // -1: Unknown/Initial (White), 0: Fail (Red), 1: Success (Green) int checkResult = -1; int id; // æ ¼åIDï¼1-30ï¼ int checkResult = -1; // èªæ£ç»æï¼-1=æªç¥ï¼ç½è²ï¼ï¼0=失败ï¼çº¢è²ï¼ï¼1=æåï¼ç»¿è²ï¼ï¼2=çå¾ ï¼ç°è²ï¼ public BoxStatus(int id) { this.id = id; @@ -612,18 +771,17 @@ @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { // Calculate Box ID based on 3 rows of 10, Right to Left logic as seen in image // Row 1 (pos 0-9): 10 ... 1 // Row 2 (pos 10-19): 20 ... 11 // Row 3 (pos 20-29): 30 ... 21 // è®¡ç®æ ¼åIDï¼3è¡10åï¼ä»å·¦å°å³æå // 第1è¡ï¼ä½ç½®0-9ï¼ï¼1-10 // 第2è¡ï¼ä½ç½®10-19ï¼ï¼11-20 // 第3è¡ï¼ä½ç½®20-29ï¼ï¼21-30 int row = position / 10; int col = position % 10; int boxId = (row + 1) * 10 - col; int boxId = row * 10 + col + 1; holder.tvBoxNumber.setText(String.valueOf(boxId)); // Find status for this box // æ¥æ¾å¯¹åºæ ¼åçç¶æ BoxStatus status = null; for (BoxStatus s : boxStatusList) { if (s.id == boxId) { @@ -632,16 +790,24 @@ } } // æ ¹æ®èªæ£ç»æè®¾ç½®èæ¯é¢è² if (status != null) { if (status.checkResult == 1) { // Success holder.viewBox.setBackgroundResource(R.drawable.bg_box_full); // Green // èªæ£æåï¼æ¾ç¤ºç»¿è² holder.viewBox.setBackgroundResource(R.drawable.bg_box_full); holder.viewBox.getBackground().clearColorFilter(); } else if (status.checkResult == 0) { // Fail holder.viewBox.setBackgroundResource(R.drawable.bg_box_error); // Red // èªæ£å¤±è´¥ï¼æ¾ç¤ºçº¢è² holder.viewBox.setBackgroundResource(R.drawable.bg_box_error); holder.viewBox.getBackground().clearColorFilter(); } else if (status.checkResult == 2) { // çå¾ ç¶æï¼æ¾ç¤ºç°è² holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty); holder.viewBox.getBackground().setColorFilter(android.graphics.Color.GRAY, android.graphics.PorterDuff.Mode.MULTIPLY); } else { // Unknown / Initial holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty); // White // æªç¥ç¶æï¼æ¾ç¤ºç½è² holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty); holder.viewBox.getBackground().clearColorFilter(); } } } @@ -662,4 +828,25 @@ } } } private void restoreCurrentButton() { if (currentOperatingButton != null) { updateButtonState(currentOperatingButton, true); currentOperatingButton = null; } } private void updateButtonState(Button btn, boolean enable) { if (btn == null) return; btn.setEnabled(enable); if (enable) { if (btn.getBackground() != null) { btn.getBackground().clearColorFilter(); } } else { if (btn.getBackground() != null) { btn.getBackground().setColorFilter(android.graphics.Color.GRAY, android.graphics.PorterDuff.Mode.MULTIPLY); } } } } app/src/main/java/com/shlb/comb/activity/SystemSettingsActivity.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,86 @@ package com.shlb.comb.activity; import android.widget.CompoundButton; import android.widget.Toast; import com.qmuiteam.qmui.widget.grouplist.QMUICommonListItemView; import com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView; import com.shlb.comb.R; import com.shlb.comb.base.BaseActivity; import com.shlb.comb.event.UpdateEvent; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; public class SystemSettingsActivity extends BaseActivity { private QMUIGroupListView mGroupListView; @Override protected void contentView() { setContentView(R.layout.activity_system_settings); } @Override protected void initView() { super.initHead(); setTitle("ç³»ç»è®¾ç½®"); // Set left button to back icon if (iv_left != null) { iv_left.setImageResource(R.mipmap.icon_back); iv_left.setOnClickListener(v -> finish()); } // Hide right text (connection status) android.view.View tvRightText = findViewById(R.id.tv_right_text); if (tvRightText != null) { tvRightText.setVisibility(android.view.View.GONE); } mGroupListView = findViewById(R.id.groupListView); initGroupListView(); // Hide right button if not needed if (iv_right != null) { iv_right.setVisibility(android.view.View.GONE); } } @Override protected void initData() { } @Override protected void initEvent() { } private void initGroupListView() { QMUICommonListItemView itemCmdLog = mGroupListView.createItemView("CMDæ¥å¿"); itemCmdLog.setAccessoryType(QMUICommonListItemView.ACCESSORY_TYPE_SWITCH); boolean isCmdLogEnabled = com.blankj.utilcode.util.SPUtils.getInstance().getBoolean("cmd_log_enabled", false); itemCmdLog.getSwitch().setChecked(isCmdLogEnabled); itemCmdLog.getSwitch().setOnCheckedChangeListener((buttonView, isChecked) -> { com.blankj.utilcode.util.SPUtils.getInstance().put("cmd_log_enabled", isChecked); Toast("CMDæ¥å¿: " + (isChecked ? "å¼å¯" : "å ³é")); }); // QMUICommonListItemView itemTestPage = mGroupListView.createItemView("æµè¯é¡µé¢"); // itemTestPage.setAccessoryType(QMUICommonListItemView.ACCESSORY_TYPE_SWITCH); // itemTestPage.getSwitch().setOnCheckedChangeListener((buttonView, isChecked) -> { // Toast("æµè¯é¡µé¢: " + (isChecked ? "å¼å¯" : "å ³é")); // }); QMUIGroupListView.newSection(this) .setTitle("设置") .addItemView(itemCmdLog, null) // .addItemView(itemTestPage, null) .addTo(mGroupListView); } @Subscribe(threadMode = ThreadMode.MAIN) public void onEvent(UpdateEvent event) { // Placeholder to satisfy EventBus requirements in BaseActivity } } app/src/main/java/com/shlb/comb/fragment/DeviceListFragment.java
@@ -356,8 +356,6 @@ if (scanManager != null) { scanManager.setScanCallbackCompat(null); scanManager.setScanOverListener(null); // éæ¾èµæº scanManager.release(); } } } app/src/main/java/com/shlb/comb/fragment/SettingsFragment.java
@@ -1,5 +1,6 @@ package com.shlb.comb.fragment; import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -34,32 +35,50 @@ import android.text.Html; import android.content.DialogInterface; /** * 设置çé¢ Fragment * <p> * 该类è´è´£è®¾å¤åæ°çé ç½®ã设å¤ç¶æççæ§ä»¥åèçéä¿¡çå¤çï¼ * 1. æ¯æç«å·ã屿°ãæ³¢ç¹ççåæ°ç读åååå ¥ * 2. 宿¶çæ§è®¾å¤å¨çº¿ç¶æåç»çææ æ åµ * 3. æä¾èçè¿æ¥ç¶æçæ¾ç¤ºåå¤ç * 4. å®ç°æ¥å¿è®°å½åæ¾ç¤ºåè½ * 5. æ¯æå¨ææ§æ°æ®è¯»åï¼æ¯1ç§ï¼ï¼é¿å é¢ç¹æ¥å¿å¯¼è´é¡µé¢å¡é¡¿ * </p> */ public class SettingsFragment extends Fragment { private RecyclerView rvGrid; private EditText etLayer; private EditText etStation; private Spinner spinnerBaud; private QMUIRoundButton btnWriteAll; private QMUIRoundButton btnReadData; private QMUIRoundButton btnReadParam; private QMUIRoundButton btnClearLog; private TextView tvStatus; private TextView tvLog; private TextView tvLayerStatus; private TextView tvStationStatus; private TextView tvBaudStatus; private android.widget.ScrollView svLog; private QMUITipDialog mLoadingDialog; private GridAdapter mAdapter; private List<BoxStatus> boxStatusList = new ArrayList<>(); private StringBuilder logBuilder = new StringBuilder(); // ç¨æ·çé¢ç»ä»¶ private RecyclerView rvGrid; // 设å¤ç¶æç½æ ¼è§å¾ private TextView etLayer; // 屿°æ¾ç¤ºææ¬æ¡ private EditText etStation; // ç«å·è¾å ¥æ¡ private Spinner spinnerBaud; // æ³¢ç¹çéæ©å¨ private QMUIRoundButton btnWriteAll; // åå ¥ææåæ°æé® private QMUIRoundButton btnReadParam; // 读ååæ°æé® private QMUIRoundButton btnClearLog; // æ¸ é¤æ¥å¿æé® private TextView tvMonitorTitle; // çæ§è¯¦æ æ é¢ private TextView tvMonitorUpdateTime; // çæ§æ°æ®æ´æ°æ¶é´ private TextView tvStatus; // ç¶ææ¾ç¤ºææ¬ private TextView tvLog; // æ¥å¿æ¾ç¤ºææ¬ private TextView tvLayerStatus; // 屿°ç¶ææ ç¾ private TextView tvStationStatus; // ç«å·ç¶ææ ç¾ private TextView tvBaudStatus; // æ³¢ç¹çç¶ææ ç¾ private java.util.Queue<String> cmdQueue = new java.util.LinkedList<>(); private android.widget.ScrollView svLog; // æ¥å¿æ»å¨è§å¾ private androidx.cardview.widget.CardView cvLog; // æ¥å¿å®¹å¨ private QMUITipDialog mLoadingDialog; // å è½½å¯¹è¯æ¡ private GridAdapter mAdapter; // ç½æ ¼éé å¨ private List<BoxStatus> boxStatusList = new ArrayList<>(); // 设å¤ç¶æå表 private StringBuilder logBuilder = new StringBuilder(); // æ¥å¿å 容æå»ºå¨ private java.util.Queue<String> cmdQueue = new java.util.LinkedList<>(); // æä»¤éåï¼ç¨äºæé¡ºåºæ§è¡æä»¤ private String currentExecutingCmd = ""; // å½åæ£å¨æ§è¡çæä»¤ï¼ç¨äºæ ¡éªååº private boolean isPeriodicRead = false; // æ è®°æ¯å¦ä¸ºå¨ææ§è¯»åï¼ç¨äºæ§å¶æ¥å¿è®°å½ private boolean isFirstLoad = true; // æ è®°æ¯å¦ä¸ºé¦æ¬¡å è½½ï¼é¿å éå¤è§¦å读ååæ° private View currentOperatingButton; // å½åæ£å¨æä½çæé® private static class BoxStatus { int id; boolean isOnline; @@ -72,30 +91,61 @@ } } private Runnable autoReadRunnable = new Runnable() { // 卿æ§è¯»åæ°æ®ç¸å ³åé private android.os.Handler periodicReadHandler = new android.os.Handler(); // ç¨äºå¤ç卿æ§è¯»åä»»å¡çHandler private static final long PERIODIC_READ_INTERVAL = 500; // 卿æ§è¯»åé´éï¼æ¯«ç§ï¼ï¼1ç§ private Runnable periodicReadRunnable = new Runnable() { @Override public void run() { // æ¯1ç§è¯»å䏿¬¡æ°æ®ï¼ä¸è®°å½æ¥å¿ if (BleGlobalManager.getInstance().isConnected()) { periodicReadData(); // ç»§ç»å®æä¸ä¸æ¬¡è¯»å periodicReadHandler.postDelayed(this, PERIODIC_READ_INTERVAL); } } }; // èªå¨è¯»ååæ°ç¸å ³åé private Runnable autoReadRunnable = new Runnable() { // èçè¿æ¥æååèªå¨è¯»ååæ°çä»»å¡ @Override public void run() { // èçè¿æ¥æåå èªå¨è§¦åçæ§è¯¦æ ç è¯»åæ°æ® å åæ°è®¾å®è¿éç 读ååæ° if (BleGlobalManager.getInstance().isConnected()) { showLoading("æ£å¨åæ¥æ°æ®..."); if (btnReadData != null) btnReadData.performClick(); // å 触åè¯»åæ°æ® tvStatus.setText("ç¶æï¼æ£å¨è¯»åæ°æ®..."); if (tvMonitorTitle != null) tvMonitorTitle.performClick(); new android.os.Handler().postDelayed(() -> { // å触å读ååæ° if (btnReadParam != null) btnReadParam.performClick(); // åè®¾åæ°è¯»å触åå 1.5ç§ å ³é loadingï¼æè å¨è§£æå®ææåæ°åå ³é // è¿éç®åå¤çï¼å»¶æ¶å ³é new android.os.Handler().postDelayed(() -> { dismissLoading(); // åå§è®¾ç½®å®æåå¯å¨å¨ææ§è¯»å startPeriodicRead(PERIODIC_READ_INTERVAL); isFirstLoad = false; // 馿¬¡å è½½å®æ }, 1500); }, 1000); // é´é1ç§ï¼é¿å æä»¤å²çª } } }; private android.os.Handler debounceHandler = new android.os.Handler(); private static final long DEBOUNCE_DELAY_MS = 1500; // 1.5 seconds debounce private android.os.Handler debounceHandler = new android.os.Handler(); // ç¨äºé²æå¤ççHandler private static final long DEBOUNCE_DELAY_MS = 1500; // é²æå»¶è¿æ¶é´ï¼æ¯«ç§ï¼ï¼1.5ç§ /** * Fragment å¯è§æ¶è°ç¨ * <p> * 注å EventBus å¹¶æ´æ°èçè¿æ¥ç¶æ * </p> */ @Override public void onStart() { super.onStart(); @@ -105,11 +155,70 @@ updateConnectionStatus(); } /** * Fragment ä¸å¯è§æ¶è°ç¨ * <p> * åæ¶æ³¨å EventBusï¼é¿å å åæ³æ¼ * </p> */ @Override public void onStop() { super.onStop(); if (EventBus.getDefault().isRegistered(this)) { EventBus.getDefault().unregister(this); } } /** * Fragment æ¢å¤å¯è§æ¶è°ç¨ * \u003cp\u003e * 妿èçå·²è¿æ¥ï¼æ¢å¤å¨ææ§è¯»åæ°æ® * \u003c/p\u003e */ @Override public void onResume() { super.onResume(); // æ ¹æ®ç³»ç»è®¾ç½®æ§å¶æ¥å¿æ¾ç¤º boolean isCmdLogEnabled = com.blankj.utilcode.util.SPUtils.getInstance().getBoolean("cmd_log_enabled", false); if (cvLog != null) { cvLog.setVisibility(isCmdLogEnabled ? View.VISIBLE : View.GONE); } // 妿èçå·²è¿æ¥ï¼æ¢å¤å¨ææ§è¯»å if (BleGlobalManager.getInstance().isConnected()) { // åªæå½ä¸æ¯é¦æ¬¡å è½½ï¼å³ä»å ¶ä»é¡µé¢è¿åï¼æ¶ææå¨è§¦å读ååæ° // 馿¬¡å è½½ä¼èµ° autoReadRunnable if (!isFirstLoad && btnReadParam != null) { btnReadParam.performClick(); } startPeriodicRead(3000); } } /** * Fragment æåæ¶è°ç¨ * \u003cp\u003e * 忢卿æ§è¯»åæ°æ®ï¼èçèµæº * \u003c/p\u003e */ @Override public void onPause() { super.onPause(); // 离å¼é¡µé¢æ¶åæ¢å¨ææ§è¯»å stopPeriodicRead(); } @Override public void onDestroyView() { super.onDestroyView(); // æ¸ çææ Handler åè°ï¼é¿å å åæ³æ¼ stopPeriodicRead(); debounceHandler.removeCallbacks(autoReadRunnable); // æ¸ çå è½½å¯¹è¯æ¡ if (mLoadingDialog != null) { mLoadingDialog.dismiss(); mLoadingDialog = null; } } @@ -121,40 +230,66 @@ } } /** * äºä»¶æ»çº¿çå¬å¨ * <p> * å¤çèçè¿æ¥ç¶æåååè®¾å¤æ°æ®æ¥æ¶äºä»¶ * </p> * @param event äºä»¶å¯¹è±¡ */ @Subscribe(threadMode = ThreadMode.MAIN) public void onEventRefresh(UpdateEvent event) { if (event.getType() == UpdateEvent.Type.CONN_STATU) { // Check if obj is integer // æ£æ¥objæ¯å¦ä¸ºæ´æ° if (event.getObj() instanceof Integer) { int status = (int) event.getObj(); if (status == BluetoothProfile.STATE_CONNECTED) { tvStatus.setText("ç¶æï¼å·²è¿æ¥"); Toast.makeText(getContext(), "èçå·²è¿æ¥", Toast.LENGTH_SHORT).show(); // èçè¿æ¥æåå èªå¨è§¦å triggerAutoRead(); } else { tvStatus.setText("ç¶æï¼å·²æå¼"); // èçæå¼æ¶åæ¢å¨ææ§è¯»å stopPeriodicRead(); } } } else if (event.getType() == UpdateEvent.Type.DEVICE_INFO) { // Received data // æ¶å°æ°æ® String hex = event.getMsg(); tvStatus.setText("æ¶å°æ°æ®: " + hex); appendLog(hex, false); // false for received (Green) if (tvStatus != null && !isPeriodicRead) { tvStatus.setText("æ¶å°æ°æ®: " + hex); } // æ£æ¥æ¯å¦ä¸ºå¨ææ§è¯»å if (!isPeriodicRead) { appendLog(hex, false); // falseè¡¨ç¤ºæ¥æ¶çæ°æ®ï¼ç»¿è²ï¼ } parseAndRefresh(hex); // æ 论æ¯å¦ä¸ºå¨ææ§è¯»åï¼å¤ç宿åé½éç½®æ è®° isPeriodicRead = false; } } /** * è§£æå¹¶å·æ°æ°æ® * <p> * æ ¹æ®æä»¤ç±»åååå°ä¸åçè§£ææ¹æ³ * </p> * @param hex åå è¿å¶æ ¼å¼çåå§æ°æ® */ private void parseAndRefresh(String hex) { if (hex == null) return; // æ ¹æ®CMDå8ä½å¤ææä»¤ç±»å // æ ¹æ®æä»¤å8ä½å¤ææä»¤ç±»å if (hex.length() >= 8) { // åæ°è¯»åè¿å (é¿åº¦è³å°12ä½) // è¯»å±æ°: A55A0308... // 读ç«å·: A55A0304... // 读波ç¹ç: A55A0305... // è¯»å±æ°æä»¤: A55A0308... // 读ç«å·æä»¤: A55A0304... // 读波ç¹çæä»¤: A55A0305... if (hex.length() >= 12 && ( hex.startsWith(CMD.READ_FLOORS.substring(0, 8)) || hex.startsWith(CMD.READ_STATION_NUM.substring(0, 8)) || @@ -164,14 +299,14 @@ } // æ°æ®è¯»åè¿å (é¿åº¦è³å°32ä½) // è¯»æ°æ®: A55A0301... // è¯»æ°æ®æä»¤: A55A0301... if (hex.length() >= 32 && hex.startsWith(CMD.READ_DATA.substring(0, 8))) { parseDataResponse(hex); return; } } // åå ¥æä»¤çè¿å (A55A06å¼å¤´) // åå ¥æä»¤çè¿å (以A55A06å¼å¤´) if (hex.startsWith("A55A06") && hex.length() >= 12) { parseWriteResponse(hex); return; @@ -208,11 +343,18 @@ } setLabelStatus(tvBaudStatus, "(读åå¼)", R.color.base_color); appendLog("è¯»åæ³¢ç¹ç: " + value); // 读åç»æï¼æ¢å¤æé®ç¶æ restoreCurrentButton(); } else if ("08".equals(cmdType)) { // 屿° if (etLayer != null) etLayer.setText(String.valueOf(value)); if (etLayer != null) { etLayer.setText(String.valueOf(value)); etLayer.setTextColor(0xFF333333); } setLabelStatus(tvLayerStatus, "(读åå¼)", R.color.base_color); appendLog("读å屿°: " + value); com.blankj.utilcode.util.SPUtils.getInstance().put("layer_count", value); } } catch (Exception e) { e.printStackTrace(); @@ -259,6 +401,7 @@ cmdQueue.clear(); currentExecutingCmd = ""; tvStatus.setText("åå ¥å¤±è´¥"); restoreCurrentButton(); // æ¢å¤æé® if (currentExecutingCmd.startsWith(CMD.WRITE_STATION_NUM)) { setLabelStatus(tvStationStatus, "(åå ¥å¤±è´¥)", R.color.orange); @@ -278,6 +421,7 @@ // å¼å¸¸ä¹è§ä¸ºå¤±è´¥ setLabelStatus(tvStationStatus, "(åå ¥å¤±è´¥)", R.color.orange); setLabelStatus(tvBaudStatus, "(åå ¥å¤±è´¥)", R.color.orange); restoreCurrentButton(); // å¼å¸¸æ¢å¤æé® } } @@ -303,25 +447,38 @@ currentExecutingCmd = ""; tvStatus.setText("åæ°åå ¥å®æ"); Toast.makeText(getContext(), "åæ°åå ¥æå", Toast.LENGTH_SHORT).show(); restoreCurrentButton(); // å ¨é¨å®æï¼æ¢å¤æé® } } /** * è§£æçæ§æ°æ®è¿å (æ¯å¦æç»çãå¨çº¿ç¶æ) */ /** * è§£æçæ§æ°æ®è¿å * <p> * è§£æè®¾å¤å¨çº¿ç¶æåç»çææ æ åµ * </p> * @param hex åå è¿å¶æ ¼å¼çåå§æ°æ® */ private void parseDataResponse(String hex) { try { // è§£ææ¯å¦æç»ç: 第11-18ä½ (8个å符 = 32ä½) // ç´¢å¼ 10-17 String glassHex = hex.substring(10, 18); appendLog("è§£æç»çæ°æ®: " + glassHex); if(!isPeriodicRead){ appendLog("è§£æç»çæ°æ®: " + glassHex); } // ç´æ¥è§£æ hex 为 long (Big Endian) // "00000004" -> 4 -> ...00100 -> Bit 2 -> Box 3 // ç´æ¥å°åå è¿å¶è§£æä¸ºé¿æ´å (大端åº) // "00000004" -> 4 -> ...00100 -> 第2ä½ -> 第3ä¸ªæ ¼å long glassBits = Long.parseLong(glassHex, 16); String onlineHex = hex.substring(18, 26); if(!isPeriodicRead){ appendLog("è§£æå¨çº¿æ°æ®: " + onlineHex); } long onlineBits = Long.parseLong(onlineHex, 16); // æ´æ°30ä¸ªæ ¼åçç¶æ @@ -337,6 +494,12 @@ // å·æ°å表æ¾ç¤º if (mAdapter != null) { mAdapter.notifyDataSetChanged(); } // æ´æ°çæ§æ°æ®æ¥æ¶æ¶é´ if (tvMonitorUpdateTime != null) { String currentTime = com.blankj.utilcode.util.TimeUtils.getNowString(new java.text.SimpleDateFormat("HH:mm:ss")); tvMonitorUpdateTime.setText(currentTime); } } catch (Exception e) { @@ -358,7 +521,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Initialize box status list // åå§å设å¤ç¶æå表 boxStatusList.clear(); for (int i = 1; i <= 30; i++) { boxStatusList.add(new BoxStatus(i)); @@ -378,6 +541,82 @@ debounceHandler.postDelayed(autoReadRunnable, DEBOUNCE_DELAY_MS); } private void startPeriodicRead(long delay) { // å 忢å¯è½åå¨çä»»å¡ï¼é¿å éå¤å¯å¨ stopPeriodicRead(); // å¯å¨å¨ææ§è¯»å periodicReadHandler.postDelayed(periodicReadRunnable, delay); } private void stopPeriodicRead() { periodicReadHandler.removeCallbacks(periodicReadRunnable); } /** * ä¸é¨ç¨äºå¨ææ§è¯»åæ°æ®çæ¹æ³ï¼ä¸è®°å½æ¥å¿ */ /** * 卿æ§è¯»åæ°æ®ï¼æ¯1ç§ï¼ * <p> * ä¸é¨ç¨äºå¨ææ§è¯»åæ°æ®ï¼ä¸è®°å½æ¥å¿ä»¥é¿å 页é¢å¡é¡¿ * </p> */ private void periodicReadData() { try { if (BleGlobalManager.getInstance() != null && BleGlobalManager.getInstance().isConnected()) { if (tvStatus != null && isPeriodicRead) { tvStatus.setText("ç¶æï¼æ£å¨è¯»åæ°æ®..."); } // è®¾ç½®å¨ææ§è¯»åæ è®° isPeriodicRead = true; // ç´æ¥åéå½ä»¤ï¼ä¸è®°å½æ¥å¿ sendCmdWithCrcNoLog(CMD.READ_DATA); } } catch (Exception e) { e.printStackTrace(); // å¼å¸¸ä¹ä¸è®°å½æ¥å¿ï¼é¿å å¡é¡¿ // ç¡®ä¿æ 记被éç½® isPeriodicRead = false; } } /** * åéå½ä»¤ä½ä¸è®°å½æ¥å¿ï¼ç¨äºå¨ææ§è¯»åï¼ */ /** * åé带 CRC æ ¡éªçæä»¤ï¼ä¸è®°å½æ¥å¿ï¼ * <p> * ä¸é¨ç¨äºå¨ææ§è¯»åæ°æ®ï¼é¿å é¢ç¹è®°å½æ¥å¿å¯¼è´é¡µé¢å¡é¡¿ * </p> * @param cmd åå è¿å¶æ ¼å¼çæä»¤å 容 */ private void sendCmdWithCrcNoLog(String cmd) { if (!BleGlobalManager.getInstance().isConnected()) { return; // éé»è¿åï¼ä¸æ¾ç¤º Toast ææ¥å¿ } byte[] cmdBytes = BleGlobalManager.hexStringToBytes(cmd); if (cmdBytes != null) { String crc = CRCutil.getCRC(cmdBytes); // ç¡®ä¿CRC为4个å符ï¼ä¸è¶³åè¡¥é¶ while (crc.length() < 4) { crc = "0" + crc; } String fullCmd = cmd + crc.toUpperCase(); // ä¸è®°å½æ¥å¿ï¼ç´æ¥åéå½ä»¤ BleGlobalManager.getInstance().sendCmd(fullCmd); } } /** * æ¾ç¤ºå è½½å¯¹è¯æ¡ * <p> * ç¨äºæä½è¿ç¨ä¸ççå¾ æç¤º * </p> * @param msg å è½½æç¤ºä¿¡æ¯ */ private void showLoading(String msg) { if (mLoadingDialog != null && mLoadingDialog.isShowing()) { mLoadingDialog.dismiss(); @@ -386,25 +625,36 @@ .setIconType(QMUITipDialog.Builder.ICON_TYPE_LOADING) .setTipWord(msg) .create(); // å 许ç¹å»å¤é¨æè¿åé®åæ¶ loadingï¼åªæ¯å ³é dialogï¼ // å 许ç¹å»å¤é¨æè¿åé®åæ¶å è½½ï¼ä» å ³éå¯¹è¯æ¡ï¼ mLoadingDialog.setCancelable(true); mLoadingDialog.setCanceledOnTouchOutside(true); mLoadingDialog.show(); } /** * å ³éå è½½å¯¹è¯æ¡ */ private void dismissLoading() { if (mLoadingDialog != null && mLoadingDialog.isShowing()) { mLoadingDialog.dismiss(); } } /** * åå§åçé¢ç»ä»¶ * <p> * 设置å个UIç»ä»¶çå¼ç¨åçå¬å¨ * </p> * @param view Fragment çæ ¹è§å¾ */ private void initView(View view) { rvGrid = view.findViewById(R.id.rv_grid); etLayer = view.findViewById(R.id.et_layer); etStation = view.findViewById(R.id.et_station); spinnerBaud = view.findViewById(R.id.spinner_baud); btnWriteAll = view.findViewById(R.id.btn_write_all); btnReadData = view.findViewById(R.id.btn_read_data); tvMonitorTitle = view.findViewById(R.id.tv_monitor_title); tvMonitorUpdateTime = view.findViewById(R.id.tv_monitor_update_time); btnReadParam = view.findViewById(R.id.btn_read_param); btnClearLog = view.findViewById(R.id.btn_clear_log); tvStatus = view.findViewById(R.id.tv_status); @@ -412,7 +662,9 @@ svLog = view.findViewById(R.id.sv_log); tvLayerStatus = view.findViewById(R.id.tv_layer_status); tvStationStatus = view.findViewById(R.id.tv_station_status); tvBaudStatus = view.findViewById(R.id.tv_baud_status); cvLog = view.findViewById(R.id.cv_log); // è§£å³æ¥å¿åºåæ»å¨å²çª svLog.setOnTouchListener((v, event) -> { @@ -423,7 +675,7 @@ return false; }); // Restore logs if any // æ¢å¤æ¥å¿ï¼å¦ææï¼ if (logBuilder.length() > 0) { tvLog.setText(Html.fromHtml(logBuilder.toString())); } else { @@ -431,21 +683,25 @@ tvLog.setText(Html.fromHtml(logBuilder.toString())); } // Grid Setup // 10 columns to match the image (10 boxes per row) // Since we are in a horizontal scroll view, this will layout correctly // ç½æ ¼å¸å±è®¾ç½® // æ¯è¡10ä¸ªæ ¼å // ç±äºå¨æ°´å¹³æ»å¨è§å¾ä¸ï¼å¸å±ä¼èªå¨è°æ´ rvGrid.setLayoutManager(new GridLayoutManager(getContext(), 10)); mAdapter = new GridAdapter(); rvGrid.setAdapter(mAdapter); // Spinner Setup // æ³¢ç¹çéæ©å¨è®¾ç½® String[] baudRates = new String[]{"156Kbps", "625Kbps", "2.5Mbps", "5Mbps", "10Mbps"}; ArrayAdapter<String> spinnerAdapter = new ArrayAdapter<>(getContext(), android.R.layout.simple_spinner_item, baudRates); spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinnerBaud.setAdapter(spinnerAdapter); spinnerBaud.setSelection(0); // Select 156Kbps by default spinnerBaud.setSelection(0); // é»è®¤éæ©156Kbps // Button Listeners tvMonitorTitle.setOnClickListener(v -> { onMonitorTitleClick(); }); // æé®çå¬å¨è®¾ç½® btnWriteAll.setOnClickListener(v -> { if (!BleGlobalManager.getInstance().isConnected()) { Toast.makeText(getContext(), "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); @@ -470,6 +726,9 @@ return; } currentOperatingButton = btnWriteAll; updateButtonState(currentOperatingButton, false); // Disable buttons, gray out write button // æé æä»¤éå cmdQueue.clear(); @@ -493,26 +752,19 @@ processNextCmd(); }); btnReadData.setOnClickListener(v -> { if (BleGlobalManager.getInstance().isConnected()) { tvStatus.setText("ç¶æï¼æ£å¨è¯»åæ°æ®..."); sendCmdWithCrc(CMD.READ_DATA); } else { Toast.makeText(getContext(), "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); appendLog("é误: èçæªè¿æ¥"); } }); btnReadParam.setOnClickListener(v -> { if (BleGlobalManager.getInstance().isConnected()) { currentOperatingButton = btnReadParam; updateButtonState(currentOperatingButton, false); // Disable buttons, gray out read button tvStatus.setText("ç¶æï¼æ£å¨è¯»ååæ°..."); sendCmdWithCrc(CMD.READ_FLOORS); new android.os.Handler().postDelayed(() -> { sendCmdWithCrc(CMD.READ_STATION_NUM); }, 200); }, 500); new android.os.Handler().postDelayed(() -> { sendCmdWithCrc(CMD.READ_BAUD_RATE); }, 400); }, 1000); } else { Toast.makeText(getContext(), "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); appendLog("é误: èçæªè¿æ¥"); @@ -526,6 +778,41 @@ }); } /** * çæ§è¯¦æ æ é¢ç¹å»äºä»¶ * <p> * æå¨è§¦åæ°æ®è¯»åï¼ä¼è®°å½å®æ´æ¥å¿ * </p> */ public void onMonitorTitleClick() { try { if (BleGlobalManager.getInstance() != null && BleGlobalManager.getInstance().isConnected()) { if (tvStatus != null) { tvStatus.setText("ç¶æï¼æ£å¨è¯»åæ°æ®..."); } // ç¡®ä¿ä¸æ¯å¨ææ§è¯»åï¼ä»¥ä¾¿è®°å½æ¥å¿ isPeriodicRead = false; sendCmdWithCrc(CMD.READ_DATA); } else { Context context = getContext(); if (context != null) { Toast.makeText(context, "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); } appendLog("é误: èçæªè¿æ¥"); } } catch (Exception e) { e.printStackTrace(); appendLog("ç¹å»çæ§è¯¦æ æ¶åçé误: " + e.getMessage()); } } /** * è®°å½æ®éæ¥å¿ * <p> * å°æ¥å¿ä¿¡æ¯æ·»å å°æ¥å¿æå»ºå¨å¹¶æ¾ç¤º * </p> * @param msg æ¥å¿å 容 */ private void appendLog(String msg) { appendLog(msg, null); } @@ -579,13 +866,38 @@ return ""; } private void updateButtonState(View btn, boolean enable) { if (btn == null) return; btn.setEnabled(enable); if (enable) { btn.getBackground().clearColorFilter(); } else { btn.getBackground().setColorFilter(android.graphics.Color.GRAY, android.graphics.PorterDuff.Mode.MULTIPLY); } } private void restoreCurrentButton() { if (currentOperatingButton != null) { updateButtonState(currentOperatingButton, true); currentOperatingButton = null; } } /** * è®°å½æ¥å¿ * <p> * å°æ¥å¿ä¿¡æ¯æ·»å å°æ¥å¿æå»ºå¨å¹¶æ¾ç¤ºï¼æ¯æåé/æ¥æ¶ç±»åçåºå * </p> * @param msg æ¥å¿å 容 * @param isSent true表示åéçæä»¤ï¼falseè¡¨ç¤ºæ¥æ¶çæ°æ® */ private void appendLog(String msg, Boolean isSent) { String time = com.blankj.utilcode.util.TimeUtils.getNowString(new java.text.SimpleDateFormat("HH:mm:ss.SSS")); String displayMsg = msg; String cmdDesc = ""; // å¦ææ¯ hex æä»¤ (纯 0-9 A-F a-f)ï¼å ç©ºæ ¼æ ¼å¼å // 妿æ¯åå è¿å¶æä»¤ (纯 0-9 A-F a-f)ï¼å ç©ºæ ¼æ ¼å¼å if (msg.matches("^[0-9A-Fa-f]+$")) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < msg.length(); i += 2) { @@ -603,7 +915,7 @@ String logLine; if (isSent != null) { String color = isSent ? "#1890ff" : "#05aa87"; // Blue for sent, Green for received String color = isSent ? "#1890ff" : "#05aa87"; // åé为èè²ï¼æ¥æ¶ä¸ºç»¿è² // æ¼æ¥å°åç¼åé¢: "åé-读ç«å·: " æ "æ¶å°-读ç«å·: " String prefix = (isSent ? "åé" : "æ¶å°") + cmdDesc + ": "; logLine = time + " <font color='" + color + "'>" + prefix + displayMsg + "</font><br>"; @@ -621,6 +933,13 @@ } } /** * åé带 CRC æ ¡éªçæä»¤ * <p> * è®¡ç®æä»¤ç CRC æ ¡éªå¼å¹¶åéï¼åæ¶è®°å½æ¥å¿ * </p> * @param cmd åå è¿å¶æ ¼å¼çæä»¤å 容 */ private void sendCmdWithCrc(String cmd) { if (!BleGlobalManager.getInstance().isConnected()) { Toast.makeText(getContext(), "请å è¿æ¥èç", Toast.LENGTH_SHORT).show(); @@ -637,7 +956,7 @@ } String fullCmd = cmd + crc.toUpperCase(); appendLog(fullCmd, true); // true for sent (Blue) appendLog(fullCmd, true); // true表示åéçæä»¤ï¼èè²ï¼ BleGlobalManager.getInstance().sendCmd(fullCmd); } else { appendLog("é误: æä»¤è½¬æ¢å¤±è´¥"); @@ -655,14 +974,14 @@ @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { // Calculate Box ID based on 3 rows of 10, Right to Left logic as seen in image // Row 1 (pos 0-9): 10 ... 1 // Row 2 (pos 10-19): 20 ... 11 // Row 3 (pos 20-29): 30 ... 21 // æ ¹æ®3è¡10åçç½æ ¼ï¼ä»å·¦å°å³è®¡ç®æ ¼åID // 第1è¡ (ä½ç½®0-9): 1 ... 10 // 第2è¡ (ä½ç½®10-19): 11 ... 20 // 第3è¡ (ä½ç½®20-29): 21 ... 30 int row = position / 10; int col = position % 10; int boxId = (row + 1) * 10 - col; int boxId = row * 10 + (col + 1); holder.tvBoxNumber.setText(String.valueOf(boxId)); @@ -676,18 +995,18 @@ } if (status != null) { // Priority: Online > Glass // User requirement: "ä¼å æ¾ç¤ºæ¯å¦å¨çº¿" (Prioritize displaying online status) // "19-26ä¸ºä¹æ¯16è¿å¶ï¼è§£ææäºè¿å¶32ä»£ç æè¿30ä¸ªæ ¼åæ¯å¦å¨çº¿" // Usually this means if offline, show offline color. If online, show state (glass/no glass). // ä¼å 级: å¨çº¿ç¶æ > ç»çç¶æ // ç¨æ·éæ±: "ä¼å æ¾ç¤ºæ¯å¦å¨çº¿" // "19-26ä½ä¹æ¯16è¿å¶ï¼è§£ææäºè¿å¶32ä½è¡¨ç¤º30ä¸ªæ ¼åçå¨çº¿ç¶æ" // é常æå³çå¦æç¦»çº¿ï¼æ¾ç¤ºç¦»çº¿é¢è²ï¼å¦æå¨çº¿ï¼æ¾ç¤ºç»çç¶æï¼æ/æ ï¼ if (!status.isOnline) { holder.viewBox.setBackgroundResource(R.drawable.bg_box_offline); // Offline (Grey) holder.viewBox.setBackgroundResource(R.drawable.bg_box_offline); // 离线ï¼ç°è²ï¼ } else { if (status.hasGlass) { holder.viewBox.setBackgroundResource(R.drawable.bg_box_full); // Green holder.viewBox.setBackgroundResource(R.drawable.bg_box_full); // ç»¿è² } else { holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty); // Online but empty (White) holder.viewBox.setBackgroundResource(R.drawable.bg_box_empty); // å¨çº¿ä½æ ç»çï¼ç½è²ï¼ } } } app/src/main/res/drawable/ic_arrow_drop_down.xml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,10 @@ <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" android:tint="#999999"> <path android:fillColor="@android:color/white" android:pathData="M7,10l5,5 5,-5H7z"/> </vector> app/src/main/res/layout/activity_home.xml
@@ -70,6 +70,7 @@ android:orientation="vertical"> <TextView android:id="@+id/tv_menu_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="COMB" @@ -112,8 +113,8 @@ <ImageView android:layout_width="22dp" android:layout_height="22dp" android:src="@mipmap/icon_record" app:tint="@color/drawer_item_text_gray" /> android:src="@mipmap/icon_1" /> <TextView android:layout_width="wrap_content" @@ -131,14 +132,14 @@ android:layout_height="48dp" android:gravity="center_vertical" android:orientation="horizontal" android:paddingLeft="20dp" android:paddingLeft="21dp" android:paddingRight="20dp"> <ImageView android:layout_width="22dp" android:layout_height="22dp" android:src="@mipmap/ic_bluetooth" app:tint="@color/drawer_item_text_gray" /> android:layout_width="20dp" android:layout_height="20dp" android:src="@mipmap/icon_2" /> <TextView android:layout_width="wrap_content" @@ -148,13 +149,6 @@ android:textColor="@color/drawer_item_text_gray" android:textSize="15sp" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:background="@color/drawer_divider" /> <!-- åæ°è®¾å® --> <LinearLayout @@ -169,8 +163,8 @@ <ImageView android:layout_width="22dp" android:layout_height="22dp" android:src="@mipmap/icon_topbar_overflow" app:tint="@color/drawer_item_text_gray" /> android:src="@mipmap/icon_3" /> <TextView android:layout_width="wrap_content" @@ -181,12 +175,7 @@ android:textSize="15sp" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginTop="5dp" android:layout_marginBottom="5dp" android:background="@color/drawer_divider" /> <!-- å½åè®¾å¤ --> <LinearLayout @@ -201,8 +190,8 @@ <ImageView android:layout_width="22dp" android:layout_height="22dp" android:src="@mipmap/icon_topbar_about" app:tint="@color/drawer_item_text_gray" /> android:src="@mipmap/icon_4" /> <TextView android:layout_width="wrap_content" @@ -226,8 +215,8 @@ <ImageView android:layout_width="22dp" android:layout_height="22dp" android:src="@mipmap/icon_topbar_overflow" app:tint="@color/drawer_item_text_gray" /> android:src="@mipmap/icon_5" /> <TextView android:layout_width="wrap_content" @@ -238,6 +227,32 @@ android:textSize="15sp" /> </LinearLayout> <!-- ç³»ç»è®¾ç½® --> <LinearLayout android:id="@+id/menu_system_settings" android:layout_width="match_parent" android:layout_height="48dp" android:gravity="center_vertical" android:orientation="horizontal" android:paddingLeft="20dp" android:paddingRight="20dp" android:visibility="gone"> <ImageView android:layout_width="22dp" android:layout_height="22dp" android:src="@mipmap/icon_6" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="25dp" android:text="ç³»ç»è®¾ç½®" android:textColor="@color/drawer_item_text_gray" android:textSize="15sp" /> </LinearLayout> </LinearLayout> </ScrollView> app/src/main/res/layout/activity_parameter_setting.xml
@@ -39,7 +39,7 @@ android:orientation="horizontal" android:gravity="center_vertical" android:padding="16dp" android:background="#FAFAFA"> android:background="@color/white"> <View android:layout_width="4dp" @@ -103,7 +103,7 @@ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="èªæ£æå" android:text="æå" android:textSize="12sp" android:textColor="#666666" android:layout_marginEnd="16dp"/> @@ -116,7 +116,7 @@ <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="èªæ£å¤±è´¥" android:text="失败" android:textSize="12sp" android:textColor="#666666" android:layout_marginEnd="16dp"/> @@ -147,11 +147,9 @@ android:layout_height="48dp" android:layout_weight="1" android:text="ç»æèªæ£" android:textColor="@color/base_color" android:textColor="@color/white" android:textSize="14sp" app:qmui_backgroundColor="@color/white" app:qmui_borderColor="@color/base_color" app:qmui_borderWidth="1dp" app:qmui_backgroundColor="@color/base_color" app:qmui_radius="8dp" android:layout_marginLeft="10dp"/> </LinearLayout> @@ -189,15 +187,29 @@ android:layout_height="wrap_content" android:orientation="horizontal"> <Spinner android:id="@+id/sp_threshold_mode" <FrameLayout android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1.5" android:entries="@array/threshold_modes" android:background="@drawable/bg_input_field" android:paddingStart="10dp" android:layout_marginRight="10dp" /> android:layout_marginRight="10dp"> <Spinner android:id="@+id/sp_threshold_mode" android:layout_width="match_parent" android:layout_height="match_parent" android:entries="@array/threshold_modes" android:background="@drawable/bg_input_field" android:paddingStart="10dp" android:paddingEnd="32dp" /> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@drawable/ic_arrow_drop_down" android:layout_gravity="center_vertical|end" android:layout_marginEnd="8dp" android:importantForAccessibility="no"/> </FrameLayout> <EditText android:id="@+id/et_threshold_value" @@ -218,18 +230,6 @@ android:orientation="horizontal"> <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton android:id="@+id/btn_threshold_read" android:layout_width="0dp" android:layout_height="45dp" android:layout_weight="1" android:text="读å" android:textColor="@color/white" android:textSize="14sp" app:qmui_backgroundColor="@color/base_color" app:qmui_radius="8dp" android:layout_marginRight="10dp"/> <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton android:id="@+id/btn_threshold_write" android:layout_width="0dp" android:layout_height="45dp" @@ -237,7 +237,7 @@ android:text="åå ¥" android:textColor="@color/white" android:textSize="14sp" app:qmui_backgroundColor="@color/base_color_s" app:qmui_backgroundColor="@color/base_color" app:qmui_radius="8dp"/> </LinearLayout> </LinearLayout> @@ -269,15 +269,29 @@ android:layout_height="wrap_content" android:orientation="horizontal"> <Spinner android:id="@+id/sp_sensitivity_mode" <FrameLayout android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1.5" android:entries="@array/sensitivity_modes" android:background="@drawable/bg_input_field" android:paddingStart="10dp" android:layout_marginRight="10dp" /> android:layout_marginRight="10dp"> <Spinner android:id="@+id/sp_sensitivity_mode" android:layout_width="match_parent" android:layout_height="match_parent" android:entries="@array/sensitivity_modes" android:background="@drawable/bg_input_field" android:paddingStart="10dp" android:paddingEnd="32dp" /> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@drawable/ic_arrow_drop_down" android:layout_gravity="center_vertical|end" android:layout_marginEnd="8dp" android:importantForAccessibility="no"/> </FrameLayout> <EditText android:id="@+id/et_sensitivity_value" @@ -298,18 +312,6 @@ android:orientation="horizontal"> <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton android:id="@+id/btn_sensitivity_read" android:layout_width="0dp" android:layout_height="45dp" android:layout_weight="1" android:text="读å" android:textColor="@color/white" android:textSize="14sp" app:qmui_backgroundColor="@color/base_color" app:qmui_radius="8dp" android:layout_marginRight="10dp"/> <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton android:id="@+id/btn_sensitivity_write" android:layout_width="0dp" android:layout_height="45dp" @@ -317,7 +319,7 @@ android:text="åå ¥" android:textColor="@color/white" android:textSize="14sp" app:qmui_backgroundColor="@color/base_color_s" app:qmui_backgroundColor="@color/base_color" app:qmui_radius="8dp"/> </LinearLayout> </LinearLayout> @@ -356,7 +358,7 @@ android:text="主æ¿å¤ä½" android:textColor="@color/white" android:textSize="14sp" app:qmui_backgroundColor="@color/orange" app:qmui_backgroundColor="@color/base_color" app:qmui_radius="8dp" android:layout_marginRight="10dp"/> @@ -368,7 +370,7 @@ android:text="ä¼ æå¤ä½" android:textColor="@color/white" android:textSize="14sp" app:qmui_backgroundColor="@color/orange" app:qmui_backgroundColor="@color/base_color" app:qmui_radius="8dp" android:layout_marginLeft="10dp"/> </LinearLayout> @@ -377,6 +379,7 @@ <!-- Log Display --> <androidx.cardview.widget.CardView android:id="@+id/cv_log" android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="12dp" app/src/main/res/layout/activity_system_settings.xml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,22 @@ <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/qmui_config_color_background" android:orientation="vertical"> <include layout="@layout/include_head"/> <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView android:id="@+id/groupListView" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="20dp"/> </ScrollView> </LinearLayout> app/src/main/res/layout/fragment_settings.xml
@@ -28,40 +28,40 @@ <!-- Card Header --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:padding="16dp" android:background="#FAFAFA"> <View android:layout_width="4dp" android:layout_height="16dp" android:background="@color/base_color" android:layout_marginEnd="8dp"/> <TextView android:layout_width="0dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_weight="1" android:text="çæ§è¯¦æ " android:textSize="16sp" android:textStyle="bold" android:textColor="#333333" /> <com.qmuiteam.qmui.widget.roundwidget.QMUIRoundButton android:id="@+id/btn_read_data" android:layout_width="wrap_content" android:layout_height="32dp" android:text="è¯»åæ°æ®" android:textColor="@color/white" android:textSize="12sp" android:paddingStart="12dp" android:paddingEnd="12dp" app:qmui_backgroundColor="@color/base_color" app:qmui_radius="4dp"/> </LinearLayout> android:orientation="horizontal" android:gravity="center_vertical" android:padding="16dp" android:background="@color/white"> <View android:layout_width="4dp" android:layout_height="16dp" android:background="@color/base_color" android:layout_marginEnd="8dp"/> <TextView android:id="@+id/tv_monitor_title" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="çæ§è¯¦æ " android:textSize="16sp" android:textStyle="bold" android:textColor="#333333" android:clickable="true" android:focusable="true" /> <TextView android:id="@+id/tv_monitor_update_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="" android:textSize="12sp" android:textColor="#999999"/> </LinearLayout> <View android:layout_width="match_parent" @@ -163,7 +163,7 @@ android:orientation="horizontal" android:gravity="center_vertical" android:padding="16dp" android:background="#FAFAFA"> android:background="@color/white"> <View android:layout_width="4dp" @@ -192,145 +192,144 @@ android:orientation="vertical" android:padding="16dp"> <!-- Layer Input --> <!-- Grid Layout for Parameters --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginBottom="16dp"> <!-- Row 1: Labels --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="8dp"> <TextView android:layout_width="wrap_content" <!-- Layer Label --> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:text="屿°" android:textSize="14sp" android:textColor="#666666"/> <TextView android:id="@+id/tv_layer_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:layout_marginStart="8dp"/> </LinearLayout> <EditText android:id="@+id/et_layer" android:layout_width="match_parent" android:layout_height="48dp" android:background="@drawable/bg_input_field" android:paddingStart="16dp" android:paddingEnd="16dp" android:hint="屿° (1-30)" android:textColorHint="#CCCCCC" android:textSize="14sp" android:focusable="false" android:focusableInTouchMode="false" android:inputType="none"/> </LinearLayout> android:layout_weight="1" android:orientation="horizontal" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="屿°" android:textSize="14sp" android:textColor="#666666"/> <TextView android:id="@+id/tv_layer_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:layout_marginStart="4dp"/> </LinearLayout> <!-- Station Input --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:layout_marginBottom="16dp"> <!-- Station Label --> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="horizontal" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ç«å·" android:textSize="14sp" android:textColor="#666666"/> <TextView android:id="@+id/tv_station_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:layout_marginStart="4dp"/> </LinearLayout> <!-- Baud Label --> <LinearLayout android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1.5" android:orientation="horizontal" android:gravity="center"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="æ³¢ç¹ç" android:textSize="14sp" android:textColor="#666666"/> <TextView android:id="@+id/tv_baud_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:layout_marginStart="4dp"/> </LinearLayout> </LinearLayout> <!-- Row 2: Values --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="8dp"> android:gravity="center_vertical"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ç«å·" android:textSize="14sp" android:textColor="#666666"/> <!-- Layer Value --> <TextView android:id="@+id/et_layer" android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1" android:background="@android:color/transparent" android:gravity="center" android:text="å¾ è¯»å" android:textSize="16sp" android:textStyle="bold" android:textColor="#999999" android:layout_marginEnd="4dp" /> <TextView android:id="@+id/tv_station_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:layout_marginStart="8dp"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <!-- Station Value --> <EditText android:id="@+id/et_station" android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1" android:background="@drawable/bg_input_field" android:paddingStart="16dp" android:paddingEnd="16dp" android:hint="请è¾å ¥ç«å· (1-64)" android:gravity="center" android:hint="ç«å·" android:textColorHint="#CCCCCC" android:textSize="14sp" android:inputType="number"/> </LinearLayout> </LinearLayout> android:inputType="number" android:layout_marginStart="4dp" android:layout_marginEnd="4dp"/> <!-- Baud Rate --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:gravity="center_vertical" android:layout_marginBottom="8dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="æ³¢ç¹ç" android:textSize="14sp" android:textColor="#666666"/> <TextView android:id="@+id/tv_baud_status" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textSize="10sp" android:layout_marginStart="8dp"/> </LinearLayout> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <!-- Baud Value --> <FrameLayout android:layout_width="0dp" android:layout_height="48dp" android:layout_weight="1" android:background="@drawable/bg_input_field"> android:layout_weight="1.5" android:background="@drawable/bg_input_field" android:layout_marginStart="4dp"> <Spinner android:id="@+id/spinner_baud" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@null" android:paddingStart="8dp"/> android:paddingEnd="32dp" /> <ImageView android:layout_width="24dp" android:layout_height="24dp" android:src="@drawable/ic_arrow_drop_down" android:layout_gravity="center_vertical|end" android:layout_marginEnd="8dp" android:importantForAccessibility="no"/> </FrameLayout> </LinearLayout> </LinearLayout> @@ -364,7 +363,7 @@ android:textColor="@color/white" android:textSize="16sp" android:textStyle="bold" app:qmui_backgroundColor="@color/base_color_s" app:qmui_backgroundColor="@color/base_color" app:qmui_radius="8dp"/> </LinearLayout> @@ -384,6 +383,7 @@ <!-- Log Display --> <androidx.cardview.widget.CardView android:id="@+id/cv_log" android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="12dp" app/src/main/res/mipmap-hdpi/ic_launcher.pngapp/src/main/res/mipmap-mdpi/ic_launcher.pngapp/src/main/res/mipmap-xhdpi/ic_launcher.pngapp/src/main/res/mipmap-xhdpi/icon_1.png
app/src/main/res/mipmap-xhdpi/icon_2.png
app/src/main/res/mipmap-xhdpi/icon_3.png
app/src/main/res/mipmap-xhdpi/icon_4.png
app/src/main/res/mipmap-xhdpi/icon_5.png
app/src/main/res/mipmap-xhdpi/icon_6.png
app/src/main/res/mipmap-xxhdpi/ic_launcher.pngapp/src/main/res/mipmap-xxxhdpi/ic_launcher.pngapp/src/main/res/values-land/dimens.xml
@@ -1,3 +1,3 @@ <resources> <dimen name="fab_margin">48dp</dimen> </resources> app/src/main/res/values-w1240dp/dimens.xml
@@ -1,3 +1,3 @@ <resources> <dimen name="fab_margin">200dp</dimen> </resources> app/src/main/res/values-w600dp/dimens.xml
@@ -1,3 +1,3 @@ <resources> <dimen name="fab_margin">48dp</dimen> </resources>