From ab3b53da994cafb2198ba99ae9af79f26dd8ff4e Mon Sep 17 00:00:00 2001 From: sky121113 Date: Wed, 10 Jun 2026 15:28:59 +0800 Subject: [PATCH] =?UTF-8?q?[REFACTOR]=20=E6=94=B6=E6=96=82=20project=20?= =?UTF-8?q?=E7=B6=AD=E5=BA=A6=E7=82=BA=E5=9F=B7=E8=A1=8C=E6=9C=9F=20shoppi?= =?UTF-8?q?ng=5Fmode=20+=20B014=20=E9=9B=B2=E7=AB=AF=E8=A8=AD=E5=AE=9A?= =?UTF-8?q?=E5=94=AF=E8=AE=80=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. B014 雲端設定下發 → App 解析寫入 DevSet 並持久化: - StarCloudAPI.saveSettings() 解析 DevSet(14 開關)/CashSet(7 面額)/ FunctionSet(取貨碼 PickupCode、通行碼 PassCode)/ShoppingMode; 缺漏粒度區分(整個物件缺→不動本地防誤關;物件內 key 缺→視為 false)。 - MyApp 修 TapPay30~34 持久化 bug(saveSystemSet 補序列化、initialSystemSet 改 optBoolean 防舊紀錄 crash),並新增 PickupCode/PassCode/ShoppingMode 持久化。 - DevSetStructure 新增 isPickupCode/isPassCode 欄位。 - SystemSetActivity 全部 21 個 checkbox 鎖唯讀、返回鈕只 finish() 不回寫。 - 取貨碼浮鈕(FontendActivity)改吃 DevSet.isPickupCode()、通行碼入口(BuyDialog) 改吃 DevSet.isPassCode(),取代舊 flavor 資源/寫死。 2. 收掉 project flavor 維度,三種購買流程改由雲端 shopping_mode 執行期切換: - SaleFlowHandler 改為 main 抽象基底 + create() factory,依 shopping_mode 回傳 BasicSaleFlow(basic/原 General)、EmployeeCardSaleFlow(employee_card/原 Chengwai)、 PickupSheetSaleFlow(pickup_sheet/原 Cmuh) 三策略。 - 三 flavor 的 dialog/adapter/Kotlin 購物車/Cmuh 醫令類別全併入 main;重複類別去重。 - FontendPickupCodeDialog 內部依 shopping_mode 分支(basic/employee 走 B660、 pickup_sheet 走舊 memberVerification 並跳過取貨口檢查)。 - fragment_vmc 合併掃描單據按鈕,VmcFragment 依 shopping_mode toggle 可見性。 - General strings(zh/en/ja)、drawable、購物車 layout 併入 main; devset_defaults 併為 basic-mode bootstrap 預設。 3. 取代 BuildConfig.FLAVOR_project 編譯期分支: - AppEntry.isCmuh() 改判 shopping_mode == pickup_sheet。 - DispatchDialog 6 處 Cmuh 分支改用 AppEntry.isCmuh()。 - ComPort.getFilepath() 改認硬體維度 BuildConfig.FLAVOR(S12XYU→ttyS7/S6、 S9XYU→ttyS1/S2),與購買流程正交;對現役機台行為不變。 4. build.gradle 移除 project 維度,flavorDimensions 只留 hardware;輸出檔名去掉 project 段(app-S_12_XY_U--debug.apk)。刪除 app/src/{General,Chengwai,Cmuh}/。 5. compiler-apk / release-apk(workflow + .claude/commands)改以硬體規格 S9/S12 為 打包對象,OTA keyin 改依硬體分版並提示 shopping_mode 部署鐵律。 6. verPatch 69→70(供 OTA 遠端更新)。 雙硬體 clean 編譯回歸 S12XYU/S9XYU 全綠。實機三流程驗證待硬體。 Co-Authored-By: Claude Opus 4.8 --- .agents/workflows/compiler-apk.md | 171 ++--- .agents/workflows/release-apk.md | 99 ++- .claude/commands/compiler-apk.md | 15 +- .claude/commands/release-apk.md | 13 +- app/build.gradle | 29 +- .../smartdevice/ui/SaleFlowHandler.java | 57 -- .../ui/dialog/FontendPickupCodeDialog.java | 591 ------------------ .../ui/dialog/BuyPickupCodeDialog.java | 263 -------- .../ui/dialog/FontendPickupCodeDialog.java | 429 ------------- ...RecyclerDialogPickupCodeBuyListAdpter.java | 193 ------ app/src/Cmuh/res/layout/activity_fontend.xml | 271 -------- .../res/layout/dialog_pickup_code_buy.xml | 104 --- .../res/layout/dialog_pickup_code_fontend.xml | 194 ------ app/src/Cmuh/res/layout/fragment_vmc.xml | 39 -- .../recycler_dialog_pickup_code_buy_list.xml | 109 ---- .../ui/dialog/BuyPickupCodeDialog.java | 264 -------- ...RecyclerDialogPickupCodeBuyListAdpter.java | 194 ------ .../res/layout/dialog_checkout_cash_pay.xml | 168 ----- app/src/General/res/values-en/strings.xml | 16 - app/src/General/res/values-ja/strings.xml | 16 - .../General/res/values/devset_defaults.xml | 13 - app/src/General/res/values/strings.xml | 16 - .../java/com/unibuy/smartdevice/AppEntry.java | 7 +- .../java/com/unibuy/smartdevice/MyApp.java | 32 +- .../unibuy/smartdevice/devices/ComPort.java | 12 +- .../smartdevice/external/StarCloudAPI.java | 49 +- .../structure/DevSetStructure.java | 24 +- .../structure/MedicineStructure.java | 0 .../unibuy/smartdevice/ui/BasicSaleFlow.java} | 37 +- .../smartdevice/ui/EmployeeCardSaleFlow.java | 23 + .../smartdevice/ui/FontendActivity.java | 7 +- .../smartdevice/ui/PickupSheetSaleFlow.java} | 58 +- .../smartdevice/ui/SaleFlowHandler.java | 79 +++ .../smartdevice/ui/SystemSetActivity.java | 33 +- .../smartdevice/ui/devs/vmc/VmcFragment.java | 5 + .../smartdevice/ui/dialog/BuyDialog.java | 5 + .../ui/dialog/BuyPickupCodeDialog.java | 0 .../ui/dialog/CashPayDialogGeneral.java | 0 .../smartdevice/ui/dialog/DispatchDialog.java | 12 +- .../ui/dialog/FontendPickupCodeDialog.java | 29 +- .../ui/dialog/PickupErrorTipDialog.java | 0 .../ui/dialog/TakeMedicineDialog.java | 0 ...RecyclerDialogPickupCodeBuyListAdpter.java | 0 .../RecyclerDialogTakeMedicineListAdpter.java | 0 .../java/com/unibuy/v2/UnibuyHelper.kt | 0 .../unibuy/v2/adapter/CartOperationAdapter.kt | 0 .../com/unibuy/v2/bean/CartOperationResult.kt | 0 .../unibuy/v2/ui/ShoppingCartFragment.java | 0 .../com/unibuy/v2/utils/SafeCountdownUtil.kt | 0 .../java/com/unibuy/v2/vm/CartViewModel.kt | 0 .../res/drawable/ic_minus.png | Bin .../res/drawable/ic_plus.png | Bin .../res/drawable/ic_waste.png | Bin .../res/layout/dialog_checkout_cash_pay.xml | 11 + .../res/layout/dialog_cmuh_take_medicine.xml | 0 .../res/layout/dialog_pickup_error_tip.xml | 0 .../res/layout/fragment_shopping_cart.xml | 0 app/src/main/res/layout/fragment_vmc.xml | 25 + .../res/layout/recycler_cart_operation.xml | 0 .../recycler_dialog_take_medicine_list.xml | 0 app/src/main/res/values-en/strings.xml | 12 + app/src/main/res/values-ja/strings.xml | 12 + app/src/main/res/values/devset_defaults.xml | 17 +- app/src/main/res/values/strings.xml | 12 + 64 files changed, 519 insertions(+), 3246 deletions(-) delete mode 100644 app/src/Chengwai/java/com/unibuy/smartdevice/ui/SaleFlowHandler.java delete mode 100644 app/src/Chengwai/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java delete mode 100644 app/src/Cmuh/java/com/unibuy/smartdevice/ui/dialog/BuyPickupCodeDialog.java delete mode 100644 app/src/Cmuh/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java delete mode 100644 app/src/Cmuh/java/com/unibuy/smartdevice/ui/recycler/RecyclerDialogPickupCodeBuyListAdpter.java delete mode 100644 app/src/Cmuh/res/layout/activity_fontend.xml delete mode 100644 app/src/Cmuh/res/layout/dialog_pickup_code_buy.xml delete mode 100644 app/src/Cmuh/res/layout/dialog_pickup_code_fontend.xml delete mode 100644 app/src/Cmuh/res/layout/fragment_vmc.xml delete mode 100644 app/src/Cmuh/res/layout/recycler_dialog_pickup_code_buy_list.xml delete mode 100644 app/src/General/java/com/unibuy/smartdevice/ui/dialog/BuyPickupCodeDialog.java delete mode 100644 app/src/General/java/com/unibuy/smartdevice/ui/recycler/RecyclerDialogPickupCodeBuyListAdpter.java delete mode 100644 app/src/General/res/layout/dialog_checkout_cash_pay.xml delete mode 100644 app/src/General/res/values-en/strings.xml delete mode 100644 app/src/General/res/values-ja/strings.xml delete mode 100644 app/src/General/res/values/devset_defaults.xml delete mode 100644 app/src/General/res/values/strings.xml rename app/src/{Cmuh => main}/java/com/unibuy/smartdevice/structure/MedicineStructure.java (100%) rename app/src/{General/java/com/unibuy/smartdevice/ui/SaleFlowHandler.java => main/java/com/unibuy/smartdevice/ui/BasicSaleFlow.java} (65%) create mode 100644 app/src/main/java/com/unibuy/smartdevice/ui/EmployeeCardSaleFlow.java rename app/src/{Cmuh/java/com/unibuy/smartdevice/ui/SaleFlowHandler.java => main/java/com/unibuy/smartdevice/ui/PickupSheetSaleFlow.java} (93%) create mode 100644 app/src/main/java/com/unibuy/smartdevice/ui/SaleFlowHandler.java rename app/src/{Chengwai => main}/java/com/unibuy/smartdevice/ui/dialog/BuyPickupCodeDialog.java (100%) rename app/src/{General => main}/java/com/unibuy/smartdevice/ui/dialog/CashPayDialogGeneral.java (100%) rename app/src/{General => main}/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java (94%) rename app/src/{Cmuh => main}/java/com/unibuy/smartdevice/ui/dialog/PickupErrorTipDialog.java (100%) rename app/src/{Cmuh => main}/java/com/unibuy/smartdevice/ui/dialog/TakeMedicineDialog.java (100%) rename app/src/{Chengwai => main}/java/com/unibuy/smartdevice/ui/recycler/RecyclerDialogPickupCodeBuyListAdpter.java (100%) rename app/src/{Cmuh => main}/java/com/unibuy/smartdevice/ui/recycler/RecyclerDialogTakeMedicineListAdpter.java (100%) rename app/src/{General => main}/java/com/unibuy/v2/UnibuyHelper.kt (100%) rename app/src/{General => main}/java/com/unibuy/v2/adapter/CartOperationAdapter.kt (100%) rename app/src/{General => main}/java/com/unibuy/v2/bean/CartOperationResult.kt (100%) rename app/src/{General => main}/java/com/unibuy/v2/ui/ShoppingCartFragment.java (100%) rename app/src/{General => main}/java/com/unibuy/v2/utils/SafeCountdownUtil.kt (100%) rename app/src/{General => main}/java/com/unibuy/v2/vm/CartViewModel.kt (100%) rename app/src/{General => main}/res/drawable/ic_minus.png (100%) rename app/src/{General => main}/res/drawable/ic_plus.png (100%) rename app/src/{General => main}/res/drawable/ic_waste.png (100%) rename app/src/{Cmuh => main}/res/layout/dialog_cmuh_take_medicine.xml (100%) rename app/src/{Cmuh => main}/res/layout/dialog_pickup_error_tip.xml (100%) rename app/src/{General => main}/res/layout/fragment_shopping_cart.xml (100%) rename app/src/{General => main}/res/layout/recycler_cart_operation.xml (100%) rename app/src/{Cmuh => main}/res/layout/recycler_dialog_take_medicine_list.xml (100%) diff --git a/.agents/workflows/compiler-apk.md b/.agents/workflows/compiler-apk.md index 1175cce..32bf937 100644 --- a/.agents/workflows/compiler-apk.md +++ b/.agents/workflows/compiler-apk.md @@ -4,13 +4,19 @@ description: 自動編譯 Android APK、輸出至專屬目錄並安裝至實體 # 編譯與安裝 APK 工作流 (compiler-apk) -這個 Workflow 主要是協助您一鍵完成 Android 專案的編譯、匯出與安裝。當您呼叫 `/compiler-apk` 時,AI 會自動為您執行以下步驟: +這個 Workflow 協助您一鍵完成 Android 專案的編譯、匯出與安裝。當您呼叫 `/compiler-apk` 時,AI 會自動執行下列步驟。 + +> [!IMPORTANT] +> **客戶專案(購買流程)已收斂為執行期 `shopping_mode`(雲端 B014 下發),不再用 flavor 分家。** +> 打包對象只剩 **硬體規格(hardware flavor)**:`S12XYU`(10 吋)/ `S9XYU`(21.5 吋 / Android 9)。 +> 同一支 APK 在現場會依雲端 `shopping_mode` 自動切換為 basic(購物車)/ employee_card(員工卡)/ pickup_sheet(取貨單)。 > [!TIP] -> **支援智慧硬體判斷與參數化覆寫:** -> * 預設(不帶參數):編譯 **`Chengwai` 晟崴版 (S12XYU)**。 -> * **智慧判斷**:當編譯 **`中國醫 (Cmuh)`** 時,會自動識別並預設編譯 **`S9XYU` (Android 9 / 21.5 吋)**;編譯 **`General`** 與 **`Chengwai`** 時則預設編譯 **`S12XYU` (10 吋)**。 -> * **參數手動覆寫**:支援手動指定硬體尺寸。例如 `/compiler-apk Cmuh S12` 可強行編譯中國醫的 10 吋版本;`/compiler-apk General S9` 可強行編譯通用版的大螢幕 21.5 吋版本。 +> **參數 = 硬體規格:** +> * 預設(不帶參數):編譯 **`S12XYU`(10 吋)**。 +> * `/compiler-apk S9`(或 `S9XYU` / `21.5` / `大螢幕`)→ 編譯 **`S9XYU`(21.5 吋 / Android 9)**。 +> * `/compiler-apk S12`(或 `S12XYU` / `10`)→ 編譯 **`S12XYU`(10 吋)**。 +> * 相容舊用語:`中國醫`→S9XYU、`晟崴`/`通用`→S12XYU(僅作硬體對應,流程仍由雲端決定)。 ## 步驟零:自動檢查變更與升級版號 在開始編譯之前,AI 助手會自動檢查是否有程式碼變更,以落實實體機台的 OTA 升級規範: @@ -19,7 +25,7 @@ description: 自動編譯 Android APK、輸出至專屬目錄並安裝至實體 3. 遞增完成後,向使用者呈報最新的版本號資訊,接著繼續執行步驟一。 ## 步驟一:設定環境並編譯 Debug APK -使用 Java 17 與 Gradle 編譯對應 Flavor 與硬體規格的 Debug 程式碼。 +使用 Java 17 與 Gradle 編譯對應硬體規格的 Debug 程式碼。 ```bash // turbo @@ -39,50 +45,22 @@ fi export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai -# 取得目標 Flavor 參數 +# 取得目標硬體規格(預設 S12XYU;接受 S9/S12 與相容舊用語) RAW_INPUT="$1" -TARGET_FLAVOR="Chengwai" - -if [ -n "$RAW_INPUT" ]; then - LOWER_INPUT=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]') - if [ "$LOWER_INPUT" = "general" ] || [ "$LOWER_INPUT" = "通用" ] || [ "$LOWER_INPUT" = "通用版" ] || [ "$LOWER_INPUT" = "標準" ] || [ "$LOWER_INPUT" = "標準版" ]; then - TARGET_FLAVOR="General" - elif [ "$LOWER_INPUT" = "chengwai" ] || [ "$LOWER_INPUT" = "晟崴" ] || [ "$LOWER_INPUT" = "晟崴版" ]; then - TARGET_FLAVOR="Chengwai" - elif [ "$LOWER_INPUT" = "cmuh" ] || [ "$LOWER_INPUT" = "中國醫" ] || [ "$LOWER_INPUT" = "中國醫版" ]; then - TARGET_FLAVOR="Cmuh" - else - TARGET_FLAVOR=$(echo "$RAW_INPUT" | awk '{print toupper(substr($0,1,1))tolower(substr($0,2))}') - fi -fi - -# 根據 Flavor 動態設定智慧硬體規格預設值 TARGET_HARDWARE="S12XYU" -if [ "$TARGET_FLAVOR" = "Cmuh" ]; then - TARGET_HARDWARE="S9XYU" +if [ -n "$RAW_INPUT" ]; then + LOWER=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]') + case "$LOWER" in + s9|s9xyu|21.5|大螢幕|中國醫|中國醫版|cmuh) TARGET_HARDWARE="S9XYU" ;; + s12|s12xyu|10|晟崴|晟崴版|chengwai|通用|通用版|general) TARGET_HARDWARE="S12XYU" ;; + *) echo "⚠️ 無法識別「$RAW_INPUT」,沿用預設 S12XYU" ;; + esac fi -# 取得手動指定的第二個硬體規格參數 (S9 / S12) -RAW_HW_INPUT="$2" -if [ -n "$RAW_HW_INPUT" ]; then - LOWER_HW=$(echo "$RAW_HW_INPUT" | tr '[:upper:]' '[:lower:]') - if [ "$LOWER_HW" = "s9" ] || [ "$LOWER_HW" = "s9xyu" ] || [ "$LOWER_HW" = "大螢幕" ] || [ "$LOWER_HW" = "21.5" ]; then - TARGET_HARDWARE="S9XYU" - elif [ "$LOWER_HW" = "s12" ] || [ "$LOWER_HW" = "s12xyu" ] || [ "$LOWER_HW" = "10" ]; then - TARGET_HARDWARE="S12XYU" - fi -fi - -# 安全防護:檢查是否為支援的客製項目 -if [ "$TARGET_FLAVOR" != "General" ] && [ "$TARGET_FLAVOR" != "Chengwai" ] && [ "$TARGET_FLAVOR" != "Cmuh" ]; then - echo "❌ 不支援的 Flavor: $TARGET_FLAVOR,請使用 General, Chengwai 或 Cmuh。" - exit 1 -fi - -echo "🚀 開始編譯: ${TARGET_FLAVOR}${TARGET_HARDWARE}Debug" +echo "🚀 開始編譯: assemble${TARGET_HARDWARE}Debug" # 使用 clean 確保完整重新編譯,避免舊快取遮蔽 import 缺失等錯誤 -./gradlew clean :app:assemble${TARGET_FLAVOR}${TARGET_HARDWARE}Debug --no-daemon +./gradlew clean :app:assemble${TARGET_HARDWARE}Debug --no-daemon ``` ## 步驟二:導出 APK 到 outputs 資料夾 @@ -93,80 +71,44 @@ echo "🚀 開始編譯: ${TARGET_FLAVOR}${TARGET_HARDWARE}Debug" cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai mkdir -p outputs -# 取得 Flavor 參數 RAW_INPUT="$1" -TARGET_FLAVOR="Chengwai" - -if [ -n "$RAW_INPUT" ]; then - LOWER_INPUT=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]') - if [ "$LOWER_INPUT" = "general" ] || [ "$LOWER_INPUT" = "通用" ] || [ "$LOWER_INPUT" = "通用版" ] || [ "$LOWER_INPUT" = "標準" ] || [ "$LOWER_INPUT" = "標準版" ]; then - TARGET_FLAVOR="General" - elif [ "$LOWER_INPUT" = "chengwai" ] || [ "$LOWER_INPUT" = "晟崴" ] || [ "$LOWER_INPUT" = "晟崴版" ]; then - TARGET_FLAVOR="Chengwai" - elif [ "$LOWER_INPUT" = "cmuh" ] || [ "$LOWER_INPUT" = "中國醫" ] || [ "$LOWER_INPUT" = "中國醫版" ]; then - TARGET_FLAVOR="Cmuh" - else - TARGET_FLAVOR=$(echo "$RAW_INPUT" | awk '{print toupper(substr($0,1,1))tolower(substr($0,2))}') - fi -fi - -# 根據 Flavor 動態設定智慧硬體規格預設值 TARGET_HARDWARE="S12XYU" -if [ "$TARGET_FLAVOR" = "Cmuh" ]; then - TARGET_HARDWARE="S9XYU" +if [ -n "$RAW_INPUT" ]; then + LOWER=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]') + case "$LOWER" in + s9|s9xyu|21.5|大螢幕|中國醫|中國醫版|cmuh) TARGET_HARDWARE="S9XYU" ;; + s12|s12xyu|10|晟崴|晟崴版|chengwai|通用|通用版|general) TARGET_HARDWARE="S12XYU" ;; + esac fi -# 取得手動指定的第二個硬體規格參數 (S9 / S12) -RAW_HW_INPUT="$2" -if [ -n "$RAW_HW_INPUT" ]; then - LOWER_HW=$(echo "$RAW_HW_INPUT" | tr '[:upper:]' '[:lower:]') - if [ "$LOWER_HW" = "s9" ] || [ "$LOWER_HW" = "s9xyu" ] || [ "$LOWER_HW" = "大螢幕" ] || [ "$LOWER_HW" = "21.5" ]; then - TARGET_HARDWARE="S9XYU" - elif [ "$LOWER_HW" = "s12" ] || [ "$LOWER_HW" = "s12xyu" ] || [ "$LOWER_HW" = "10" ]; then - TARGET_HARDWARE="S12XYU" - fi -fi - -# 解析硬體規格來決定輸出檔案名格式 (S9 -> S_9, S12 -> S_12) -FORMATTED_HW="" if [ "$TARGET_HARDWARE" = "S9XYU" ]; then - FORMATTED_HW="S_9_XY_U" + FORMATTED_HW="S_9_XY_U"; MAJOR=21 else - FORMATTED_HW="S_12_XY_U" + FORMATTED_HW="S_12_XY_U"; MAJOR=10 fi -# 動態尋找編譯產出的最新 APK 檔案 -APK_SOURCE=$(find app/build/outputs/apk/${TARGET_FLAVOR}${TARGET_HARDWARE}/debug/ -name "app-${FORMATTED_HW}_${TARGET_FLAVOR}-*-debug.apk" | head -n 1) +# 動態尋找編譯產出的最新 APK 檔案(檔名已無 project 段:app---debug.apk) +APK_SOURCE=$(find app/build/outputs/apk/${TARGET_HARDWARE}/debug/ -name "app-${FORMATTED_HW}-*-debug.apk" | head -n 1) if [ -n "$APK_SOURCE" ]; then APK_NAME=$(basename "$APK_SOURCE") APK_DEST="outputs/$APK_NAME" cp "$APK_SOURCE" "$APK_DEST" echo "✅ APK 已經複製到 outputs 資料夾,路徑為:$APK_DEST" - - # 提取與計算版本資訊 - MAJOR=$(grep "def verMajor" app/build.gradle | awk '{print $4}') - - # 若在 build.gradle 內 Major 是由 hardware 動態判定,直接讀取並在 Shell 依據當前規格計算 - if [ "$TARGET_HARDWARE" = "S9XYU" ]; then - MAJOR=21 - else - MAJOR=10 - fi - + MINOR=$(grep "def verMinor" app/build.gradle | awk '{print $4}') PATCH=$(grep "def verPatch" app/build.gradle | awk '{print $4}') VERSION_NAME="${MAJOR}_0${MINOR}_${PATCH}_R" VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH)) - FLAVOR="${TARGET_FLAVOR}${TARGET_HARDWARE}" - + echo "==================================================" echo "📢 後台更新 keyin 參考資料:" echo "--------------------------------------------------" - echo "* 產品風味 (Flavor):$FLAVOR" + echo "* 硬體規格 (hardware):$TARGET_HARDWARE(${MAJOR} 吋)" echo "* 版本名稱 (versionName):$VERSION_NAME" echo "* 版本代碼 (versionCode):$VERSION_CODE" echo "* APK 檔案名稱:$APK_NAME" + echo "* 購買流程:由雲端 shopping_mode 決定(basic / employee_card / pickup_sheet)" echo "* 更新說明:[請在此處附上口語化的一句話更新說明]" echo "==================================================" else @@ -182,54 +124,27 @@ fi // turbo ADB_PATH="/mnt/c/Users/User/AppData/Local/Android/Sdk/platform-tools/adb.exe" -# 取得 Flavor 參數 RAW_INPUT="$1" -TARGET_FLAVOR="Chengwai" - -if [ -n "$RAW_INPUT" ]; then - LOWER_INPUT=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]') - if [ "$LOWER_INPUT" = "general" ] || [ "$LOWER_INPUT" = "通用" ] || [ "$LOWER_INPUT" = "通用版" ] || [ "$LOWER_INPUT" = "標準" ] || [ "$LOWER_INPUT" = "標準版" ]; then - TARGET_FLAVOR="General" - elif [ "$LOWER_INPUT" = "chengwai" ] || [ "$LOWER_INPUT" = "晟崴" ] || [ "$LOWER_INPUT" = "晟崴版" ]; then - TARGET_FLAVOR="Chengwai" - elif [ "$LOWER_INPUT" = "cmuh" ] || [ "$LOWER_INPUT" = "中國醫" ] || [ "$LOWER_INPUT" = "中國醫版" ]; then - TARGET_FLAVOR="Cmuh" - else - TARGET_FLAVOR=$(echo "$RAW_INPUT" | awk '{print toupper(substr($0,1,1))tolower(substr($0,2))}') - fi -fi - -# 根據 Flavor 動態設定智慧硬體規格預設值 TARGET_HARDWARE="S12XYU" -if [ "$TARGET_FLAVOR" = "Cmuh" ]; then - TARGET_HARDWARE="S9XYU" +if [ -n "$RAW_INPUT" ]; then + LOWER=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]') + case "$LOWER" in + s9|s9xyu|21.5|大螢幕|中國醫|中國醫版|cmuh) TARGET_HARDWARE="S9XYU" ;; + s12|s12xyu|10|晟崴|晟崴版|chengwai|通用|通用版|general) TARGET_HARDWARE="S12XYU" ;; + esac fi -# 取得手動指定的第二個硬體規格參數 (S9 / S12) -RAW_HW_INPUT="$2" -if [ -n "$RAW_HW_INPUT" ]; then - LOWER_HW=$(echo "$RAW_HW_INPUT" | tr '[:upper:]' '[:lower:]') - if [ "$LOWER_HW" = "s9" ] || [ "$LOWER_HW" = "s9xyu" ] || [ "$LOWER_HW" = "大螢幕" ] || [ "$LOWER_HW" = "21.5" ]; then - TARGET_HARDWARE="S9XYU" - elif [ "$LOWER_HW" = "s12" ] || [ "$LOWER_HW" = "s12xyu" ] || [ "$LOWER_HW" = "10" ]; then - TARGET_HARDWARE="S12XYU" - fi -fi - -# 解析硬體規格來決定輸出檔案名格式 (S9 -> S_9, S12 -> S_12) -FORMATTED_HW="" if [ "$TARGET_HARDWARE" = "S9XYU" ]; then FORMATTED_HW="S_9_XY_U" else FORMATTED_HW="S_12_XY_U" fi -# 檢查是否有設備連接 DEVICE_COUNT=$($ADB_PATH devices | grep -v "List of devices" | grep "device" | wc -l) if [ "$DEVICE_COUNT" -gt 0 ]; then echo "📱 偵測到 $DEVICE_COUNT 個設備,開始安裝..." - APK_TARGET=$(find outputs/ -name "app-${FORMATTED_HW}_${TARGET_FLAVOR}-*-debug.apk" | head -n 1) + APK_TARGET=$(find outputs/ -name "app-${FORMATTED_HW}-*-debug.apk" | head -n 1) if [ -n "$APK_TARGET" ]; then $ADB_PATH install -r -d -t "/home/mama/projects/TaiwanStar_general_size_s_Chengwai/$APK_TARGET" echo "🚀 安裝完成,正在啟動 App..." diff --git a/.agents/workflows/release-apk.md b/.agents/workflows/release-apk.md index 7dd50b3..04714bd 100644 --- a/.agents/workflows/release-apk.md +++ b/.agents/workflows/release-apk.md @@ -4,19 +4,26 @@ description: 自動編譯正式發行 Release APK、升級 Patch 版本號、並 # 生產環境發版打包工作流 (release-apk) -這個 Workflow 專門為應用程式「生產環境上線/發版」設計。當您準備好將特定客戶的最新功能派送至實體販賣機時,呼叫 `/release-apk` 將自動為您完成嚴格的發行檢查、版號遞增、Release 簽章打包與發版資訊生成。 +這個 Workflow 專門為應用程式「生產環境上線/發版」設計。呼叫 `/release-apk` 將自動完成嚴格的發行檢查、版號遞增、Release 簽章打包與發版資訊生成。 + +> [!IMPORTANT] +> **客戶專案(購買流程)已收斂為執行期 `shopping_mode`(雲端 B014 下發),不再用 flavor 分家。** +> 發版對象只剩 **硬體規格(hardware flavor)**:`S12XYU`(10 吋)/ `S9XYU`(21.5 吋 / Android 9)。 +> 同一支 APK 上線後,現場機台由雲端 `shopping_mode` 決定購買流程。 +> **部署鐵律**:發布統一版前,star-cloud 必須先為每台現役機設好 `shopping_mode` +> (晟崴=employee_card、中國醫=pickup_sheet),否則預設 basic 會跑錯流程。 > [!TIP] -> **支援參數化指定編譯變體:** -> * 預設(不帶參數):打包 **`Chengwai` (晟崴客製正式版)**。 -> * 您可以呼叫 `/release-apk General`(或 `general`)打包 **`General` (通用正式版)**。 -> * 您可以呼叫 `/release-apk Cmuh`(或 `cmuh`)打包 **`Cmuh` (中國醫客製正式版)**。 +> **參數 = 硬體規格:** +> * 預設(不帶參數):打包 **`S12XYU`(10 吋)**。 +> * `/release-apk S9`(或 `S9XYU` / `中國醫`)→ 打包 **`S9XYU`(21.5 吋)**。 +> * `/release-apk S12`(或 `S12XYU` / `晟崴` / `通用`)→ 打包 **`S12XYU`(10 吋)**。 --- ## 步驟零:發行檢查與版號自動遞增 (OTA 鐵律) 為了確保實體機台能夠順利觸發 OTA 自動下載覆蓋安裝,新編譯的 `versionCode` 必須嚴格大於現場的舊版號: -1. AI 助手將讀取 `app/build.gradle` 並自動將 `verPatch`(修補版號)**遞增 1**(例如從 `8` 自動升級為 `9`),確保 versionCode 順利變大。 +1. AI 助手將讀取 `app/build.gradle` 並自動將 `verPatch`(修補版號)**遞增 1**,確保 versionCode 順利變大。 2. 自動在 terminal 呈報即將打包的最新版本號資訊。 ## 步驟一:清理快取並進行正式簽章編譯 @@ -40,28 +47,22 @@ fi export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai -# 取得目標 Flavor 參數(預設為 Chengwai,支援 General, Chengwai, Cmuh) -TARGET_FLAVOR="Chengwai" -if [ -n "$1" ]; then - TARGET_FLAVOR=$(echo "$1" | awk '{print toupper(substr($0,1,1))tolower(substr($0,2))}') +# 取得目標硬體規格(預設 S12XYU) +RAW_INPUT="$1" +TARGET_HARDWARE="S12XYU" +if [ -n "$RAW_INPUT" ]; then + LOWER=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]') + case "$LOWER" in + s9|s9xyu|21.5|大螢幕|中國醫|中國醫版|cmuh) TARGET_HARDWARE="S9XYU" ;; + s12|s12xyu|10|晟崴|晟崴版|chengwai|通用|通用版|general) TARGET_HARDWARE="S12XYU" ;; + *) echo "⚠️ 無法識別「$RAW_INPUT」,沿用預設 S12XYU" ;; + esac fi -# 安全防護:檢查是否為支援的客製項目 -if [ "$TARGET_FLAVOR" != "General" ] && [ "$TARGET_FLAVOR" != "Chengwai" ] && [ "$TARGET_FLAVOR" != "Cmuh" ]; then - echo "❌ 不支援的 Flavor: $TARGET_FLAVOR,請使用 General, Chengwai 或 Cmuh。" - exit 1 -fi - -# 根據專案 Flavor 判定硬體 Flavor(中國醫 Cmuh 使用 S9XYU,其餘使用 S12XYU) -HW_FLAVOR="S12XYU" -if [ "$TARGET_FLAVOR" = "Cmuh" ]; then - HW_FLAVOR="S9XYU" -fi - -echo "📦 [Release] 開始編譯正式版變體: ${TARGET_FLAVOR}${HW_FLAVOR}Release" +echo "📦 [Release] 開始編譯正式版變體: assemble${TARGET_HARDWARE}Release" # 使用 clean 確保完整重新編譯,避開 incremental 快取問題 -./gradlew clean :app:assemble${TARGET_FLAVOR}${HW_FLAVOR}Release --no-daemon +./gradlew clean :app:assemble${TARGET_HARDWARE}Release --no-daemon ``` ## 步驟二:導出正式 APK 並產出後台 Keyin 派送參考資料 @@ -72,19 +73,24 @@ echo "📦 [Release] 開始編譯正式版變體: ${TARGET_FLAVOR}${HW_FLAVOR}Re cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai mkdir -p outputs -# 取得 Flavor 參數 -TARGET_FLAVOR="Chengwai" -if [ -n "$1" ]; then - TARGET_FLAVOR=$(echo "$1" | awk '{print toupper(substr($0,1,1))tolower(substr($0,2))}') +RAW_INPUT="$1" +TARGET_HARDWARE="S12XYU" +if [ -n "$RAW_INPUT" ]; then + LOWER=$(echo "$RAW_INPUT" | tr '[:upper:]' '[:lower:]') + case "$LOWER" in + s9|s9xyu|21.5|大螢幕|中國醫|中國醫版|cmuh) TARGET_HARDWARE="S9XYU" ;; + s12|s12xyu|10|晟崴|晟崴版|chengwai|通用|通用版|general) TARGET_HARDWARE="S12XYU" ;; + esac fi -HW_FLAVOR="S12XYU" -if [ "$TARGET_FLAVOR" = "Cmuh" ]; then - HW_FLAVOR="S9XYU" +if [ "$TARGET_HARDWARE" = "S9XYU" ]; then + FORMATTED_HW="S_9_XY_U"; MAJOR=21 +else + FORMATTED_HW="S_12_XY_U"; MAJOR=10 fi -# 動態尋找編譯產出的最新正式版 APK 檔案 -APK_SOURCE=$(find app/build/outputs/apk/${TARGET_FLAVOR}${HW_FLAVOR}/release/ -name "app-S_*_${TARGET_FLAVOR}-*-release.apk" | head -n 1) +# 動態尋找編譯產出的最新正式版 APK 檔案(檔名已無 project 段) +APK_SOURCE=$(find app/build/outputs/apk/${TARGET_HARDWARE}/release/ -name "app-${FORMATTED_HW}-*-release.apk" | head -n 1) if [ -n "$APK_SOURCE" ]; then APK_NAME=$(basename "$APK_SOURCE") @@ -93,31 +99,21 @@ if [ -n "$APK_SOURCE" ]; then echo "==================================================" echo "✅ 正式版 APK 封裝成功,已導出至:$APK_DEST" echo "==================================================" - - # 提取與計算版本資訊 + MINOR=$(grep "def verMinor" app/build.gradle | awk '{print $4}') PATCH=$(grep "def verPatch" app/build.gradle | awk '{print $4}') - - # 根據硬體規格決定 Major (S9XYU 為 21 吋,S12XYU 為 10 吋) - if [ "$HW_FLAVOR" = "S9XYU" ]; then - MAJOR=21 - else - MAJOR=10 - fi - VERSION_NAME="${MAJOR}_0${MINOR}_${PATCH}_R" VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH)) - FLAVOR="${TARGET_FLAVOR}${HW_FLAVOR}" - + echo "🚀 實體販賣機 OTA 後台新增版本 Keyin 參考資料:" echo "--------------------------------------------------" - echo "1. 專案/客戶風味 (Flavor) : $FLAVOR" + echo "1. 硬體規格 (hardware) : $TARGET_HARDWARE(${MAJOR} 吋)" echo "2. 版本代碼 (versionCode) : $VERSION_CODE" echo "3. 版本名稱 (versionName) : $VERSION_NAME" - echo "4. 螢幕吋數 (Screen Size) : ${MAJOR} 吋" - echo "5. 正式版 APK 檔名 : $APK_NAME" + echo "4. 正式版 APK 檔名 : $APK_NAME" + echo "5. 購買流程 : 由雲端 shopping_mode 決定(須先於後台設好)" echo "--------------------------------------------------" - echo "📢 [溫馨提醒] 請將此 APK 上傳至 OTA 平台,並設定為對應機型的強迫升級。" + echo "📢 [溫馨提醒] 請將此 APK 上傳至 OTA 平台(依硬體分版),並確認該機 shopping_mode 已設定。" echo "==================================================" else echo "❌ 找不到編譯產出的正式版 APK 檔案,封裝失敗!" @@ -126,14 +122,13 @@ fi ``` ## 步驟三:自動化 Git 發版標記引導 -為了落實公司發版規範第 5 條「必須在主分支合併節點打上 Git Tag,以利日後追溯特定現場機台的版本程式碼」,AI 助手會自動為您計算出對應的版本 Tag 標籤與提示: +為了落實公司發版規範第 5 條「必須在主分支合併節點打上 Git Tag,以利日後追溯特定現場機台的版本程式碼」,AI 助手會自動計算出對應的版本 Tag 標籤與提示: ```bash // turbo -MAJOR=$(grep "def verMajor" app/build.gradle | awk '{print $4}') MINOR=$(grep "def verMinor" app/build.gradle | awk '{print $4}') PATCH=$(grep "def verPatch" app/build.gradle | awk '{print $4}') -TAG_NAME="v${MAJOR}.${MINOR}.${PATCH}" +TAG_NAME="v1.${MINOR}.${PATCH}" echo "==================================================" echo "🔔 Git 發版追溯標記指南" diff --git a/.claude/commands/compiler-apk.md b/.claude/commands/compiler-apk.md index 5afe7ed..0ad0de2 100644 --- a/.claude/commands/compiler-apk.md +++ b/.claude/commands/compiler-apk.md @@ -1,22 +1,25 @@ --- -description: 編譯 Debug APK、導出 outputs/、偵測到設備則安裝(晟崴/通用/中國醫,可選硬體尺寸) -argument-hint: [General|Chengwai|Cmuh] [S9|S12] +description: 編譯 Debug APK、導出 outputs/、偵測到設備則安裝(依硬體規格 S9/S12 打包;購買流程由雲端 shopping_mode 決定) +argument-hint: [S9|S12] allowed-tools: Bash, Read, Edit --- 執行本專案的 APK 編譯安裝 workflow,定義於 `.agents/workflows/compiler-apk.md`(單一真相來源)。 -本次參數:「$ARGUMENTS」(第一個=Flavor,第二個=硬體 S9/S12;可能為空) +本次參數:「$ARGUMENTS」(硬體規格:S9 / S12;可能為空,預設 S12XYU) + +> 注意:客戶專案(購買流程)已收斂為執行期 `shopping_mode`(雲端 B014 下發),打包對象只剩硬體規格。 +> 同一支 APK 在現場依雲端 `shopping_mode` 切換 basic / employee_card / pickup_sheet。 ## 執行方式 1. 先 Read `.agents/workflows/compiler-apk.md`,理解步驟零~三。 2. 步驟零(版號):執行 `git status --porcelain app/`;若有未提交改動且本輪對話尚未升過版,讀 `app/build.gradle` 把 `verPatch` 遞增 1 並回報新版號。 -3. 步驟一~三:依序執行 workflow 內每個 bash 區塊。**關鍵**:workflow 的 bash 以位置參數取得 Flavor 與硬體,所以執行每個區塊前,先在區塊最前面注入本次參數,例如: +3. 步驟一~三:依序執行 workflow 內每個 bash 區塊。**關鍵**:workflow 的 bash 以位置參數 `$1` 取得硬體規格,所以執行每個區塊前,先在區塊最前面注入本次參數: set -- $ARGUMENTS - 讓 workflow 內的位置參數正確解析(第一個=Flavor、第二個=S9/S12)。若本次無參數,workflow 自身預設 Chengwai+智慧硬體。 + 讓 workflow 內的 `$1` 正確解析(S9/S12,或相容舊用語 中國醫→S9、晟崴/通用→S12)。若本次無參數,workflow 自身預設 S12XYU。 4. 環境用 workflow 指定的 Java 17(`JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64`)。 5. 編譯/導出屬長任務 → 用 run_in_background,完成再回報。 6. 步驟三會偵測 adb 設備:有連設備才安裝並啟動 App;沒設備就保留 APK 結束。 -7. 最後完整回報 workflow 輸出的「後台 keyin 參考資料」(Flavor / versionName / versionCode / APK 檔名)。 +7. 最後完整回報 workflow 輸出的「後台 keyin 參考資料」(硬體規格 / versionName / versionCode / APK 檔名)。 diff --git a/.claude/commands/release-apk.md b/.claude/commands/release-apk.md index 77369f5..812b1ad 100644 --- a/.claude/commands/release-apk.md +++ b/.claude/commands/release-apk.md @@ -1,21 +1,24 @@ --- -description: 正式版 Release APK 打包、升 Patch 版號、產出 OTA keyin 與 Git tag 指引(晟崴/通用/中國醫) -argument-hint: [General|Chengwai|Cmuh] +description: 正式版 Release APK 打包、升 Patch 版號、產出 OTA keyin 與 Git tag 指引(依硬體 S9/S12;流程由雲端 shopping_mode 決定) +argument-hint: [S9|S12] allowed-tools: Bash, Read, Edit --- 執行本專案的正式發版 workflow,定義於 `.agents/workflows/release-apk.md`(單一真相來源)。 -本次參數:「$ARGUMENTS」(Flavor;可能為空,預設 Chengwai) +本次參數:「$ARGUMENTS」(硬體規格:S9 / S12;可能為空,預設 S12XYU) + +> 注意:客戶專案(購買流程)已收斂為執行期 `shopping_mode`(雲端 B014 下發),發版對象只剩硬體規格。 +> 部署鐵律:發布前 star-cloud 必須先為每台現役機設好 `shopping_mode`,否則預設 basic 跑錯流程。 ## 執行方式 1. 先 Read `.agents/workflows/release-apk.md`,理解步驟零~三。 2. 步驟零(OTA 鐵律):讀 `app/build.gradle` 把 `verPatch` 遞增 1(確保 versionCode 變大可觸發 OTA),回報新版號。 -3. 步驟一~三:依序執行 workflow 內每個 bash 區塊。**關鍵**:workflow 的 bash 以位置參數取得 Flavor,執行每個區塊前先注入本次參數: +3. 步驟一~三:依序執行 workflow 內每個 bash 區塊。**關鍵**:workflow 的 bash 以位置參數 `$1` 取得硬體規格,執行每個區塊前先注入本次參數: set -- $ARGUMENTS - 無參數時預設 Chengwai。硬體:Cmuh→S9XYU,其餘→S12XYU。 + 無參數時預設 S12XYU(接受 S9/S12,或相容 中國醫→S9、晟崴/通用→S12)。 4. 環境用 Java 17(`JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64`);Release 會跑混淆與 lintVital,較久 → 用 run_in_background。 5. 步驟二導出正式簽章 APK 到 `outputs/` 並印出 OTA 後台 keyin 對照資料。 6. 步驟三回報 Git 發版 tag 指引。 diff --git a/app/build.gradle b/app/build.gradle index 20a9596..454fed0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,7 +10,7 @@ plugins { // 避免不同尺寸機台間版本號產生衝突。 // ========================================================================= def verMinor = 1 -def verPatch = 69 +def verPatch = 70 @@ -30,23 +30,13 @@ android { } // ===================================================================== - // 雙維度 Flavor:project(客戶專案)× hardware(硬體規格) + // 單維度 Flavor:hardware(硬體規格 / IPC 世代) + // 客戶專案(購買流程)已收斂為執行期 shopping_mode(雲端 B014 下發),不再用 flavor 分家。 + // hardware 維度決定螢幕尺寸、minSdk(安卓版本)與 COM port(見 ComPort.getFilepath)。 // ===================================================================== - flavorDimensions "project", "hardware" + flavorDimensions "hardware" productFlavors { - General { - dimension "project" - } - - Chengwai { - dimension "project" - } - - Cmuh { - dimension "project" - } - S12XYU { dimension "hardware" } @@ -68,8 +58,7 @@ android { // 自定義打包檔案名稱與動態版本號計算 // 遵循公司命名規範:來源_安卓號_主板號_機器系列_專案名稱_螢幕大小_次版號_修補版號_環境 applicationVariants.all { variant -> - def project = variant.productFlavors[0].name // project dimension - def hardware = variant.productFlavors[1].name // hardware dimension + def hardware = variant.productFlavors[0].name // hardware dimension(唯一維度) def buildType = variant.buildType.name // 根據硬體規格自動動態判定螢幕吋數 (verMajor) 與版號 @@ -100,8 +89,8 @@ android { def hwFormatted = hardware .replaceAll(/^([A-Z])(\d+)([A-Z]+)([A-Z])$/, '$1_$2_$3_$4') - // 輸出範例:app-S_12_XY_U_Chengwai-10_08_8_R-release.apk - output.outputFileName = "app-${hwFormatted}_${project}-${calculatedVersionName}-${buildType}.apk" + // 輸出範例:app-S_12_XY_U-10_08_8_R-release.apk(依硬體分版,購買流程由雲端 shopping_mode 決定) + output.outputFileName = "app-${hwFormatted}-${calculatedVersionName}-${buildType}.apk" } } @@ -152,7 +141,7 @@ dependencies { implementation 'io.netty:netty-handler:4.1.94.Final' implementation 'com.google.code.gson:gson:2.10.1' - // Kotlin / MVVM(為 General flavor 的多品項購物車) + // Kotlin / MVVM(basic 模式的多品項購物車) implementation libs.kotlin.stdlib implementation libs.lifecycle.viewmodel.ktx implementation libs.lifecycle.livedata.ktx diff --git a/app/src/Chengwai/java/com/unibuy/smartdevice/ui/SaleFlowHandler.java b/app/src/Chengwai/java/com/unibuy/smartdevice/ui/SaleFlowHandler.java deleted file mode 100644 index 4faa0a6..0000000 --- a/app/src/Chengwai/java/com/unibuy/smartdevice/ui/SaleFlowHandler.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.unibuy.smartdevice.ui; - -import android.content.Context; -import com.unibuy.smartdevice.tools.HandlerMain; -import com.unibuy.smartdevice.ui.dialog.MemberCardPickupDialog; - -/** - * Chengwai (晟崴版) 銷售流程處理器。 - * 點選商品/購物車時直接進入員工卡刷卡取貨 Dialog (MemberCardPickupDialog)。 - */ -public class SaleFlowHandler { - - protected final Context context; - protected final SaleFlowCallback callback; - protected final HandlerMain handlerMain; - - public SaleFlowHandler(Context context, HandlerMain handlerMain, SaleFlowCallback callback) { - this.context = context; - this.handlerMain = handlerMain; - this.callback = callback; - } - - public void onSetupUI() { - // 晟崴版 UI 設定,預設無動作 - } - - public void onSetupUI(android.view.View rootView) { - // 晟崴版 UI 設定,預設無動作 - } - - public void onBuyRequested() { - new MemberCardPickupDialog(context, handlerMain).show(); - } - - public void onShoppingCartRequested() { - // 晟崴版維持原行為:購物車浮鈕等同進入購買流程 - onBuyRequested(); - } - - /** 晟崴版不消化商品點擊,交回 adapter 既有流程(員工卡取貨)。 */ - public boolean handleProductSelected(com.unibuy.smartdevice.structure.SlotStructure slot) { - return false; - } - - /** 晟崴版使用標準現金 Dialog。 */ - public Class cashDialogClass() { - return com.unibuy.smartdevice.ui.dialog.CashPayDialog.class; - } - - public void onBarcodeReceived(String rawData) { - // 晟崴版不處理掃碼 - } - - public void onPickupRequested() { - // 晟崴版不處理取貨碼 - } -} diff --git a/app/src/Chengwai/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java b/app/src/Chengwai/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java deleted file mode 100644 index 1731703..0000000 --- a/app/src/Chengwai/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java +++ /dev/null @@ -1,591 +0,0 @@ -package com.unibuy.smartdevice.ui.dialog; - -import android.content.Context; - -import com.unibuy.smartdevice.DialogAbstract; -import com.unibuy.smartdevice.MyApp; -import com.unibuy.smartdevice.R; -import com.unibuy.smartdevice.databinding.DialogAccessCodeBinding; -import com.unibuy.smartdevice.databinding.DialogPickupCodeFontendBinding; -import com.unibuy.smartdevice.devices.DeviceType; -import com.unibuy.smartdevice.devices.SlotField; -import com.unibuy.smartdevice.devices.UsbDev; -import com.unibuy.smartdevice.exception.LogsEmptyException; -import com.unibuy.smartdevice.exception.LogsIOException; -import com.unibuy.smartdevice.exception.LogsParseException; -import com.unibuy.smartdevice.exception.LogsSettingEmptyException; -import com.unibuy.smartdevice.exception.LogsUnsupportedOperationException; -import com.unibuy.smartdevice.external.HttpAPI; -import com.unibuy.smartdevice.structure.BuyStructure; -import com.unibuy.smartdevice.structure.ProductStructure; -import com.unibuy.smartdevice.structure.SlotStructure; -import com.unibuy.smartdevice.tools.HandlerMain; -import com.unibuy.smartdevice.tools.HandlerMainCountdown; -import com.unibuy.smartdevice.tools.HandlerMainScheduler; -import com.unibuy.smartdevice.tools.ToastHandlerMain; -import com.unibuy.smartdevice.tools.Tools; -import com.unibuy.smartdevice.ui.FontendActivity; -import android.app.AlertDialog; -import android.os.Handler; -import android.os.Looper; -import com.unibuy.smartdevice.controller.DevController; -import com.unibuy.smartdevice.devices.PortTools; -import com.unibuy.smartdevice.ui.dialog.BuyDialog; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -public class FontendPickupCodeDialog extends DialogAbstract { - public enum Option { - TOAST(0), - COUNTDOWN_CANCEL(1), - SET_COUNTDOWN_TEXT(2), - INVOICE_SUCCESSFUL(4), - INVOICE_FAILED(5), - CHANGE_DISPATCH(6), - CHANGE_BUY(7), - SET_STATUS_TEXT(8), - ; - - private int option; - - public int getOption() { - return option; - } - - Option(int option) { - this.option = option; - } - } - - public static final Map optionMap = new HashMap<>(); - static { - for (Option option : Option.values()) { - optionMap.put(option.getOption(), option); - } - } - - @Override - protected Context setCtx() { - return getContext(); - } - - @Override - protected Class setCls() { - return getClass(); - } - - @Override - protected HandlerMain setHandlerMain() { - return new ToastHandlerMain(getCtx(), getLogs()) { - @Override - protected void execute(Context context, int commandCode, String message) { - Option option = optionMap.get(commandCode); - - switch (option) { - case TOAST: - super.execute(context, commandCode, message); - break; - case COUNTDOWN_CANCEL: - cancelOnThreadCountdown(); - break; - case SET_COUNTDOWN_TEXT: - setButtonCancelText(Integer.valueOf(message)); - break; - case INVOICE_SUCCESSFUL: - MyApp.getInstance().getInvoiceData().setCarrier(message); - MyApp.getInstance().getReportFlowInfoData().setInvoiceInfo("carrier:" + MyApp.getInstance().getInvoiceData().getCarrier()); - break; - case INVOICE_FAILED: - break; - case CHANGE_DISPATCH: - //reportAccessCodeData();//上傳通行碼資訊到後台系統 - changeDispatch(); - break; - case CHANGE_BUY: - try { - getLogs().info("CHANGE_BUY triggered with message: " + message); - SlotStructure slotProduct = MyApp.getInstance().getSlotDataByPid(message); - if (slotProduct == null) { - setStatusText(getContext().getString(R.string.product_not_found)); - return; - } - - getLogs().info("Slot found: " + slotProduct.getSlot() + ", Field: " + slotProduct.getField()); - SlotField slotField = SlotField.getSlotField(slotProduct.getField()); - switch (slotField) { - case VMC: - if (slotProduct.isLock()) { - getSrcHandlerMain().start("FontendPickupCodeDialog", "@"+getContext().getString(R.string.product_sales_suspended));//商品暫停販售 - dialogCancel(); - } else if (slotProduct.getCount() == 0) { - getSrcHandlerMain().start("FontendPickupCodeDialog", "@"+getContext().getString(R.string.product_out_of_stock));//商品無存貨 - dialogCancel(); - } else { - // 取貨口檢查已在 onCreate 執行,這裡直接進行 - BuyStructure buyProduct = new BuyStructure(slotProduct.getField(), slotProduct.getSlot(), slotProduct.getProduct(), 1); - MyApp.getInstance().getBuyList().add(buyProduct); - changeBuy(); - } - break; - case ELECTRIC: - if (slotProduct.isLock()) - MyApp.getInstance().getSlotDataByPid(message).setLock(false); - - getSrcHandlerMain().start("FontendPickupCodeDialog", "@"+getContext().getString(R.string.unlock) + slotProduct.getProduct().getProductName());//解鎖 - getSrcHandlerMain().start("FontendPickupCodeDialog", FontendActivity.Option.RESET_DATA.getOption(), "reset data"); - dialogCancel(); - - break; - } - } catch (LogsEmptyException e) { - setStatusText(getContext().getString(R.string.product_not_found));//找不到商品 - } catch (Exception e) { - getLogs().info("Error in CHANGE_BUY: " + e.getMessage()); - e.printStackTrace(); - // 發生未知錯誤時,嘗試繼續或顯示錯誤 - setStatusText("System Error: " + e.getMessage()); - } - break; - case SET_STATUS_TEXT: - setStatusText(message); - break; - } - } - }; - } - - private DialogPickupCodeFontendBinding binding; - private CloseDialogOnThreadHandler closeDialogCountdown; - private boolean isChangeDispatch; - private boolean openBarcode; - private HttpAPI httpAPI; - private boolean haveThings, doorError; - - public FontendPickupCodeDialog(Context context, HandlerMain srcHandlerMain) { - super(context, srcHandlerMain); - } - - @Override - protected void onCreate(Context context) { - binding = DialogPickupCodeFontendBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - MyApp.getInstance().initialReportData(); - - isChangeDispatch = true; - openBarcode = true; - httpAPI = new HttpAPI(getHandlerMain()); - - binding.buttonFinish.setOnClickListener(v -> changeOk()); - binding.buttonCancel.setOnClickListener(v -> changeBuy()); - - closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain()); - closeDialogCountdown.start(40); - - binding.buttonR1.setOnClickListener(v -> { - String text = binding.editTextNumber.getText().toString(); - if (text.length() > 0) { // ✅ 避免長度為 0 崩潰 - binding.editTextNumber.setText(text.substring(0, text.length() - 1)); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - } - closeDialogCountdown.setCountdown(40); - }); - - binding.buttonC1.setOnClickListener(v -> { - binding.editTextNumber.setText(""); - closeDialogCountdown.setCountdown(40); - }); - - binding.button10.setOnClickListener(v -> { - String btnName = binding.button10.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button11.setOnClickListener(v -> { - String btnName = binding.button11.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button12.setOnClickListener(v -> { - String btnName = binding.button12.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button13.setOnClickListener(v -> { - String btnName = binding.button13.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button14.setOnClickListener(v -> { - String btnName = binding.button14.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button15.setOnClickListener(v -> { - String btnName = binding.button15.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button16.setOnClickListener(v -> { - String btnName = binding.button16.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button17.setOnClickListener(v -> { - String btnName = binding.button17.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button18.setOnClickListener(v -> { - String btnName = binding.button18.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button19.setOnClickListener(v -> { - String btnName = binding.button19.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - new ReadBarCodeOnScheduler(getHandlerMain()).start(5, TimeUnit.SECONDS); - - // 進入頁面時立即檢查取貨口 (使用貨道1作為代表) - // callback 為 null,表示檢查通過後不做額外動作,僅在檢查失敗時顯示警告 - checkSlotHaveProduct(1, 1, null); - } - - public class ReadBarCodeOnScheduler extends HandlerMainScheduler { - public ReadBarCodeOnScheduler(HandlerMain handlerMain) { - super(handlerMain); - } - - @Override - protected HandlerMain setHandlerMain() { - return getSrcHandlerMain(); - } - - @Override - protected void execute(HandlerMain handlerMain) { - try { - int totalPrice = 0; - for (BuyStructure buy : MyApp.getInstance().getBuyList()) { - totalPrice += buy.getFullPrice(); - } - - UsbDev usbDev = new UsbDev(DeviceType.QRCODE_SCANNER); - byte[] buffer = new byte[1000]; - usbDev.read(buffer, 100); - for (int i=0; i<7; i++) { - if (isRun() && openBarcode) { - int size = usbDev.read(buffer, 5000); - if (size > 5) { - String barCodeString = new String(buffer, 0, size); - getHandlerMain().start(getLogs().getClassName(), getCtx().getString(R.string.receive_barcode)+":"+ Tools.randomReplace(barCodeString, 0.3)+","+getCtx().getString(R.string.send_transaction));//收到條碼,送出交易 - - try { - MyApp.getInstance().getMemberVerificationStructure().setProductId("0"); - MyApp.getInstance().getMemberVerificationStructure().setProductPrice(0); - MyApp.getInstance().getMemberVerificationStructure().setMemberBarCode(barCodeString); - MyApp.getInstance().getMemberVerificationStructure().setVerificationType(2); - httpAPI.memberVerification(MyApp.getInstance().getMemberVerificationStructure()); - } catch (LogsParseException | LogsSettingEmptyException e) { - getLogs().warning(e); - } - - openBarcode = false; - } - } - } - usbDev.closePort(); - } catch (LogsUnsupportedOperationException | LogsIOException | LogsEmptyException e) { - getLogs().warning(e); - } - } - - @Override - protected Class setCls() { - return getClass(); - } - } - - public int getTotalPrice() { - int totalPrice = 0; - if (MyApp.getInstance().getBuyList().size() == 0) return totalPrice; - for(BuyStructure buy: MyApp.getInstance().getBuyList()) { - totalPrice += buy.getFullPrice(); - } - return totalPrice; - } - - public void changeDispatch() { - if (isChangeDispatch) { - closeDialogCountdown.shutdown(); - isChangeDispatch = false; - changeDialog(DispatchDialog.class); - } - } - - public void changeOk() { - try { - String number = binding.editTextNumber.getText().toString(); - // 長度檢查 - if (number.length() < 8) { - getHandlerMain().start(getClass().getSimpleName(), "統一編號長度不能少於 8 碼"); - return; - } - MyApp.getInstance().getMemberVerificationStructure().setProductId("0"); - MyApp.getInstance().getMemberVerificationStructure().setProductPrice(0); - MyApp.getInstance().getMemberVerificationStructure().setMemberBarCode(number); - MyApp.getInstance().getMemberVerificationStructure().setVerificationType(2); - httpAPI.memberVerification(MyApp.getInstance().getMemberVerificationStructure()); - } catch (LogsParseException e) { - throw new RuntimeException(e); - } catch (LogsSettingEmptyException e) { - throw new RuntimeException(e); - } - } - - public void changeBuy() { - closeDialogCountdown.shutdown(); - BuyPickupCodeDialog buyDialog = new BuyPickupCodeDialog(getContext(), getSrcHandlerMain()); - buyDialog.show(); - cancel(); - } - - public void dialogCancel() { - closeDialogCountdown.shutdown(); - MyApp.getInstance().getBuyList().clear(); - cancel(); - } - - public void cancelOnThreadCountdown() { - MyApp.getInstance().getBuyList().clear(); - cancel(); - } - - public void setButtonCancelText(long countdown) { - binding.buttonCancel.setText(getContext().getString(R.string.return_to)+"("+countdown+")");//返回 - } - - public void setStatusText(String message) { - binding.textStatus.setText(message); - } - - public void reportAccessCodeData() { - try { - MyApp.getInstance().getReportAccessCodeStructure().setField(MyApp.getInstance().getReportAccessCodeStructure().getField()); - MyApp.getInstance().getReportAccessCodeStructure().setSlot(MyApp.getInstance().getBuyList().get(0).getSlot()); - MyApp.getInstance().getReportAccessCodeStructure().setProductId(MyApp.getInstance().getReportAccessCodeStructure().getProductId()); - httpAPI.reportAccessCode(MyApp.getInstance().getReportAccessCodeStructure()); - } catch (LogsParseException e) { - throw new RuntimeException(e); - } catch (LogsSettingEmptyException e) { - throw new RuntimeException(e); - } - } - - public void updatePickupCodeData() { - try { - String number = binding.editTextNumber.getText().toString(); - MyApp.getInstance().getMemberVerificationStructure().setProductId("0"); - MyApp.getInstance().getMemberVerificationStructure().setProductPrice(0); - MyApp.getInstance().getMemberVerificationStructure().setMemberBarCode(number); - MyApp.getInstance().getMemberVerificationStructure().setVerificationType(2); - httpAPI.memberVerification(MyApp.getInstance().getMemberVerificationStructure()); - } catch (LogsParseException e) { - throw new RuntimeException(e); - } catch (LogsSettingEmptyException e) { - throw new RuntimeException(e); - } - } - - public static class CloseDialogOnThreadHandler extends HandlerMainCountdown { - public CloseDialogOnThreadHandler(HandlerMain handlerMain) { - super(handlerMain); - handlerMain.start(getClass().getSimpleName(), CashPayDialog.Option.SET_COUNTDOWN_TEXT.getOption(), String.valueOf(getSrcCountdown())); - } - - @Override - protected HandlerMain setHandlerMain() { - return getSrcHandlerMain(); - } - - @Override - protected void close(HandlerMain handlerMain) { - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.COUNTDOWN_CANCEL.getOption(), "close dialog"); - } - - @Override - protected void execute(long countdown, HandlerMain handlerMain) { - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.SET_COUNTDOWN_TEXT.getOption(), String.valueOf(countdown)); - } - - @Override - protected Class setCls() { - return getClass(); - } - } - - public void checkSlotHaveProduct(int slotnum, int slotcount, Runnable onSafe) { - haveThings = false; - doorError = false; - - // 使用 AtomicBoolean 防止重複執行 (Timeout vs Callback) - // 預設為 false,只要有一方執行過就設為 true,避免重複觸發 - java.util.concurrent.atomic.AtomicBoolean isHandled = new java.util.concurrent.atomic.AtomicBoolean(false); - - // 設定 3000ms 超時機制:如果機器沒反應,視為正常並繼續 - // 增加這段是因為如果 Slot 為空,VMC 可能不會回傳任何資料,導致程式卡住 - // 但如果 onSafe 為 null (代表僅監控不阻塞流程),則不啟動超時,以免太快判定為安全而蓋過隨後回傳的 Error - if (onSafe != null) { - new Handler(Looper.getMainLooper()).postDelayed(() -> { - if (!isHandled.getAndSet(true)) { - getLogs().info("VMC Check Slot Timeout - Approving Safe (Timeout Safeguard)"); - onSafe.run(); - } - }, 3000); - } - - MyApp.getInstance().getDevXinYuanController().addSendBuffer(MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlotPre(slotnum,slotcount)); - MyApp.getInstance().getDevXinYuanController().addSendBuffer(MyApp.getInstance().getDevXinYuanController().getDevXinYuan().checkSlot(slotnum)); - MyApp.getInstance().getDevXinYuanController().getReadBufferByNow(new DevCheckSlotByVMC(getHandlerMain(), onSafe, isHandled)); - } - - public class DevCheckSlotByVMC extends DevController.ReadBufferOnScheduler { - private Runnable onSafe; - private java.util.concurrent.atomic.AtomicBoolean isHandled; - - public DevCheckSlotByVMC(HandlerMain handlerMain, Runnable onSafe, java.util.concurrent.atomic.AtomicBoolean isHandled) { - super(handlerMain); - this.onSafe = onSafe; - this.isHandled = isHandled; - } - - @Override - protected HandlerMain setHandlerMain() { - return getSrcHandlerMain(); - } - - @Override - protected Class setCls() { - return getClass(); - } - - @Override - public void readBufferOnScheduler(DevController devController, HandlerMain handlerMain) { - // 每次檢查前先重置門狀態旗標 - doorError = false; - for (byte[] buffer: devController.getReadBufferByMax()) { - if (buffer[0] == 0x02) { - getLogs().info("XinYuan Read:" + PortTools.showHex(buffer)); - if (buffer[1] == 0x05) { - haveThings = true; - break; - }else if (buffer[1] == 0x06 || buffer[1] == 0x09) { - // 0x06:取貨口門沒關上 - // 0x09:微波爐取貨口門關閉錯誤 - getLogs().info("檢測到取貨口門未正常關閉,狀態碼=" + String.format("%02X", buffer[1])); - doorError = true; - break; - } - } - } - - if(haveThings){ - // 防止 Timeout 後又執行這裡,或者重複執行 - if (isHandled.getAndSet(true)) return; - - // 使用主執行緒顯示 AlertDialog - new Handler(Looper.getMainLooper()).post(() -> { - AlertDialog dialog = new AlertDialog.Builder(getContext()) - .setTitle(R.string.abnormal_reminder) - .setMessage(R.string.there_is_something_at_the_pickup_port) - .setCancelable(false) - .setPositiveButton(R.string.i_knew, (d, which) -> { - d.dismiss(); - // 如果有東西,可能需要重置或關閉 - FontendPickupCodeDialog.this.dialogCancel(); - }) - .create(); - - dialog.show(); - - // 加入 10 秒後自動觸發點擊確定 - new Handler(Looper.getMainLooper()).postDelayed(() -> { - if (dialog.isShowing()) { - dialog.dismiss(); - FontendPickupCodeDialog.this.dialogCancel(); - } - }, 10_000); // 10 秒(10000 毫秒) - }); - } else if (doorError) { - if (isHandled.getAndSet(true)) return; - - // 取貨口門未關閉,提示使用者並阻止後續購買流程 - handlerMain.start("FontendPickupCodeDialog", "取貨口門未正常關閉,請先確認門已關好"); - // 使用主執行緒顯示 AlertDialog - new Handler(Looper.getMainLooper()).post(() -> { - AlertDialog dialog = new AlertDialog.Builder(getContext()) - .setTitle(R.string.abnormal_reminder) - .setMessage("取貨口門未正常關閉,請先確認門已關好。") - .setCancelable(false) - .setPositiveButton(R.string.i_knew, (d, which) -> { - d.dismiss(); - FontendPickupCodeDialog.this.dialogCancel(); - }) - .create(); - - dialog.show(); - - // 加入 10 秒後自動觸發點擊確定 - new Handler(Looper.getMainLooper()).postDelayed(() -> { - if (dialog.isShowing()) { - dialog.dismiss(); - FontendPickupCodeDialog.this.dialogCancel(); - } - }, 10_000); // 10 秒(10000 毫秒) - }); - } else { - // 沒有其他狀況,執行後續動作 - // 注意:這裡假設 VMC 回傳就代表檢測完成。如果 VMC 會分段回傳,可能需要更嚴謹的判斷。 - // 但依照原邏輯,只要沒 error 就算 pass - if (!isHandled.getAndSet(true)) { - if (onSafe != null) { - new Handler(Looper.getMainLooper()).post(onSafe); - } - } - } - } - } -} diff --git a/app/src/Cmuh/java/com/unibuy/smartdevice/ui/dialog/BuyPickupCodeDialog.java b/app/src/Cmuh/java/com/unibuy/smartdevice/ui/dialog/BuyPickupCodeDialog.java deleted file mode 100644 index c88d5fa..0000000 --- a/app/src/Cmuh/java/com/unibuy/smartdevice/ui/dialog/BuyPickupCodeDialog.java +++ /dev/null @@ -1,263 +0,0 @@ -package com.unibuy.smartdevice.ui.dialog; - -import android.content.Context; -import android.view.View; - -import androidx.recyclerview.widget.LinearLayoutManager; - -import com.unibuy.smartdevice.DialogAbstract; -import com.unibuy.smartdevice.MyApp; -import com.unibuy.smartdevice.databinding.DialogPickupCodeBuyBinding; -import com.unibuy.smartdevice.devices.SlotField; -import com.unibuy.smartdevice.exception.LogsEmptyException; -import com.unibuy.smartdevice.structure.BuyStructure; -import com.unibuy.smartdevice.tools.HandlerMain; -import com.unibuy.smartdevice.tools.HandlerMainCountdown; -import com.unibuy.smartdevice.tools.ToastHandlerMain; -import com.unibuy.smartdevice.tools.Tools; -import com.unibuy.smartdevice.ui.recycler.RecyclerDialogPickupCodeBuyListAdpter; - -import java.util.HashMap; -import java.util.Map; - -public class BuyPickupCodeDialog extends DialogAbstract { - public enum Option { - TOAST(0), - COUNTDOWN_CANCEL(1), - SET_COUNTDOWN_TEXT(2), - SET_PRICE(3), - RESET_COUNTDOWM(4), - RESET_COUNTDOWM_TOAST(5), - ; - - private int option; - - public int getOption() { - return option; - } - - Option(int option) { - this.option = option; - } - } - - public static final Map optionMap = new HashMap<>(); - static { - for (Option option : Option.values()) { - optionMap.put(option.getOption(), option); - } - } - - @Override - protected Context setCtx() { - return getContext(); - } - - @Override - protected Class setCls() { - return getClass(); - } - - @Override - protected HandlerMain setHandlerMain() { - return new ToastHandlerMain(getContext(), getLogs()) { - @Override - protected void execute(Context context, int commandCode, String message) { - if (MyApp.getInstance().getBuyList().size() <= 0) { - - dialogCancel(); - } else { - Option option = optionMap.get(commandCode); - - switch (option) { - case TOAST: - super.execute(context, commandCode, message); - break; - case COUNTDOWN_CANCEL: - cancelOnThreadCountdown(); - break; - case SET_COUNTDOWN_TEXT: - setButtonCancelText(Integer.valueOf(message)); - break; - case SET_PRICE: - binding.textPrice.setText(String.valueOf(getTotalPrice())); - closeDialogCountdown.setCountdown(40); - break; - case RESET_COUNTDOWM: - closeDialogCountdown.setCountdown(40); - break; - case RESET_COUNTDOWM_TOAST: - super.execute(context, commandCode, message); - closeDialogCountdown.setCountdown(40); - break; - } - } - } - }; - } - - private DialogPickupCodeBuyBinding binding; - private RecyclerDialogPickupCodeBuyListAdpter recyclerDialogPickupCodeBuyListAdpter; - private CloseDialogOnThreadHandler closeDialogCountdown; - private boolean isChangeDispatch; - - public BuyPickupCodeDialog(Context context, HandlerMain srcHandlerMain) { - super(context, srcHandlerMain); - getLogs().info("open:" + getClass().getSimpleName()); - } - - @Override - protected void onCreate(Context context) { - binding = DialogPickupCodeBuyBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - isChangeDispatch = true; - - this.recyclerDialogPickupCodeBuyListAdpter = new RecyclerDialogPickupCodeBuyListAdpter(getCtx(), MyApp.getInstance().getBuyList(), getHandlerMain()); - this.binding.recyclerDialogBuyList.setAdapter(recyclerDialogPickupCodeBuyListAdpter); - this.binding.recyclerDialogBuyList.setLayoutManager(new LinearLayoutManager(context)); - - MyApp.getInstance().getReportFlowInfoData().setOrderId(Tools.generateTimeBasedUUID()); - MyApp.getInstance().getReportFlowInfoData().setFlowType(6); - MyApp.getInstance().getPostInvoiceByGreenData().setOrderId(MyApp.getInstance().getReportFlowInfoData().getOrderId()); - - this.binding.layoutMemberTransactions.setVisibility(View.GONE); - - this.binding.buttonGetProduct.setOnClickListener(v -> gotoGetProduct()); - this.binding.buttonCancel.setOnClickListener(v -> dialogCancel()); - - closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain()); - closeDialogCountdown.start(40); - - binding.textPrice.setText(String.valueOf(getTotalPrice())); - } - - public int getTotalPrice() { - int totalPrice = 0; - if (MyApp.getInstance().getBuyList().size() == 0) return totalPrice; - for(BuyStructure buy: MyApp.getInstance().getBuyList()) { - totalPrice += buy.getFullPrice(); - } - return totalPrice; - } - - public void gotoGetProduct() { - if (isChangeDispatch) { - closeDialogCountdown.shutdown(); - isChangeDispatch = false; - changeDialog(DispatchDialog.class); - cancel(); - } - } - - public void dialogCancel() { - closeDialogCountdown.shutdown(); - int buyListSize = MyApp.getInstance().getBuyList().size(); - MyApp.getInstance().getBuyList().clear(); - if (buyListSize > 0) { - recyclerDialogPickupCodeBuyListAdpter.notifyItemRangeRemoved(0, buyListSize); - } - cancel(); - } - - public void cancelOnThreadCountdown() { - int buyListSize = MyApp.getInstance().getBuyList().size(); - MyApp.getInstance().getBuyList().clear(); - if (buyListSize > 0) { - recyclerDialogPickupCodeBuyListAdpter.notifyItemRangeRemoved(0, buyListSize); - } - getTools().dialogClose(); - cancel(); - } - - public void setButtonCancelText(long countdown) { - binding.buttonCancel.setText("取消("+countdown+")"); - } - - public void gotoDialog() { - closeDialogCountdown.shutdown(); - int flowType = MyApp.getInstance().getReportFlowInfoData().getFlowType(); - if (flowType == 5) { - changeCheckout(); - } else { - if (MyApp.getInstance().getDevSet().isShoppingCar()) { - if (MyApp.getInstance().getDevSet().isInvoice()) { - changeDialog(InvoiceBarcodeDialog.class); - } else { - changeCheckout(); - } - } else { - if (MyApp.getInstance().getDevSet().isInvoice()) { - BuyStructure buyData = MyApp.getInstance().getBuyList().get(0); - try { - SlotField slotField = SlotField.getSlotField(buyData.getField()); - if (slotField.isInvoice()) { - changeDialog(InvoiceBarcodeDialog.class); - } else { - changeCheckout(); - } - } catch (LogsEmptyException e) { - changeDialog(InvoiceBarcodeDialog.class); - } - } else { - changeCheckout(); - } - } - } - } - - public void changeCheckout() { - closeDialogCountdown.shutdown(); - int flowType = MyApp.getInstance().getReportFlowInfoData().getFlowType(); - switch (flowType) { - case 1: - changeDialog(CheckoutNfcCardDialog.class); - break; - case 2: - changeDialog(CheckoutNfcRechargeDialog.class); - break; - case 3: - changeDialog(CheckoutEsunPayDialog.class); - break; - case 4: - changeDialog(CheckoutNfcPhoneDialog.class); - break; - case 5: - changeDialog(CheckoutAccessCodeDialog.class); - break; - case 9: - changeDialog(CheckoutCashDialog.class); - break; - case 70: - changeDialog(CheckoutLinePayDialog.class); - break; - } - } - - - public static class CloseDialogOnThreadHandler extends HandlerMainCountdown { - public CloseDialogOnThreadHandler(HandlerMain handlerMain) { - super(handlerMain); - } - - @Override - protected HandlerMain setHandlerMain() { - return getSrcHandlerMain(); - } - - @Override - protected void close(HandlerMain handlerMain) { - handlerMain.start(getClass().getSimpleName(), Option.COUNTDOWN_CANCEL.getOption(), "close dialog"); - } - - @Override - protected void execute(long countdown, HandlerMain handlerMain) { - handlerMain.start(getClass().getSimpleName(), Option.SET_COUNTDOWN_TEXT.getOption(), String.valueOf(countdown)); - } - - @Override - protected Class setCls() { - return getClass(); - } - } -} diff --git a/app/src/Cmuh/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java b/app/src/Cmuh/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java deleted file mode 100644 index 03b205a..0000000 --- a/app/src/Cmuh/java/com/unibuy/smartdevice/ui/dialog/FontendPickupCodeDialog.java +++ /dev/null @@ -1,429 +0,0 @@ -package com.unibuy.smartdevice.ui.dialog; - -import android.content.Context; - -import com.unibuy.smartdevice.DialogAbstract; -import com.unibuy.smartdevice.MyApp; -import com.unibuy.smartdevice.databinding.DialogAccessCodeBinding; -import com.unibuy.smartdevice.databinding.DialogPickupCodeFontendBinding; -import com.unibuy.smartdevice.devices.DeviceType; -import com.unibuy.smartdevice.devices.SlotField; -import com.unibuy.smartdevice.devices.UsbDev; -import com.unibuy.smartdevice.exception.LogsEmptyException; -import com.unibuy.smartdevice.exception.LogsIOException; -import com.unibuy.smartdevice.exception.LogsParseException; -import com.unibuy.smartdevice.exception.LogsSettingEmptyException; -import com.unibuy.smartdevice.exception.LogsUnsupportedOperationException; -import com.unibuy.smartdevice.external.HttpAPI; -import com.unibuy.smartdevice.structure.BuyStructure; -import com.unibuy.smartdevice.structure.ProductStructure; -import com.unibuy.smartdevice.structure.SlotStructure; -import com.unibuy.smartdevice.tools.HandlerMain; -import com.unibuy.smartdevice.tools.HandlerMainCountdown; -import com.unibuy.smartdevice.tools.HandlerMainScheduler; -import com.unibuy.smartdevice.tools.ToastHandlerMain; -import com.unibuy.smartdevice.tools.Tools; -import com.unibuy.smartdevice.ui.FontendActivity; - -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.TimeUnit; - -public class FontendPickupCodeDialog extends DialogAbstract { - public enum Option { - TOAST(0), - COUNTDOWN_CANCEL(1), - SET_COUNTDOWN_TEXT(2), - INVOICE_SUCCESSFUL(4), - INVOICE_FAILED(5), - CHANGE_DISPATCH(6), - CHANGE_BUY(7), - SET_STATUS_TEXT(8), - ; - - private int option; - - public int getOption() { - return option; - } - - Option(int option) { - this.option = option; - } - } - - public static final Map optionMap = new HashMap<>(); - static { - for (Option option : Option.values()) { - optionMap.put(option.getOption(), option); - } - } - - @Override - protected Context setCtx() { - return getContext(); - } - - @Override - protected Class setCls() { - return getClass(); - } - - @Override - protected HandlerMain setHandlerMain() { - return new ToastHandlerMain(getCtx(), getLogs()) { - @Override - protected void execute(Context context, int commandCode, String message) { - Option option = optionMap.get(commandCode); - - switch (option) { - case TOAST: - super.execute(context, commandCode, message); - break; - case COUNTDOWN_CANCEL: - cancelOnThreadCountdown(); - break; - case SET_COUNTDOWN_TEXT: - setButtonCancelText(Integer.valueOf(message)); - break; - case INVOICE_SUCCESSFUL: - MyApp.getInstance().getInvoiceData().setCarrier(message); - MyApp.getInstance().getReportFlowInfoData().setInvoiceInfo("carrier:" + MyApp.getInstance().getInvoiceData().getCarrier()); - break; - case INVOICE_FAILED: - break; - case CHANGE_DISPATCH: - reportAccessCodeData();//上傳通行碼資訊到後台系統 - changeDispatch(); - break; - case CHANGE_BUY: - try { - SlotStructure slotProduct = MyApp.getInstance().getSlotDataByPid(message); - SlotField slotField = SlotField.getSlotField(slotProduct.getField()); - switch (slotField) { - case VMC: - if (slotProduct.isLock()) { - getSrcHandlerMain().start("FontendPickupCodeDialog", "@商品暫停販售"); - dialogCancel(); - } else if (slotProduct.getCount() == 0) { - getSrcHandlerMain().start("FontendPickupCodeDialog", "@商品無存貨"); - dialogCancel(); - } - - BuyStructure buyProduct = new BuyStructure(slotProduct.getField(), slotProduct.getSlot(), slotProduct.getProduct(), 1); - MyApp.getInstance().getBuyList().add(buyProduct); - changeBuy(); - - break; - case ELECTRIC: - if (slotProduct.isLock()) - MyApp.getInstance().getSlotDataByPid(message).setLock(false); - - getSrcHandlerMain().start("FontendPickupCodeDialog", "@解鎖" + slotProduct.getProduct().getProductName()); - getSrcHandlerMain().start("FontendPickupCodeDialog", FontendActivity.Option.RESET_DATA.getOption(), "reset data"); - dialogCancel(); - - break; - } - } catch (LogsEmptyException e) { - setStatusText("找不到商品"); - } - break; - case SET_STATUS_TEXT: - setStatusText(message); - break; - } - } - }; - } - - private DialogPickupCodeFontendBinding binding; - private CloseDialogOnThreadHandler closeDialogCountdown; - private boolean isChangeDispatch; - private boolean openBarcode; - private HttpAPI httpAPI; - - public FontendPickupCodeDialog(Context context, HandlerMain srcHandlerMain) { - super(context, srcHandlerMain); - } - - @Override - protected void onCreate(Context context) { - binding = DialogPickupCodeFontendBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - MyApp.getInstance().initialReportData(); - - isChangeDispatch = true; - openBarcode = true; - httpAPI = new HttpAPI(getHandlerMain()); - - binding.buttonFinish.setOnClickListener(v -> changeOk()); - binding.buttonCancel.setOnClickListener(v -> changeBuy()); - - closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain()); - closeDialogCountdown.start(40); - - binding.buttonR1.setOnClickListener(v -> { - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text.substring(0, text.length()-1)); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.buttonC1.setOnClickListener(v -> { - binding.editTextNumber.setText(""); - closeDialogCountdown.setCountdown(40); - }); - - binding.button10.setOnClickListener(v -> { - String btnName = binding.button10.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button11.setOnClickListener(v -> { - String btnName = binding.button11.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button12.setOnClickListener(v -> { - String btnName = binding.button12.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button13.setOnClickListener(v -> { - String btnName = binding.button13.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button14.setOnClickListener(v -> { - String btnName = binding.button14.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button15.setOnClickListener(v -> { - String btnName = binding.button15.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button16.setOnClickListener(v -> { - String btnName = binding.button16.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button17.setOnClickListener(v -> { - String btnName = binding.button17.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button18.setOnClickListener(v -> { - String btnName = binding.button18.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - binding.button19.setOnClickListener(v -> { - String btnName = binding.button19.getText().toString(); - String text = binding.editTextNumber.getText().toString(); - binding.editTextNumber.setText(text+btnName); - binding.editTextNumber.setSelection(binding.editTextNumber.getText().length()); - closeDialogCountdown.setCountdown(40); - }); - - new ReadBarCodeOnScheduler(getHandlerMain()).start(5, TimeUnit.SECONDS); - } - - public class ReadBarCodeOnScheduler extends HandlerMainScheduler { - public ReadBarCodeOnScheduler(HandlerMain handlerMain) { - super(handlerMain); - } - - @Override - protected HandlerMain setHandlerMain() { - return getSrcHandlerMain(); - } - - @Override - protected void execute(HandlerMain handlerMain) { - try { - int totalPrice = 0; - for (BuyStructure buy : MyApp.getInstance().getBuyList()) { - totalPrice += buy.getFullPrice(); - } - - UsbDev usbDev = new UsbDev(DeviceType.QRCODE_SCANNER); - byte[] buffer = new byte[1000]; - usbDev.read(buffer, 100); - for (int i=0; i<7; i++) { - if (isRun() && openBarcode) { - int size = usbDev.read(buffer, 5000); - if (size > 5) { - String barCodeString = new String(buffer, 0, size); - getHandlerMain().start(getLogs().getClassName(), "收到條碼:"+ Tools.randomReplace(barCodeString, 0.3)+",送出交易"); - - try { - // TODO: [新後台改接] 待未來新後台啟用後,此處取貨碼驗證需改串接新的 StarCloudAPI B660 介面 - MyApp.getInstance().getMemberVerificationStructure().setProductId("0"); - MyApp.getInstance().getMemberVerificationStructure().setProductPrice(0); - MyApp.getInstance().getMemberVerificationStructure().setMemberBarCode(barCodeString); - MyApp.getInstance().getMemberVerificationStructure().setVerificationType(2); - httpAPI.memberVerification(MyApp.getInstance().getMemberVerificationStructure()); - } catch (LogsParseException | LogsSettingEmptyException e) { - getLogs().warning(e); - } - - openBarcode = false; - } - } - } - usbDev.closePort(); - } catch (LogsUnsupportedOperationException | LogsIOException | LogsEmptyException e) { - getLogs().warning(e); - } - } - - @Override - protected Class setCls() { - return getClass(); - } - } - - public int getTotalPrice() { - int totalPrice = 0; - if (MyApp.getInstance().getBuyList().size() == 0) return totalPrice; - for(BuyStructure buy: MyApp.getInstance().getBuyList()) { - totalPrice += buy.getFullPrice(); - } - return totalPrice; - } - - public void changeDispatch() { - if (isChangeDispatch) { - closeDialogCountdown.shutdown(); - isChangeDispatch = false; - changeDialog(DispatchDialog.class); - } - } - - public void changeOk() { - try { - String number = binding.editTextNumber.getText().toString(); - MyApp.getInstance().getMemberVerificationStructure().setProductId("0"); - MyApp.getInstance().getMemberVerificationStructure().setProductPrice(0); - MyApp.getInstance().getMemberVerificationStructure().setMemberBarCode(number); - MyApp.getInstance().getMemberVerificationStructure().setVerificationType(2); - httpAPI.memberVerification(MyApp.getInstance().getMemberVerificationStructure()); - } catch (LogsParseException e) { - throw new RuntimeException(e); - } catch (LogsSettingEmptyException e) { - throw new RuntimeException(e); - } - } - - public void changeBuy() { - closeDialogCountdown.shutdown(); - BuyPickupCodeDialog buyDialog = new BuyPickupCodeDialog(getContext(), getSrcHandlerMain()); - buyDialog.show(); - cancel(); - } - - public void dialogCancel() { - closeDialogCountdown.shutdown(); - MyApp.getInstance().getBuyList().clear(); - cancel(); - } - - public void cancelOnThreadCountdown() { - MyApp.getInstance().getBuyList().clear(); - cancel(); - } - - public void setButtonCancelText(long countdown) { - binding.buttonCancel.setText("返回("+countdown+")"); - } - - public void setStatusText(String message) { - binding.textStatus.setText(message); - } - - public void reportAccessCodeData() { - try { - MyApp.getInstance().getReportAccessCodeStructure().setField(MyApp.getInstance().getReportAccessCodeStructure().getField()); - MyApp.getInstance().getReportAccessCodeStructure().setSlot(MyApp.getInstance().getBuyList().get(0).getSlot()); - MyApp.getInstance().getReportAccessCodeStructure().setProductId(MyApp.getInstance().getReportAccessCodeStructure().getProductId()); - httpAPI.reportAccessCode(MyApp.getInstance().getReportAccessCodeStructure()); - } catch (LogsParseException e) { - throw new RuntimeException(e); - } catch (LogsSettingEmptyException e) { - throw new RuntimeException(e); - } - } - - public void updatePickupCodeData() { - try { - String number = binding.editTextNumber.getText().toString(); - MyApp.getInstance().getMemberVerificationStructure().setProductId("0"); - MyApp.getInstance().getMemberVerificationStructure().setProductPrice(0); - MyApp.getInstance().getMemberVerificationStructure().setMemberBarCode(number); - MyApp.getInstance().getMemberVerificationStructure().setVerificationType(2); - httpAPI.memberVerification(MyApp.getInstance().getMemberVerificationStructure()); - } catch (LogsParseException e) { - throw new RuntimeException(e); - } catch (LogsSettingEmptyException e) { - throw new RuntimeException(e); - } - } - - public static class CloseDialogOnThreadHandler extends HandlerMainCountdown { - public CloseDialogOnThreadHandler(HandlerMain handlerMain) { - super(handlerMain); - handlerMain.start(getClass().getSimpleName(), CheckoutCashDialog.Option.SET_COUNTDOWN_TEXT.getOption(), String.valueOf(getSrcCountdown())); - } - - @Override - protected HandlerMain setHandlerMain() { - return getSrcHandlerMain(); - } - - @Override - protected void close(HandlerMain handlerMain) { - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.COUNTDOWN_CANCEL.getOption(), "close dialog"); - } - - @Override - protected void execute(long countdown, HandlerMain handlerMain) { - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.SET_COUNTDOWN_TEXT.getOption(), String.valueOf(countdown)); - } - - @Override - protected Class setCls() { - return getClass(); - } - } -} diff --git a/app/src/Cmuh/java/com/unibuy/smartdevice/ui/recycler/RecyclerDialogPickupCodeBuyListAdpter.java b/app/src/Cmuh/java/com/unibuy/smartdevice/ui/recycler/RecyclerDialogPickupCodeBuyListAdpter.java deleted file mode 100644 index 67418ae..0000000 --- a/app/src/Cmuh/java/com/unibuy/smartdevice/ui/recycler/RecyclerDialogPickupCodeBuyListAdpter.java +++ /dev/null @@ -1,193 +0,0 @@ -package com.unibuy.smartdevice.ui.recycler; - -import android.content.Context; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.recyclerview.widget.RecyclerView; - -import com.unibuy.smartdevice.databinding.RecyclerDialogBuyListBinding; -import com.unibuy.smartdevice.databinding.RecyclerDialogPickupCodeBuyListBinding; -import com.unibuy.smartdevice.devices.SlotField; -import com.unibuy.smartdevice.exception.Logs; -import com.unibuy.smartdevice.exception.LogsEmptyException; -import com.unibuy.smartdevice.structure.BuyStructure; -import com.unibuy.smartdevice.tools.HandlerMain; -import com.unibuy.smartdevice.ui.dialog.BuyDialog; -import com.unibuy.smartdevice.ui.tools.ImageGlide; - -import java.util.List; - -public class RecyclerDialogPickupCodeBuyListAdpter extends RecyclerView.Adapter { - private Logs logs; - private Context context; - private final List buyList; - private HandlerMain handlerMain; - - public RecyclerDialogPickupCodeBuyListAdpter(Context context, List buyList, HandlerMain handlerMain) { - this.logs = new Logs(this.getClass()); - this.context = context; - this.buyList = buyList; - this.handlerMain = handlerMain; - } - - public static class ViewHolder extends RecyclerView.ViewHolder { - RecyclerDialogPickupCodeBuyListBinding binding; - - public ViewHolder(@NonNull RecyclerDialogPickupCodeBuyListBinding binding) { - super(binding.getRoot()); - this.binding = binding; - } - } - - @NonNull - @Override - public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { - // 使用 ViewBinding 來綁定佈局 - RecyclerDialogPickupCodeBuyListBinding binding = RecyclerDialogPickupCodeBuyListBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false); - - return new ViewHolder(binding); - } - - @Override - public void onBindViewHolder(@NonNull ViewHolder holder, int position) { - logs.info("position:" + position); - - BuyStructure buyProduct = buyList.get(position); - holder.binding.textPosition.setText(String.valueOf(position + 1)); - String productID = buyProduct.getProduct().getProductID(); - if (!productID.isEmpty() && !productID.equals("?")) { - holder.binding.textProductName.setText(buyProduct.getProduct().getProductName()); - } else { - holder.binding.textProductName.setText("點我選擇商品"); - } -// holder.binding.textDelete.setOnClickListener(new textDeleteOnClickListener(position)); - String urlString = buyProduct.getProduct().getProductImg(); - try { - new ImageGlide(context).fileload(urlString, holder.binding.imageProductPicture); - } catch (LogsEmptyException e) { - this.logs.warning(e); - } - int machinePrice = buyProduct.getProduct().getMachinePrice(); - int sellingPrice = buyProduct.getProduct().getSellingPrice(); - int memberPrice = buyProduct.getProduct().getMemberPrice(); - - int slotUpperLimit = buyProduct.getProduct().getSlotUpperLimit(); - int count = buyProduct.getCount(); - - int price = machinePrice > 0? machinePrice: sellingPrice; - - holder.binding.textPrice.setText("$"+price); - holder.binding.textCount.setText("x"+String.valueOf(count)); - - logs.info("field:" + buyProduct.getField()); - } - - public class addition1OnClickListener implements View.OnClickListener { - private Context context; - private RecyclerDialogBuyListBinding binding; - private int position; - - public addition1OnClickListener(Context context, RecyclerDialogBuyListBinding binding, int position) { - this.context = context; - this.binding = binding; - this.position = position; - } - - @Override - public void onClick(View v) { - BuyStructure buyProduct = buyList.get(position); - - int count = buyProduct.getCount(); - count++; - buyList.get(position).setCount(count); - binding.textCount.setText("x"+String.valueOf(count)); - - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.SET_PRICE.getOption(), "total price"); - } - } - - public class subtraction1OnClickListener implements View.OnClickListener { - private Context context; - private RecyclerDialogBuyListBinding binding; - private int position; - public subtraction1OnClickListener(Context context, RecyclerDialogBuyListBinding binding, int position) { - this.context = context; - this.binding = binding; - this.position = position; - } - - @Override - public void onClick(View v) { - BuyStructure buyProduct = buyList.get(position); - - int count = buyProduct.getCount(); - count--; - if (count <= 0) { - buyList.remove(position); - notifyItemRemoved(position); - notifyItemRangeChanged(position, buyList.size() - position); - - handlerMain.start(getClass().getSimpleName(),"刪除一筆資料"); - } else { - buyList.get(position).setCount(count); - binding.textCount.setText("x"+String.valueOf(count)); - - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.SET_PRICE.getOption(),"total price"); - } - } - } - - public class editCountOnEditorActionListener implements TextView.OnEditorActionListener{ - private ViewHolder holder; - private int position; - - public editCountOnEditorActionListener(ViewHolder holder, int position) { - this.holder = holder; - this.position = position; - } - - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_SEND) { - int count = Integer.valueOf(v.getText().toString()); - buyList.get(position).setCount(count); - notifyItemChanged(position); - return true; - } - return false; - } - } - - public class textDeleteOnClickListener implements View.OnClickListener{ - private int position; - - public textDeleteOnClickListener(int position) { - this.position = position; - } - - @Override - public void onClick(View v) { - delProductData(position); - } - } - @Override - public int getItemCount() { - return buyList.size(); - } - - public void addProductData(BuyStructure buyProduct) { - buyList.add(buyProduct); - notifyItemInserted(buyList.size() -1); - } - - public void delProductData(int position) { - buyList.remove(position); - notifyItemRemoved(position); - } -} diff --git a/app/src/Cmuh/res/layout/activity_fontend.xml b/app/src/Cmuh/res/layout/activity_fontend.xml deleted file mode 100644 index 66c6859..0000000 --- a/app/src/Cmuh/res/layout/activity_fontend.xml +++ /dev/null @@ -1,271 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -