diff --git a/.agents/workflows/compiler-apk.md b/.agents/workflows/compiler-apk.md index 08cd42d..a2242dd 100644 --- a/.agents/workflows/compiler-apk.md +++ b/.agents/workflows/compiler-apk.md @@ -157,6 +157,7 @@ if [ -n "$APK_SOURCE" ]; then echo "* 版本名稱 (versionName):$VERSION_NAME" echo "* 版本代碼 (versionCode):$VERSION_CODE" echo "* APK 檔案名稱:$APK_NAME" + echo "* 更新說明:[請在此處附上口語化的一句話更新說明]" echo "==================================================" else echo "❌ 找不到編譯產出的 APK 檔案,複製失敗!" diff --git a/app/build.gradle b/app/build.gradle index c0a409a..a85360a 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,7 +9,7 @@ plugins { // 避免不同尺寸機台間版本號產生衝突。 // ========================================================================= def verMinor = 1 -def verPatch = 38 +def verPatch = 56 android { diff --git a/app/src/main/java/com/unibuy/smartdevice/tools/OtaManager.java b/app/src/main/java/com/unibuy/smartdevice/tools/OtaManager.java index f40d825..ed1542f 100644 --- a/app/src/main/java/com/unibuy/smartdevice/tools/OtaManager.java +++ b/app/src/main/java/com/unibuy/smartdevice/tools/OtaManager.java @@ -17,6 +17,8 @@ import java.net.URL; public class OtaManager { private static final String TAG = "OtaManager"; private static final Logs logs = new Logs(OtaManager.class); + private static final int DOWNLOAD_CONNECT_TIMEOUT_MS = 30000; + private static final int DOWNLOAD_READ_TIMEOUT_MS = 120000; /** * 啟動背景執行緒執行 APP OTA 更新 @@ -67,8 +69,8 @@ public class OtaManager { private static File downloadApk(Context context, String apkUrl) throws Exception { URL url = new URL(apkUrl); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setConnectTimeout(15000); - connection.setReadTimeout(15000); + connection.setConnectTimeout(DOWNLOAD_CONNECT_TIMEOUT_MS); + connection.setReadTimeout(DOWNLOAD_READ_TIMEOUT_MS); connection.connect(); if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) { diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java index bfa0bd8..1b46301 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java @@ -118,11 +118,11 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl private Fragment activeFragment; private Fragment vmcFragment; private Fragment electricFragment; - private static ReportShotTimeByScheduler reportOnScheduler; + private ReportShotTimeByScheduler reportOnScheduler; private ConnectivityManager connectivityManager; private ConnectivityManager.NetworkCallback networkCallback; - private static final long IDLE_TIMEOUT = 5 * 60 * 1000; // 3 分鐘 + private static final long IDLE_TIMEOUT = 5 * 60 * 1000; // 5 分鐘 private final long TIME_LIMIT = 200000; private int clickCount = 0; private long firstClickTime = 0; @@ -263,7 +263,8 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl // 【第一層】定時重啟:到了整點就啟動等待模式(RestartAPP2 自己會等閒置 >= 90 秒才真重啟) if (timeByM.equals("04:00") || timeByM.equals("08:00") || timeByM.equals("12:00") || timeByM.equals("16:00") || timeByM.equals("20:00") || timeByM.equals("23:59")) { - android.content.SharedPreferences prefs = getSharedPreferences("AppRestartPrefs", Context.MODE_PRIVATE); + android.content.SharedPreferences prefs = getSharedPreferences("AppRestartPrefs", + Context.MODE_PRIVATE); String today = new SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(new Date()); String currentSlot = today + " " + timeByM; String lastRestartSlot = prefs.getString("last_restart_slot", ""); @@ -280,11 +281,11 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl // 【第二層】閒置深度刷新:任何時間閒置超過 30 分鐘就重啟 (暫時關閉,改依賴定時重啟) /* - if (MyApp.getInstance().getIdleSeconds() >= 30 * 60) { - getLogs().info("閒置超過 30 分鐘,執行深度排毒重啟..."); - new RestartAPP2(this).start(); - } - */ + * if (MyApp.getInstance().getIdleSeconds() >= 30 * 60) { + * getLogs().info("閒置超過 30 分鐘,執行深度排毒重啟..."); + * new RestartAPP2(this).start(); + * } + */ // 這裡判斷每5分鐘去跑連線下位機的狀態 // String minutePart = timeByM.split(":")[1]; // 取得 "mm" 部分 @@ -305,24 +306,24 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl // new CMUHAutoRun(this).start(); } - getLogs().update("ThreadCount", String.valueOf(Tools.threadCount())); - String tc = getLogs().getMessage(); - getLogs().update("ViceoCount", String.valueOf(VideoEoxPlayer.getExoPlayerTotal())); - String vc = getLogs().getMessage(); - binding.textThreadCount.setText("TC:" + tc + " VC:" + vc); - getLogs().clearMessage(); + // 直接使用區域變數更新 UI,避免多執行緒 Race Condition 導致 getLogs().getMessage() 被改寫或清空而使 UI 變空 + String threadCountStr = String.valueOf(Tools.threadCount()); + String videoCountStr = String.valueOf(VideoEoxPlayer.getExoPlayerTotal()); + binding.textThreadCount.setText("TC:" + threadCountStr + " VC:" + videoCountStr); + + getLogs().update("ThreadCount", threadCountStr); + getLogs().update("ViceoCount", videoCountStr); Tools.threadInfos(); String networkDevice = getNetworkDevice(); + binding.textNetwork.setText(getString(R.string.network) + ":" + networkDevice); getLogs().update("Network", networkDevice); - binding.textNetwork.setText(getString(R.string.network) + ":" + getLogs().getMessage()); - getLogs().clearMessage(); DevXinYuanController.setVersion(ver); + String tempStr = DevXinYuanController.getTemperature() + "℃"; + binding.textTemperature.setText(getString(R.string.temp) + ":" + tempStr); getLogs().update("Temperature", String.valueOf(DevXinYuanController.getTemperature()), "℃"); - binding.textTemperature.setText(getString(R.string.temp) + ":" + getLogs().getMessage()); - getLogs().clearMessage(); try { // httpAPI.detectHost(); @@ -467,7 +468,6 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl return saleFlowHandler; } - public void startOnSwitchFragment() { try { stopOnSwitchFragment(); // ✅ 先安全釋放舊播放器 @@ -487,7 +487,8 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl videoEoxPlayer = new VideoEoxPlayer(getCtx()); // ✅ 用新版建構子 getHandlerMain().getHandler().postDelayed(() -> { - if (isFinishing() || isDestroyed()) return; + if (isFinishing() || isDestroyed()) + return; if (videoEoxPlayer != null) { // videoEoxPlayer.playMedia(binding.videoView, binding.imageAd); // ✅ 傳入 view // 開始播放 @@ -700,12 +701,7 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl binding.floatButtonShoppingCart.setVisibility(View.GONE); } - if (reportOnScheduler != null) { - reportOnScheduler.shutdown(); - } - - reportOnScheduler = new ReportShotTimeByScheduler(getHandlerMain()); - reportOnScheduler.start(1L, 10L, TimeUnit.SECONDS); + startHeaderReportScheduler(); // binding.btnswitchlanTW.setOnClickListener(v -> switchLanguage("tw")); // @@ -753,6 +749,20 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl startOnSwitchFragment(); } + private void startHeaderReportScheduler() { + stopHeaderReportScheduler(); + reportOnScheduler = new ReportShotTimeByScheduler(getHandlerMain()); + getHandlerMain().start(getClass().getSimpleName(), Option.REPORT_AND_SHOT_TIME.getOption(), "start"); + reportOnScheduler.start(1L, 10L, TimeUnit.SECONDS); + } + + private void stopHeaderReportScheduler() { + if (reportOnScheduler != null) { + reportOnScheduler.shutdown(); + reportOnScheduler = null; + } + } + private void hiddenMultiLanguageButton() { binding.btnswitchlanTW.setVisibility(View.GONE); binding.btnswitchlanEn.setVisibility(View.GONE); @@ -1230,9 +1240,6 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl } } - if (reportOnScheduler != null) { - reportOnScheduler.shutdown(); - reportOnScheduler = null; - } + stopHeaderReportScheduler(); } }