From 028cac10b5aaa5f137025d16d010bed91d6e6d7d Mon Sep 17 00:00:00 2001 From: sky121113 Date: Wed, 13 May 2026 09:33:32 +0800 Subject: [PATCH] =?UTF-8?q?[FIX]=20=E5=84=AA=E5=8C=96=E5=87=BA=E8=B2=A8?= =?UTF-8?q?=E6=B5=81=E7=A8=8B=20UI=20=E9=AB=94=E9=A9=97=E8=88=87=E4=BF=AE?= =?UTF-8?q?=E5=BE=A9=E9=9B=99=E9=87=8D=E5=80=92=E6=95=B8=20Bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修復出貨結案時,因 VMC 重複回傳訊號導致 15 秒倒數重置的 Bug。 2. 實作 isFinalizing 狀態鎖,確保結案流程不被舊有的定時器或重複訊號干擾。 3. 優化出貨流程 UI:初始狀態顯示「準備出貨」,取代原先錯誤的提示文字。 4. 調整「取貨確認」按鈕邏輯:出貨中隱藏按鈕,僅在 15 秒取貨倒數時顯示,並支援使用者提前確認取貨。 5. 修正「員工卡」錯字並同步多語系(英、日)字串資源。 6. 在 MqttService 與 FontendActivity 實作 isMachineBusy() 檢查,防止交易過程中觸發遠端重啟。 7. 優化 VMC 偵測邏輯,加入重複訊號過濾防護。 --- .agents/workflows/compiler-apk.md | 18 ++--- .../smartdevice/service/MqttService.java | 5 ++ .../smartdevice/ui/FontendActivity.java | 26 +++++++ .../ui/devs/vmc/RecyclerVmcBuyListAdpter.java | 15 +++++ .../smartdevice/ui/dialog/DispatchDialog.java | 67 +++++++++++++++++-- .../ui/dialog/MemberCardPickupDialog.java | 14 ++++ app/src/main/res/values-en/strings.xml | 1 + app/src/main/res/values-ja/strings.xml | 1 + app/src/main/res/values/strings.xml | 3 +- 9 files changed, 135 insertions(+), 15 deletions(-) diff --git a/.agents/workflows/compiler-apk.md b/.agents/workflows/compiler-apk.md index f4de6a7..4b97a0a 100644 --- a/.agents/workflows/compiler-apk.md +++ b/.agents/workflows/compiler-apk.md @@ -17,33 +17,35 @@ cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai ``` ## 步驟二:導出 APK 到 outputs 資料夾 -將編譯出來的深層 APK 複製到專案最外層的 `outputs` 資料夾,並統一命名為 `TaiwanStar_debug.apk`。 +將編譯出來的 APK 複製到專案最外層的 `outputs` 資料夾。 ```bash // turbo cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai mkdir -p outputs -cp app/build/outputs/apk/S_12_XY_U_Standard_/debug/app-S_12_XY_U_Standard_-10_08_2_R-debug.apk outputs/TaiwanStar_debug.apk -echo "✅ APK 已經複製到專案根目錄的 outputs 資料夾中" +APK_SOURCE="app/build/outputs/apk/S_12_XY_U_Standard_/debug/app-S_12_XY_U_Standard_-10_08_2_R-debug.apk" +APK_DEST="outputs/TaiwanStar_debug.apk" + +cp $APK_SOURCE $APK_DEST +echo "✅ APK 已經複製到 outputs 資料夾,路徑為:$APK_DEST" ``` -## 步驟三:安裝並啟動 App (如果有連接設備) -利用 WSL 串接 Windows 端的 ADB,偵測到設備後才執行安裝與啟動。 +## 步驟三:安裝並啟動 App (如有連接設備) +偵測到設備後才執行安裝。如果未偵測到,則保留 APK 檔案並結束工作流。 ```bash // turbo ADB_PATH="/mnt/c/Users/User/AppData/Local/Android/Sdk/platform-tools/adb.exe" -APK_PATH="/home/mama/projects/TaiwanStar_general_size_s_Chengwai/outputs/TaiwanStar_debug.apk" # 檢查是否有設備連接 DEVICE_COUNT=$($ADB_PATH devices | grep -v "List of devices" | grep "device" | wc -l) if [ "$DEVICE_COUNT" -gt 0 ]; then echo "📱 偵測到 $DEVICE_COUNT 個設備,開始安裝..." - $ADB_PATH install -r -d -t $APK_PATH + $ADB_PATH install -r -d -t "/home/mama/projects/TaiwanStar_general_size_s_Chengwai/outputs/TaiwanStar_debug.apk" echo "🚀 安裝完成,正在啟動 App..." $ADB_PATH shell am start -n com.unibuy.smartdevice/com.unibuy.smartdevice.HomeActivity else - echo "⚠️ 未偵測到連接的手機或設備,跳過安裝與啟動步驟。" + echo "⚠️ 未偵測到連接的手機或設備,已保留 APK 檔案,跳過安裝步驟。" fi ``` diff --git a/app/src/main/java/com/unibuy/smartdevice/service/MqttService.java b/app/src/main/java/com/unibuy/smartdevice/service/MqttService.java index 1e5a4df..1edb2d7 100644 --- a/app/src/main/java/com/unibuy/smartdevice/service/MqttService.java +++ b/app/src/main/java/com/unibuy/smartdevice/service/MqttService.java @@ -296,6 +296,11 @@ public class MqttService extends Service { .publishCommandAck(new com.unibuy.smartdevice.structure.mqtt.CommandAckPayload( cmdId, "success", "Inventory updated successfully", String.valueOf(stock))); + + // ✅ 通知前台銷售平台刷新商品列表(輕量級,不重建 Fragment) + Intent refreshIntent = new Intent("com.unibuy.smartdevice.ACTION_SLOT_UPDATED"); + refreshIntent.setPackage(getPackageName()); + sendBroadcast(refreshIntent); } else { throw new Exception("Slot not found: " + slotNo); } 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 d3ab362..a249fe8 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java @@ -5,8 +5,10 @@ import static android.view.View.VISIBLE; import android.Manifest; import android.app.AlarmManager; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.graphics.Color; @@ -128,6 +130,17 @@ public class FontendActivity extends AppCompatActivityAbstract { private Handler idleHandler = new Handler(); private Runnable idleRunnable; + // ✅ 遠端庫存更新監聽器(輕量級,只刷新 Adapter 不重建 Fragment) + private final BroadcastReceiver slotUpdateReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + getLogs().info("收到遠端庫存更新通知,刷新銷售平台商品列表..."); + if (vmcFragment instanceof VmcFragment) { + ((VmcFragment) vmcFragment).updateFlowerBuyList(); + } + } + }; + private static VideoEoxPlayer videoEoxPlayer; @Override @@ -1043,6 +1056,13 @@ public class FontendActivity extends AppCompatActivityAbstract { protected void onResume() { super.onResume(); resetIdleTimer(); + // ✅ 註冊庫存更新監聽 + IntentFilter filter = new IntentFilter("com.unibuy.smartdevice.ACTION_SLOT_UPDATED"); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + registerReceiver(slotUpdateReceiver, filter, Context.RECEIVER_NOT_EXPORTED); + } else { + registerReceiver(slotUpdateReceiver, filter); + } } @Override @@ -1051,6 +1071,12 @@ public class FontendActivity extends AppCompatActivityAbstract { if (idleHandler != null) { idleHandler.removeCallbacks(idleRunnable); } + // ✅ 取消註冊庫存更新監聽 + try { + unregisterReceiver(slotUpdateReceiver); + } catch (Exception e) { + getLogs().info("unregisterReceiver slotUpdateReceiver: " + e.getMessage()); + } } @Override diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/devs/vmc/RecyclerVmcBuyListAdpter.java b/app/src/main/java/com/unibuy/smartdevice/ui/devs/vmc/RecyclerVmcBuyListAdpter.java index b3fe2d7..4b3e01e 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/devs/vmc/RecyclerVmcBuyListAdpter.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/devs/vmc/RecyclerVmcBuyListAdpter.java @@ -150,6 +150,21 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter dispatchList; private int dispatchIndex; private boolean isReportDispatch; + private boolean isFinalizing = false; // 標記是否正在進行最後的取貨倒數結案 private boolean isChangeStatus; private int countdownNumber = 40; private CountDownTimer countDownTimer; @@ -293,9 +301,19 @@ public class DispatchDialog extends DialogAbstract { this.binding.recyclerDialogDispatchList.setLayoutManager(new LinearLayoutManager(context)); this.binding.buttonOk.setOnClickListener(v -> { - currentlyProcessingGoods(); + if (isFinalizing) { + // 結案倒數中,按下 = 提前確認取貨,關閉 dialog + if (countDownTimer != null) { + countDownTimer.cancel(); + } + dialogCancel(); + } else { + currentlyProcessingGoods(); + } }); + // 出貨過程中隱藏按鈕,等到 15 秒取貨倒數再顯示 + binding.buttonOk.setVisibility(View.GONE); setButtonOkText(0); closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain()); @@ -440,10 +458,11 @@ public class DispatchDialog extends DialogAbstract { checkSlotRunCount = 0; MyApp.getInstance().getDevXinYuanController().start(); } else { - getLogs().info("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA004"); -// MyApp.getInstance().getDevXinYuanController().getReadBufferByNow(new DevCheckSlotByVMC(handlerMain, slot)); -// MyApp.getInstance().getDevXinYuanController().addSendBuffer(MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlot(slot)); + getLogs().info("機台未回應,第 " + (checkSlotRunCount + 1) + " 次重試 checkSlot..."); + isCheckSlotByVMC = false; // 重置旗標,允許下次回調進入 checkSlotRunCount++; + MyApp.getInstance().getDevXinYuanController().getReadBufferByNow(new DevCheckSlotByVMC(handlerMain, slot)); + MyApp.getInstance().getDevXinYuanController().addSendBuffer(MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlot(slot)); } } } @@ -616,7 +635,7 @@ public class DispatchDialog extends DialogAbstract { getLogs().warning(e); } - setTextViewCountdown(0); + setTextViewCountdown(getCtx().getString(R.string.preparing_to_dispatch), 65); isGetProductByVMC = false; isCheckSlotByVMC = false; checkSlotRunCount = 0; @@ -803,6 +822,9 @@ public class DispatchDialog extends DialogAbstract { //偵測是否還有下個出貨商品 if (this.dispatchIndex >= this.dispatchList.size()-1) { //沒有貨 // ✅ 所有商品出貨完畢,發送 MQTT Finalize 結案訊息 + if (isFinalizing) return; // 防呆:如果已經在結案倒數了,直接跳出 + isFinalizing = true; + getLogs().info("所有商品出貨完畢,發送 MQTT Finalize 結案訊息。"); MqttManager.getInstance().publishTransactionFinalize(finalizeBuilder.build()); getSrcHandlerMain().send(getClass().getSimpleName(), FontendActivity.Option.RESET_DATA.getOption(), "update data"); @@ -813,7 +835,40 @@ public class DispatchDialog extends DialogAbstract { getLogs().info(logItem); } - dialogCancel(); // 結束對話框 + // 停止所有計時器 + closeDialogCountdown.shutdown(); + if (!buttonControlCountdown.isShutdown()) { + buttonControlCountdown.shutdown(); + } + if (countDownTimer != null) { + countDownTimer.cancel(); + } + + // 隱藏底部倒數文字 + binding.tvCountdown.setVisibility(View.GONE); + + // 顯示取貨確認按鈕(讓使用者可以提前確認取貨) + binding.buttonOk.setVisibility(View.VISIBLE); + binding.buttonOk.setEnabled(true); + binding.buttonOk.setTextColor(Color.BLACK); + setButtonOkText(0); // 顯示「取貨確認」不帶倒數 + + // 顯示取貨倒數 15 秒後自動關閉對話框 + isChangeStatus = true; + setTextViewCountdown(getCtx().getString(R.string.pick_up_goods_within_15_seconds_after_pick_up_port_opened), 65); + countDownTimer = new CountDownTimer(15_000, 1_000) { + public void onTick(long millisUntilFinished) { + int seconds = (int) (millisUntilFinished / 1000); + setTextViewCountdown(seconds); + } + public void onFinish() { + dialogCancel(); + } + }.start(); + getLogs().info("📢 出貨完畢,取貨倒數 15 秒後關閉對話框"); + + isDispatchingNext = false; + isGetProductByVMC = false; return; // 結束方法 } else { //有貨要出了 this.dispatchIndex += 1; //表記下一個商品要出貨 diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/dialog/MemberCardPickupDialog.java b/app/src/main/java/com/unibuy/smartdevice/ui/dialog/MemberCardPickupDialog.java index f184e07..51cf466 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/dialog/MemberCardPickupDialog.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/dialog/MemberCardPickupDialog.java @@ -188,6 +188,20 @@ public class MemberCardPickupDialog extends DialogAbstract { public void onSuccess(String codeId) { getLogs().info("MemberCardPickupDialog - B680 Success, code_id: " + codeId); + // ✅ 最後一道防線:出貨前即時檢查庫存 + for (com.unibuy.smartdevice.structure.BuyStructure buy : MyApp.getInstance().getBuyList()) { + try { + com.unibuy.smartdevice.structure.SlotStructure liveSlot = + MyApp.getInstance().getSlotData(buy.getField(), buy.getSlot()); + if (liveSlot != null && liveSlot.getCount() <= 0) { + getLogs().info("庫存已歸零 slot=" + buy.getSlot() + ",取消出貨,返回銷售平台。"); + getHandlerMain().start(getClass().getSimpleName(), Option.TOAST.getOption(), "商品已售罄,請重新選擇"); + dialogCancel(); // 內部已清空 BuyList + dismiss + return; + } + } catch (Exception ignored) {} + } + // 設定支付資訊 MyApp.getInstance().getReportFlowInfoData().setFlowType(41); // 員工卡 MyApp.getInstance().getReportFlowInfoData().setReportFlowId(cardNo); diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 91e9487..6c0c0b4 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -340,6 +340,7 @@ merchant payment checkout TapPay Checkout Pick up goods within 15 seconds after the pick-up port is opened + Preparing to dispatch Enter the gift or scan the QR Code Select combination Pickup diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml index 25cc620..48d67bd 100644 --- a/app/src/main/res/values-ja/strings.xml +++ b/app/src/main/res/values-ja/strings.xml @@ -340,6 +340,7 @@ 販売者支払いチェックアウト TapPayチェックアウト ピックアップポートが開いてから15秒以内に商品を受け取る + 出荷準備中 ギフトを入力するか、QRコードをスキャンしてください 組み合わせを選択 選び出す diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1742f4f..957d467 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -250,6 +250,7 @@ 商家支付結帳 TapPay結帳 取貨口開啟15秒內取貨 + 準備出貨 輸入來店禮 或 掃描QRCode 選擇組合 取貨 @@ -300,7 +301,7 @@ 知道了 輸入載具 領取確認 - 請將員山卡靠近讀卡機 + 請將員工卡靠近讀卡機 下載商品資料 確定要從雲端下載商品資料嗎?這將會更新所有商品的品名、價格與圖片,並覆蓋目前的資訊。 正在下載商品資料...