3 Commits
1.0.4 ... 1.0.5

Author SHA1 Message Date
Zhanghu
cac9dd41aa 如果串口路径不为空时则开关一次设备 2026-03-05 14:47:07 +08:00
Zhanghu
d29aa0dfd1 * 仅串口路径为空时才自动检测设备
* 设置界面可清除串口路径
2026-03-05 14:04:38 +08:00
Zhanghu
5b0eb711a6 changelog 2026-01-05 16:57:27 +08:00
6 changed files with 133 additions and 6 deletions

View File

@@ -9,7 +9,17 @@ author:
2. $ {VERSION_CODE} (去掉空格),会自动替换实际修订号,比如 1.1.4.$ {VERSION_CODE} 2. $ {VERSION_CODE} (去掉空格),会自动替换实际修订号,比如 1.1.4.$ {VERSION_CODE}
--> -->
### [1.0.4.${VERSION_CODE}] - 2026.1.5 ### [1.0.5.${VERSION_CODE}] - 2026.3.5
#### 文件下载
* [dashboardclient_1.0.5.apk](dashboardclient_1.0.5.apk)
#### 更新记录
* App 启动时如果串口路径为空时自动检测设备, 否则开关一次设备
* 设置界面可清除串口路径
### [1.0.4.23] - 2026.1.5
#### 文件下载 #### 文件下载

View File

