diff --git a/.agents/rules/versioning.md b/.agents/rules/versioning.md index dbf6b57..64b282b 100644 --- a/.agents/rules/versioning.md +++ b/.agents/rules/versioning.md @@ -28,8 +28,8 @@ trigger: always_on 依據公司標準規範,實體機台發佈之 APK 與版本名稱需嚴格符合以下欄位對照: ``` -來源 _ 安卓號 _ 主板號 _ 機器系列 _ 專案名稱 _ 螢幕大小 _ 次版號 _ 修補版號 _ 環境 -S _ 12 _ XY _ U _ Chengwai _ 10 _ 08 _ 8 _ R +來源 _ 安卓號 _ 主板號 _ 機器系列 _ 螢幕大小 _ 次版號 _ 修補版號 _ 環境 +S _ 12 _ XY _ U _ 10 _ 08 _ 8 _ R ``` ### 欄位解析: @@ -37,7 +37,6 @@ S _ 12 _ XY _ U _ Chengwai _ 10 _ 08 _ 8 _ R * **`12` (安卓號)**:Android 系統主版本號。 * **`XY` (主板號)**:主板型號規格。 * **`U` (機器系列 / 機型規格)**:例如代表 U 系列機型。 -* **`Chengwai` (專案名稱)**:客戶/銷售場景專案代碼(如 `General`, `Chengwai`, `Cmuh`)。 * **`10` (螢幕大小)**:對應 Gradle 中的 `verMajor`。 * **`08` (次版號)**:對應 Gradle 中的 `verMinor`(格式化為雙位數)。 * **`8` (修補版號)**:對應 Gradle 中的 `verPatch`。 @@ -55,38 +54,32 @@ S _ 12 _ XY _ U _ Chengwai _ 10 _ 08 _ 8 _ R --- -## 4. 雙維度產品風味 (Product Flavors) 與 APK 命名規範 +## 4. 單維度硬體規格 (Hardware Flavor) 與 APK 命名規範 -本專案採用**雙維度 Flavor** 架構,以完美分離「客戶銷售場景」與「底層硬體規格」,方便後續快速擴充。 +本專案原本採用雙維度,但目前客戶專案(購買流程)已收斂為執行期 `shopping_mode`(雲端 B014 下發)動態決定,因此不再使用 flavor 分家。 +目前僅保留**單一維度 `hardware`**,專門處理底層硬體規格差異。 -### 4.1 雙維度定義 (flavorDimensions) -本專案於 `app/build.gradle` 中定義了兩個 Flavor 維度: +### 4.1 硬體維度定義 (flavorDimensions) +本專案於 `app/build.gradle` 中定義了唯一的 Flavor 維度 `hardware`: -1. **`project` 維度 (客戶/銷售場景區隔)**: - * **`General`** (通用主幹):預設顯示付款方式選單(彈出 `BuyDialog`)。未來新客戶最常用此通用主幹。 - * **`Chengwai`** (晟崴客製):直接跳出員工卡刷卡畫面(彈出 `MemberCardPickupDialog`),不顯示付款選單。 - * **`Cmuh`** (中國醫客製):支援 QR Code 掃碼、處方籤醫令離線 AES 解密與解析領藥流程。 -2. **`hardware` 維度 (硬體與系統規格區隔)**: - * **`S12XYU`**:代表 `S` (來源) + `12` (安卓號) + `XY` (主板號) + `U` (機器系列/機型規格)。 - * **`S9XYU`**:代表中國醫現場使用的 Android 9 / 21.5 吋硬體規格;除非明確指定中國醫 10 吋或 `S12XYU`,中國醫 (`Cmuh`) 相關修正與驗證預設必須使用此硬體 flavor。 +* **`S12XYU`** (10 吋預設硬體):代表 `S` (來源) + `12` (安卓號) + `XY` (主板號) + `U` (機器系列/機型規格)。 +* **`S9XYU`** (21.5 吋硬體):代表中國醫現場使用的 Android 9 / 21.5 吋硬體規格。 ### 4.2 Flavor 驗證與編譯指令選擇 -針對特定客戶或現場問題修改後,必須編譯對應的 Gradle variant,不可只用通用 `assembleDebug` 或錯誤硬體 flavor 代替。 +修改後,必須編譯對應硬體規格的 Gradle variant,不可只用通用 `assembleDebug`: -* **中國醫現場版 (`Cmuh`) 驗證指令**:`./gradlew :app:assembleCmuhS9XYUDebug --no-daemon` -* **晟崴版 (`Chengwai`) 預設驗證指令**:`./gradlew :app:assembleChengwaiS12XYUDebug --no-daemon` -* **通用版 (`General`) 預設驗證指令**:`./gradlew :app:assembleGeneralS12XYUDebug --no-daemon` +* **10 吋一般機台版 (含晟崴、通用等)**:`./gradlew :app:assembleS12XYUDebug --no-daemon` +* **21.5 吋中國醫等大螢幕機台版**:`./gradlew :app:assembleS9XYUDebug --no-daemon` ### 4.3 實體機台 APK 命名鐵律 -為了符合實體機台 OTA 平台發佈規範,打包輸出的 APK 檔名必須嚴格連動雙維度,並遵循以下格式: -`app-[格式化硬體規格]_[專案名稱]-[版本號]-[BuildType].apk` +為了符合實體機台 OTA 平台發佈規範,打包輸出的 APK 檔名必須遵循以下格式: +`app-[格式化硬體規格]-[版本號]-[BuildType].apk` * **格式化規則**:`S12XYU` 會在 Gradle 打包時被自動展開格式化為 `S_12_XY_U`。 * **輸出檔名範例**: - * `app-S_12_XY_U_General-10_08_8_R-release.apk` - * `app-S_12_XY_U_Chengwai-10_08_8_R-release.apk` - * `app-S_12_XY_U_Cmuh-10_08_8_R-release.apk` + * `app-S_12_XY_U-10_08_8_R-release.apk` + * `app-S_9_XY_U-21_08_8_R-release.apk` * **注意**:此檔名轉換規則已完全由 `app/build.gradle` 頂部 Gradle 變數與 `applicationVariants.all` 自動連動生成,**嚴禁手動修改輸出檔名**。 --- diff --git a/AGENTS.md b/AGENTS.md index bacc403..027509f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,8 @@ - 禁止直接手改成品 `versionCode`/`versionName` 值。 - 版號管理透過 `verMajor`、`verMinor`、`verPatch` 變數。 - OTA 發版時,`versionCode` 必須嚴格遞增。 -- 中國醫現場版 (`Cmuh`) 修正預設以 `:app:assembleCmuhS9XYUDebug` 驗證。 +- 21.5 吋大螢幕現場版(如中國醫)修正預設以 `:app:assembleS9XYUDebug` 驗證。 +- 10 吋一般現場版(如晟崴、通用)修正預設以 `:app:assembleS12XYUDebug` 驗證。 ## When Rules Conflict 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 0d16f57..c186267 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java @@ -740,6 +740,22 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl binding.floatButtonShoppingCart.setVisibility(View.GONE); } + // 觀察購物車內容以更新數量 Badge + com.unibuy.v2.UnibuyHelper.INSTANCE.getCartViewModel().getCartItems().observe(this, cartItems -> { + int totalCount = 0; + if (cartItems != null) { + for (com.unibuy.smartdevice.structure.SlotStructure item : cartItems) { + totalCount += item.cartSelectedCount; + } + } + if (totalCount > 0) { + binding.textCartBadge.setText(String.valueOf(totalCount)); + binding.textCartBadge.setVisibility(View.VISIBLE); + } else { + binding.textCartBadge.setVisibility(View.GONE); + } + }); + startHeaderReportScheduler(); // binding.btnswitchlanTW.setOnClickListener(v -> switchLanguage("tw")); diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/dialog/PickupSuccessDialog.java b/app/src/main/java/com/unibuy/smartdevice/ui/dialog/PickupSuccessDialog.java new file mode 100644 index 0000000..fd16d2b --- /dev/null +++ b/app/src/main/java/com/unibuy/smartdevice/ui/dialog/PickupSuccessDialog.java @@ -0,0 +1,148 @@ +package com.unibuy.smartdevice.ui.dialog; + +import android.content.Context; + +import com.unibuy.smartdevice.DialogAbstract; +import com.unibuy.smartdevice.databinding.DialogPickupSuccessBinding; +import com.unibuy.smartdevice.tools.HandlerMain; +import com.unibuy.smartdevice.tools.HandlerMainCountdown; +import com.unibuy.smartdevice.tools.ToastHandlerMain; + +import java.util.HashMap; +import java.util.Map; + +public class PickupSuccessDialog extends DialogAbstract { + public enum Option { + TOAST(0), + COUNTDOWN_CANCEL(1), + SET_COUNTDOWN_TEXT(2), + RESET_COUNTDOWN(3), + ; + + private int option; + + public int getOption() { + return option; + } + + Option(int option) { + this.option = option; + } + } + + public PickupSuccessDialog(Context context, HandlerMain srcHandlerMain) { + super(context, srcHandlerMain); + getLogs().info("PickupSuccessDialog:" + getClass().getSimpleName()); + } + + public static final Map optionMap = new HashMap<>(); + static { + for (Option option : Option.values()) { + optionMap.put(option.getOption(), option); + } + } + + @Override + protected Context setCtx() { + return this.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) { + Option option = optionMap.get(commandCode); + if (option != null) { + switch (option) { + case TOAST: + super.execute(context, commandCode, message); + break; + case COUNTDOWN_CANCEL: + cancelOnThreadCountdown(); + break; + case SET_COUNTDOWN_TEXT: + setButtonConfirmText(Integer.valueOf(message)); + break; + case RESET_COUNTDOWN: + closeDialogCountdown.setCountdown(15); + break; + } + } + } + }; + } + + private DialogPickupSuccessBinding binding; + private CloseDialogOnThreadHandler closeDialogCountdown; + private String laneNumber = "99"; // 預設貨道號碼 + + public void setLaneNumber(String laneNumber) { + this.laneNumber = laneNumber; + if (binding != null) { + binding.tvLaneNumber.setText(laneNumber); + binding.tvSubtitle.setText("請至" + laneNumber + "號櫃門拿取您的商品"); + } + } + + @Override + protected void onCreate(Context context) { + binding = DialogPickupSuccessBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + + // 初始化貨道數字與副標文字 + setLaneNumber(laneNumber); + + binding.btnConfirm.setOnClickListener(v -> dialogCancel()); + + closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain()); + closeDialogCountdown.start(15); + } + + public void dialogCancel() { + if (closeDialogCountdown != null) { + closeDialogCountdown.shutdown(); + } + cancel(); + } + + public void cancelOnThreadCountdown() { + getTools().dialogClose(); + cancel(); + } + + public void setButtonConfirmText(long countdown) { + binding.btnConfirm.setText("確認已取貨(" + countdown + "秒)"); + } + + 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/main/java/com/unibuy/smartdevice/ui/dialog/SystemInspectionDialog.java b/app/src/main/java/com/unibuy/smartdevice/ui/dialog/SystemInspectionDialog.java new file mode 100644 index 0000000..05feb99 --- /dev/null +++ b/app/src/main/java/com/unibuy/smartdevice/ui/dialog/SystemInspectionDialog.java @@ -0,0 +1,44 @@ +package com.unibuy.smartdevice.ui.dialog; + +import android.content.Context; + +import com.unibuy.smartdevice.DialogAbstract; +import com.unibuy.smartdevice.databinding.DialogSystemInspectionBinding; +import com.unibuy.smartdevice.tools.HandlerMain; +import com.unibuy.smartdevice.tools.ToastHandlerMain; + +public class SystemInspectionDialog extends DialogAbstract { + + public SystemInspectionDialog(Context context, HandlerMain srcHandlerMain) { + super(context, srcHandlerMain); + getLogs().info("SystemInspectionDialog:" + getClass().getSimpleName()); + } + + @Override + protected Context setCtx() { + return this.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) { + super.execute(context, commandCode, message); + } + }; + } + + private DialogSystemInspectionBinding binding; + + @Override + protected void onCreate(Context context) { + binding = DialogSystemInspectionBinding.inflate(getLayoutInflater()); + setContentView(binding.getRoot()); + } +} diff --git a/app/src/main/res/drawable/TapPay30.png b/app/src/main/res/drawable/TapPay30.png new file mode 100644 index 0000000..802e4e8 Binary files /dev/null and b/app/src/main/res/drawable/TapPay30.png differ diff --git a/app/src/main/res/drawable/TapPay31.png b/app/src/main/res/drawable/TapPay31.png new file mode 100644 index 0000000..d719ffc Binary files /dev/null and b/app/src/main/res/drawable/TapPay31.png differ diff --git a/app/src/main/res/drawable/TapPay32.png b/app/src/main/res/drawable/TapPay32.png new file mode 100644 index 0000000..d719ffc Binary files /dev/null and b/app/src/main/res/drawable/TapPay32.png differ diff --git a/app/src/main/res/drawable/TapPay33.png b/app/src/main/res/drawable/TapPay33.png new file mode 100644 index 0000000..e9529bd Binary files /dev/null and b/app/src/main/res/drawable/TapPay33.png differ diff --git a/app/src/main/res/drawable/TapPay34.png b/app/src/main/res/drawable/TapPay34.png new file mode 100644 index 0000000..11503e7 Binary files /dev/null and b/app/src/main/res/drawable/TapPay34.png differ diff --git a/app/src/main/res/drawable/add_24.xml b/app/src/main/res/drawable/add_24.xml new file mode 100644 index 0000000..7a1b5bb --- /dev/null +++ b/app/src/main/res/drawable/add_24.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/bg_circle_primary.xml b/app/src/main/res/drawable/bg_circle_primary.xml new file mode 100644 index 0000000..8a6a5f6 --- /dev/null +++ b/app/src/main/res/drawable/bg_circle_primary.xml @@ -0,0 +1,6 @@ + + + + + diff --git a/app/src/main/res/drawable/bg_quantity_outline_button.xml b/app/src/main/res/drawable/bg_quantity_outline_button.xml new file mode 100644 index 0000000..e6e9c1c --- /dev/null +++ b/app/src/main/res/drawable/bg_quantity_outline_button.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/bg_waste_outline_button.xml b/app/src/main/res/drawable/bg_waste_outline_button.xml new file mode 100644 index 0000000..2de6c1b --- /dev/null +++ b/app/src/main/res/drawable/bg_waste_outline_button.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/close_24.xml b/app/src/main/res/drawable/close_24.xml new file mode 100644 index 0000000..2d0ef43 --- /dev/null +++ b/app/src/main/res/drawable/close_24.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/close_warning_24.xml b/app/src/main/res/drawable/close_warning_24.xml new file mode 100644 index 0000000..89d530e --- /dev/null +++ b/app/src/main/res/drawable/close_warning_24.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/package_24.xml b/app/src/main/res/drawable/package_24.xml new file mode 100644 index 0000000..be9c696 --- /dev/null +++ b/app/src/main/res/drawable/package_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/remove_24.xml b/app/src/main/res/drawable/remove_24.xml new file mode 100644 index 0000000..c4ae68f --- /dev/null +++ b/app/src/main/res/drawable/remove_24.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/shopping_cart_24.xml b/app/src/main/res/drawable/shopping_cart_24.xml new file mode 100644 index 0000000..9919e7d --- /dev/null +++ b/app/src/main/res/drawable/shopping_cart_24.xml @@ -0,0 +1,16 @@ + + + + + diff --git a/app/src/main/res/layout/activity_fontend.xml b/app/src/main/res/layout/activity_fontend.xml index 159be00..ebe5df0 100644 --- a/app/src/main/res/layout/activity_fontend.xml +++ b/app/src/main/res/layout/activity_fontend.xml @@ -231,37 +231,132 @@ - + app:layout_constraintEnd_toEndOf="@+id/bottom_navigation"> - + + + + + + + + + + + + app:layout_constraintEnd_toEndOf="@+id/bottom_navigation"> + + + + + + + + + + + + + + + +