@@ -110,6 +110,9 @@ public class BuildingDashboardActivity extends FullscreenActivity {
configUrl = urlPrefix + "/data/config.json"; configUrl = urlPrefix + "/data/config.json";
mainUrl = urlEndPoint.isEmpty() ? urlPrefix : urlPrefix + "/" + urlEndPoint; mainUrl = urlEndPoint.isEmpty() ? urlPrefix : urlPrefix + "/" + urlEndPoint;
Log.i(TAG, "Main: " + mainUrl);
Log.i(TAG, "Config: " + configUrl);
configLoadHandler.post(configLoadRunnable); configLoadHandler.post(configLoadRunnable);
loadUrlWithRetry(); loadUrlWithRetry();
} }
@@ -117,15 +120,34 @@ public class BuildingDashboardActivity extends FullscreenActivity {
private void initSerialPort() { private void initSerialPort() {
String portPath = PreferenceConfiguration.getSerialPortPath(this); String portPath = PreferenceConfiguration.getSerialPortPath(this);
int baudRate = PreferenceConfiguration.getSerialPortBaudRate(this); int baudRate = PreferenceConfiguration.getSerialPortBaudRate(this);
boolean sendPowerOnCmd = PreferenceConfiguration.getSendPowerOnCmd(this);
// 初始化串口检测器 // 初始化串口检测器
detector = new SerialPortDetector(portPath, baudRate); detector = new SerialPortDetector(portPath, baudRate);
// 每次启动都进行串口检测/验证 // 只有 portPath 为空时才进行串口检测
// 参数 true 表示确保设备开机(发送两个命令) if (TextUtils.isEmpty(portPath)) {
// 参数 false 表示仅检测串口(任一命令有响应即可) Log.i(TAG, "Starting serial port detection/verification...");
Log.i(TAG, "Starting serial port detection/verification..."); // 参数 true 表示确保设备开机(发送两个命令)
startSerialPortDetection(true); // 参数 false 表示仅检测串口(任一命令有响应即可)
startSerialPortDetection(true);
} else {
Log.i(TAG, "Using saved serial port path: " + portPath);
// 如果勾选了"发送打开电源指令",则先发送关闭再发送打开
if (sendPowerOnCmd) {
Log.i(TAG, "Sending power on sequence...");
new Thread(() -> {
detector.sendPowerOffCommand();
try {
Thread.sleep(3000); // 等待 3 秒
} catch (InterruptedException e) {
Log.e(TAG, "Sleep interrupted", e);
}
detector.sendPowerOnCommand();
}).start();
}
}
} }
/** /**
@@ -276,6 +298,8 @@ public class BuildingDashboardActivity extends FullscreenActivity {
WebSettings webSettings = binding.webview.getSettings(); WebSettings webSettings = binding.webview.getSettings();
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
webSettings.setJavaScriptEnabled(true); webSettings.setJavaScriptEnabled(true);
// 允许媒体自动播放 (Android 8+ 必须)
webSettings.setMediaPlaybackRequiresUserGesture(false);
// 禁用缩放相关设置 // 禁用缩放相关设置
webSettings.setTextZoom(100); webSettings.setTextZoom(100);

View File

@@ -1,12 +1,16 @@
package cn.ykbox.dashboard.activity; package cn.ykbox.dashboard.activity;
import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceFragmentCompat;
import cn.ykbox.dashboard.R; import cn.ykbox.dashboard.R;
import cn.ykbox.dashboard.perferences.PreferenceConfiguration;
public class SettingsActivity extends AppCompatActivity { public class SettingsActivity extends AppCompatActivity {
@@ -30,6 +34,21 @@ public class SettingsActivity extends AppCompatActivity {
@Override @Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.root_preferences, rootKey); setPreferencesFromResource(R.xml.root_preferences, rootKey);
Preference clearDevicePref = findPreference("k_clear_device");
if (clearDevicePref != null) {
clearDevicePref.setOnPreferenceClickListener(preference -> {
new AlertDialog.Builder(requireContext())
.setTitle("确认清除")
.setMessage("确定要清除当前串口设备路径吗?")
.setPositiveButton("确定", (dialog, which) -> {
PreferenceConfiguration.setSerialPortPath(requireContext(), "");
})
.setNegativeButton("取消", null)
.show();
return true;
});
}
} }
} }
} }

View File

@@ -13,6 +13,7 @@ public class PreferenceConfiguration {
private static final String KEY_SERIAL_PORT_PATH = "k_serial_port_path"; private static final String KEY_SERIAL_PORT_PATH = "k_serial_port_path";
private static final String KEY_SERIAL_PORT_BAUD_RATE = "k_serial_baud"; private static final String KEY_SERIAL_PORT_BAUD_RATE = "k_serial_baud";
private static final String KEY_SEND_POWER_ON_CMD = "k_send_power_on_cmd";
public static String getSerialPortPath(Context context) { public static String getSerialPortPath(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
@@ -29,4 +30,9 @@ public class PreferenceConfiguration {
String baud = prefs.getString(KEY_SERIAL_PORT_BAUD_RATE, "9600"); String baud = prefs.getString(KEY_SERIAL_PORT_BAUD_RATE, "9600");
return Integer.parseInt(baud); return Integer.parseInt(baud);
} }
public static boolean getSendPowerOnCmd(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
return prefs.getBoolean(KEY_SEND_POWER_ON_CMD, true);
}
} }

View File

@@ -45,6 +45,51 @@ public class SerialPortDetector {
this.callback = callback; this.callback = callback;
} }
/**
* 发送关闭设备指令到已保存的串口(不等待响应)
*/
public void sendPowerOffCommand() {
sendCommand(TEST_CMD_OFF);
}
/**
* 发送打开设备指令到已保存的串口(不等待响应)
*/
public void sendPowerOnCommand() {
sendCommand(TEST_CMD_ON);
}
/**
* 发送指令到已保存的串口(不等待响应)
* @param commandHex 十六进制命令字符串
*/
private void sendCommand(String commandHex) {
if (TextUtils.isEmpty(savedPath)) {
Log.w(TAG, "No serial port path configured");
return;
}
new Thread(() -> {
SerialHelper serialHelper = null;
try {
serialHelper = new SimpleSerialHelper(savedPath, baudRate);
serialHelper.open();
serialHelper.sendHex(commandHex);
Log.d(TAG, "Sent command: " + commandHex);
} catch (Exception e) {
Log.e(TAG, "Error sending command: " + e.getMessage());
} finally {
if (serialHelper != null) {
try {
serialHelper.close();
} catch (Exception e) {
Log.e(TAG, "Error closing port: " + e.getMessage());
}
}
}
}).start();
}
/** /**
* 开始串口检测(智能检测) * 开始串口检测(智能检测)
* 先验证已保存的串口,如果不可用再进行全局检测 * 先验证已保存的串口,如果不可用再进行全局检测
@@ -226,6 +271,20 @@ public class SerialPortDetector {
} }
} }
/**
* 简单的串口 Helper用于只发送命令不等待响应
*/
private static class SimpleSerialHelper extends SerialHelper {
public SimpleSerialHelper(String sPort, int iBaudRate) {
super(sPort, iBaudRate);
}
@Override
protected void onDataReceived(ComBean comBean) {
// 不需要处理响应
}
}
/** /**
* 内部测试用的 SerialHelper * 内部测试用的 SerialHelper
*/ */

View File

@@ -25,5 +25,14 @@
app:title="串口波特率" app:title="串口波特率"
app:defaultValue="9600" app:defaultValue="9600"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<CheckBoxPreference
app:key="k_send_power_on_cmd"
app:title="App 启动时发送打开电源指令"
app:summary="每次启动 App 时先发送关闭再发送打开电源指令"
app:defaultValue="true" />
<Preference
app:key="k_clear_device"
app:title="清除设备"
app:summary="清除当前串口设备路径, 下次启动时自动检测设备。" />
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>