[REFACTOR] 收斂 project 維度為執行期 shopping_mode + B014 雲端設定唯讀同步
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-<ver>-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 <noreply@anthropic.com>
This commit is contained in:
parent
2c3255cb64
commit
ab3b53da99
@ -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-<HW>-<ver>-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..."
|
||||
|
||||
@ -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 發版追溯標記指南"
|
||||
|
||||
@ -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 檔名)。
|
||||
|
||||
@ -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 指引。
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<? extends com.unibuy.smartdevice.DialogAbstract> cashDialogClass() {
|
||||
return com.unibuy.smartdevice.ui.dialog.CashPayDialog.class;
|
||||
}
|
||||
|
||||
public void onBarcodeReceived(String rawData) {
|
||||
// 晟崴版不處理掃碼
|
||||
}
|
||||
|
||||
public void onPickupRequested() {
|
||||
// 晟崴版不處理取貨碼
|
||||
}
|
||||
}
|
||||
@ -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<Integer, Option> optionMap = new HashMap<>();
|
||||
static {
|
||||
for (Option option : Option.values()) {
|
||||
optionMap.put(option.getOption(), option);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Context setCtx() {
|
||||
return getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends DialogAbstract> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<Integer, Option> optionMap = new HashMap<>();
|
||||
static {
|
||||
for (Option option : Option.values()) {
|
||||
optionMap.put(option.getOption(), option);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Context setCtx() {
|
||||
return getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends DialogAbstract> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<Integer, Option> optionMap = new HashMap<>();
|
||||
static {
|
||||
for (Option option : Option.values()) {
|
||||
optionMap.put(option.getOption(), option);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Context setCtx() {
|
||||
return getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends DialogAbstract> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<RecyclerDialogPickupCodeBuyListAdpter.ViewHolder> {
|
||||
private Logs logs;
|
||||
private Context context;
|
||||
private final List<BuyStructure> buyList;
|
||||
private HandlerMain handlerMain;
|
||||
|
||||
public RecyclerDialogPickupCodeBuyListAdpter(Context context, List<BuyStructure> 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);
|
||||
}
|
||||
}
|
||||
@ -1,271 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/light_gray"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout5"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:background="@color/red"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingRight="20dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textVersion"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:text="20250108ver1"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textThreadCount"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:text="TC:0"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
android:id="@+id/btnswitchlanTW"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:text="中"
|
||||
android:visibility="gone"
|
||||
android:textSize="30sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnswitchlanEn"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:text="En"
|
||||
android:visibility="gone"
|
||||
android:textSize="30sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/btnswitchlanJa"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start"
|
||||
android:text="日"
|
||||
android:visibility="gone"
|
||||
android:textSize="30sp"
|
||||
android:textColor="@color/white"
|
||||
android:textStyle="bold"
|
||||
/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textTemperature"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="end"
|
||||
android:text="0 ℃"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textTime"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="end"
|
||||
android:text="1911/01/01 01:01:01"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textNetwork"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center_vertical|end"
|
||||
android:text="@string/no_internet"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/relativeLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="60dp"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:background="@color/black"
|
||||
app:layout_constraintDimensionRatio="18:21"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.media3.ui.PlayerView
|
||||
android:id="@+id/videoView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:show_subtitle_button="true"
|
||||
app:use_artwork="true"
|
||||
app:use_controller="false"
|
||||
app:resize_mode="fit"
|
||||
app:surface_type="texture_view" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageAd"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
/>
|
||||
</RelativeLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/fragment_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/bottom_navigation"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/relativeLayout" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/bottom_navigation"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="10dp">
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/buttonVmc"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/drinksale"
|
||||
app:cornerRadius="12dp"
|
||||
android:backgroundTint="@android:color/transparent"
|
||||
app:strokeColor="@color/gray"
|
||||
android:textSize="38sp"
|
||||
app:strokeWidth="2dp"
|
||||
android:visibility="gone"
|
||||
android:layout_margin="4dp"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/buttonElectric"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="@string/board_games"
|
||||
app:cornerRadius="12dp"
|
||||
android:backgroundTint="@android:color/transparent"
|
||||
app:strokeColor="@color/gray"
|
||||
android:textSize="38sp"
|
||||
android:visibility="gone"
|
||||
app:strokeWidth="2dp"
|
||||
android:layout_margin="4dp"/>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/buttonOther"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:visibility="gone"
|
||||
android:text="@string/other"
|
||||
app:cornerRadius="12dp"
|
||||
android:backgroundTint="@android:color/transparent"
|
||||
app:strokeColor="@color/gray"
|
||||
android:textSize="38sp"
|
||||
app:strokeWidth="2dp"
|
||||
android:layout_margin="4dp"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/floatButtonPickupCode"
|
||||
android:layout_width="50dp"
|
||||
android:layout_height="150dp"
|
||||
android:text="取\n貨\n碼"
|
||||
android:textSize="28sp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginBottom="80dp"
|
||||
android:backgroundTint="@color/gray"
|
||||
android:textColor="@color/black"
|
||||
android:alpha="0.8"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@+id/floatButtonShoppingCart"
|
||||
app:layout_constraintEnd_toEndOf="@+id/bottom_navigation"
|
||||
android:textStyle="bold"/>
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/floatButtonShoppingCart"
|
||||
android:layout_width="58dp"
|
||||
android:layout_height="51dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:layout_marginBottom="80dp"
|
||||
android:contentDescription="TODO"
|
||||
android:visibility="gone"
|
||||
app:backgroundTint="@color/gray"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/fragment_container"
|
||||
app:layout_constraintEnd_toEndOf="@+id/bottom_navigation"
|
||||
app:maxImageSize="60dp"
|
||||
app:srcCompat="@drawable/baseline_add_shopping_cart_24" />
|
||||
|
||||
|
||||
<Button
|
||||
android:id="@+id/btnTopRight"
|
||||
android:layout_width="200dp"
|
||||
android:layout_height="100dp"
|
||||
android:text="進入後台"
|
||||
android:textSize="24sp"
|
||||
android:backgroundTint="@color/red"
|
||||
android:textColor="@android:color/white"
|
||||
android:alpha="1"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="gone"
|
||||
android:layout_marginTop="60dp"
|
||||
android:layout_marginEnd="8dp"/>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,104 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/linearLayout3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/light_gray">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout4"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/light_gray"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@color/red"
|
||||
android:gravity="center"
|
||||
android:text="購物車"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:background="@color/white"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.15"
|
||||
android:gravity="center|end"
|
||||
android:text="合計金額:"
|
||||
android:textColor="@color/red"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPrice"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="0.85"
|
||||
android:gravity="center|start"
|
||||
android:text="0"
|
||||
android:textColor="@color/red"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerDialogBuyList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="750dp"
|
||||
/>
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutMemberTransactions"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="340dp"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:orientation="vertical">
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonCancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
android:text="取消(40)"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonGetProduct"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
android:text="取貨"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="20sp"
|
||||
android:visibility="visible" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,194 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/light_gray">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/light_gray"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="40dp"
|
||||
android:background="@color/red"
|
||||
android:gravity="center"
|
||||
android:text="取貨碼"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:background="@color/white"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textStatus"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:text="輸入取貨碼 或 掃描QRCode"
|
||||
android:textColor="@color/red"
|
||||
android:textSize="30sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearMark1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:paddingTop="10dp"
|
||||
android:paddingBottom="20dp"
|
||||
android:background="@color/white"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/editTextNumber"
|
||||
android:layout_width="300dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:textSize="20sp"
|
||||
android:hint="取貨碼"
|
||||
android:gravity="center"
|
||||
android:inputType="number" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button10"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="0" />
|
||||
<Button
|
||||
android:id="@+id/button11"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="1" />
|
||||
<Button
|
||||
android:id="@+id/button12"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="2" />
|
||||
<Button
|
||||
android:id="@+id/button13"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="3" />
|
||||
<Button
|
||||
android:id="@+id/button14"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="4" />
|
||||
<Button
|
||||
android:id="@+id/buttonR1"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/light_gray"
|
||||
android:text="←" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/button15"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="5" />
|
||||
<Button
|
||||
android:id="@+id/button16"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="6" />
|
||||
<Button
|
||||
android:id="@+id/button17"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="7" />
|
||||
<Button
|
||||
android:id="@+id/button18"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="8" />
|
||||
<Button
|
||||
android:id="@+id/button19"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/black"
|
||||
android:text="9" />
|
||||
<Button
|
||||
android:id="@+id/buttonC1"
|
||||
android:layout_width="70dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="@color/light_gray"
|
||||
android:text="C" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="50dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonCancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
android:text="返回(40)"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonFinish"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
android:text="輸入完成"
|
||||
android:textSize="20sp"
|
||||
android:textColor="@color/black" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,39 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/black"
|
||||
tools:context=".ui.devs.vmc.VmcFragment">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerBuy"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="500dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/scanbarcodebtn"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="400dp"
|
||||
android:layout_marginTop="-50dp"
|
||||
android:padding="10dp"
|
||||
android:text="掃描單據\n按鈕"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="70sp"
|
||||
app:strokeColor="@color/gray"
|
||||
app:strokeWidth="5dp" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,109 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="1dp"
|
||||
android:paddingBottom="1dp"
|
||||
>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:layout_marginTop="1dp"
|
||||
android:background="@color/white"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPosition"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_marginEnd="2dp"
|
||||
android:layout_weight="0.3"
|
||||
android:gravity="center"
|
||||
android:text="0"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:visibility="visible" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0.5"
|
||||
android:paddingTop="5dp"
|
||||
android:paddingBottom="5dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageProductPicture"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:adjustViewBounds="true"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/default_product"
|
||||
app:layout_constraintDimensionRatio="16:9" />
|
||||
</RelativeLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@color/white"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
tools:layout_editor_absoluteY="1dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@color/white"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="10dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textProductName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:text="商品名稱"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@color/white"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPrice"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:text="$0"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
android:id="@+id/textCount"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center"
|
||||
android:text="x0"
|
||||
android:textSize="24sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,264 +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.R;
|
||||
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<Integer, Option> optionMap = new HashMap<>();
|
||||
static {
|
||||
for (Option option : Option.values()) {
|
||||
optionMap.put(option.getOption(), option);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Context setCtx() {
|
||||
return getContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<? extends DialogAbstract> 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(getContext().getString(R.string.cancel)+"("+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(CashPayDialog.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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,194 +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.R;
|
||||
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<RecyclerDialogPickupCodeBuyListAdpter.ViewHolder> {
|
||||
private Logs logs;
|
||||
private Context context;
|
||||
private final List<BuyStructure> buyList;
|
||||
private HandlerMain handlerMain;
|
||||
|
||||
public RecyclerDialogPickupCodeBuyListAdpter(Context context, List<BuyStructure> 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));
|
||||
String productID = buyProduct.getProduct().getProductID();
|
||||
if (!productID.isEmpty() && !productID.equals("?")) {
|
||||
holder.binding.textProductName.setText(buyProduct.getProduct().getProductName());
|
||||
} else {
|
||||
holder.binding.textProductName.setText(context.getString(R.string.click_select_product));//
|
||||
}
|
||||
// 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(),context.getString(R.string.delete_a_record));//刪除一筆資料
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
@ -1,168 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/linearLayout3"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/light_gray">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout4"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/light_gray"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:background="@color/red"
|
||||
android:gravity="center"
|
||||
android:text="現金支付"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="20sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="280dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:background="@color/white"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1.1"
|
||||
android:gravity="center|end"
|
||||
android:text="訂單金額:"
|
||||
android:textColor="@color/red"
|
||||
android:textSize="40sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textPrice"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="0.9"
|
||||
android:gravity="center|start"
|
||||
android:text="0"
|
||||
android:textColor="@color/red"
|
||||
android:textSize="40sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="680dp"
|
||||
android:layout_marginBottom="1dp"
|
||||
android:background="@color/white"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/testOne"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="0.90"
|
||||
android:gravity="center"
|
||||
android:text="請投入硬幣或紙鈔"
|
||||
android:textColor="@color/red"
|
||||
android:textSize="80sp"
|
||||
android:textStyle="bold" />
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/cash"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="0.90"
|
||||
android:gravity="center"
|
||||
android:text="已投入的金額:"
|
||||
android:textColor="#0060E2"
|
||||
android:textSize="40sp"
|
||||
android:textStyle="bold" />
|
||||
<TextView
|
||||
android:id="@+id/textStatus2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="5dp"
|
||||
android:layout_weight="0.90"
|
||||
android:gravity="center"
|
||||
android:text="未投入的金額:"
|
||||
android:textColor="#ff0000"
|
||||
android:textSize="40sp"
|
||||
android:textStyle="bold" />
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical"
|
||||
android:visibility="visible">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="@null"
|
||||
android:scaleType="fitCenter"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/checkout_wait" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_marginTop="40dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonCancel"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="200dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
android:text="取消並退幣"
|
||||
android:textSize="28sp"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonChange"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
android:text="@string/replacement_payment"
|
||||
android:visibility="gone"
|
||||
android:textSize="28sp"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonRetryEscrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="200dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="#FF9800"
|
||||
android:text="嘗試退鈔並重試"
|
||||
android:visibility="gone"
|
||||
android:textSize="28sp"
|
||||
android:textColor="@color/white" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- General flavor multi-item shopping cart -->
|
||||
<string name="shopping_cart_title">Shopping Cart</string>
|
||||
<string name="add_more_items">Add More Items</string>
|
||||
<string name="go_to_pay">Proceed to Checkout</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="cancel_button_format">Cancel (%d sec)</string>
|
||||
<string name="total_items_format">Items: (%d)</string>
|
||||
<string name="total_amount_format">NT$ %d</string>
|
||||
<string name="cart_out_of_stock">Out of Stock</string>
|
||||
<string name="cart_cannot_decrease">Cannot Decrease Further</string>
|
||||
<string name="cart_max_items">Up to 8 items per cart</string>
|
||||
<string name="cart_added">Added to cart</string>
|
||||
<string name="data_error">Data Error</string>
|
||||
</resources>
|
||||
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- General flavor 多品目ショッピングカート -->
|
||||
<string name="shopping_cart_title">ショッピングカート</string>
|
||||
<string name="add_more_items">商品を追加</string>
|
||||
<string name="go_to_pay">お会計へ進む</string>
|
||||
<string name="cancel">キャンセル</string>
|
||||
<string name="cancel_button_format">キャンセル(%d秒)</string>
|
||||
<string name="total_items_format">商品数:(%d)</string>
|
||||
<string name="total_amount_format">NT$ %d</string>
|
||||
<string name="cart_out_of_stock">在庫切れ</string>
|
||||
<string name="cart_cannot_decrease">これ以上減らせません</string>
|
||||
<string name="cart_max_items">カートは最大8点まで購入できます</string>
|
||||
<string name="cart_added">カートに追加しました</string>
|
||||
<string name="data_error">データ異常</string>
|
||||
</resources>
|
||||
@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
General flavor 的 DevSet 預設覆寫:裝上即可用多品項購物車 + 指定付款方式。
|
||||
(現金、刷卡/NFC、玉山掃碼、LINE Pay 掃碼、發票、購物車)
|
||||
其餘沿用 main 預設(TapPay 維持開啟,可於主控台關閉)。
|
||||
-->
|
||||
<resources>
|
||||
<bool name="devset_default_shopping_cart">true</bool>
|
||||
<bool name="devset_default_cash">true</bool>
|
||||
<bool name="devset_default_line_pay">true</bool>
|
||||
<!-- General 顯示前台取貨碼浮鈕 -->
|
||||
<bool name="show_pickup_code_button">true</bool>
|
||||
</resources>
|
||||
@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- General flavor 多品項購物車 -->
|
||||
<string name="shopping_cart_title">購物車</string>
|
||||
<string name="add_more_items">加購商品</string>
|
||||
<string name="go_to_pay">前往結帳</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="cancel_button_format">取消(%d秒)</string>
|
||||
<string name="total_items_format">商品數量:(%d)</string>
|
||||
<string name="total_amount_format">NT$ %d</string>
|
||||
<string name="cart_out_of_stock">超出庫存</string>
|
||||
<string name="cart_cannot_decrease">不能減少了</string>
|
||||
<string name="cart_max_items">購物車最多可購買 8 件商品</string>
|
||||
<string name="cart_added">已加入購物車</string>
|
||||
<string name="data_error">數據異常</string>
|
||||
</resources>
|
||||
@ -6,8 +6,13 @@ public final class AppEntry {
|
||||
private AppEntry() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否為「取貨單」購買流程(原 Cmuh 中國醫版)。
|
||||
* 改由雲端 B014 shopping_mode 決定(pickup_sheet),取代舊 project flavor 判定。
|
||||
* 開機時 {@code MyApp.initialSystemSet} 已載入持久化值,故 {@link #initialActivity()} 取得到。
|
||||
*/
|
||||
public static boolean isCmuh() {
|
||||
return "Cmuh".equalsIgnoreCase(BuildConfig.FLAVOR_project);
|
||||
return "pickup_sheet".equals(MyApp.getInstance().getShoppingMode());
|
||||
}
|
||||
|
||||
public static Class<?> initialActivity() {
|
||||
|
||||
@ -110,6 +110,9 @@ public class MyApp extends Application {
|
||||
private EsunpayStructure esunpay;
|
||||
private MachineStructure machine;
|
||||
private DevSetStructure devSet;
|
||||
// 購買流程模式:basic(多品項購物車)/ employee_card(員工卡)/ pickup_sheet(取貨單),
|
||||
// 由雲端 B014 ShoppingMode 下發,取代舊 project flavor 分家。預設 basic。
|
||||
private String shoppingMode = "basic";
|
||||
private GreenInvoiceStructure greenInvoice;
|
||||
private GreenpaySettingStructure greenpaySetting;
|
||||
private GreenpayMerchantStructure greenpayMerchant;
|
||||
@ -209,7 +212,9 @@ public class MyApp extends Application {
|
||||
devSetRes.getBoolean(R.bool.devset_default_tap_pay_31),
|
||||
devSetRes.getBoolean(R.bool.devset_default_tap_pay_32),
|
||||
devSetRes.getBoolean(R.bool.devset_default_tap_pay_33),
|
||||
devSetRes.getBoolean(R.bool.devset_default_tap_pay_34));
|
||||
devSetRes.getBoolean(R.bool.devset_default_tap_pay_34),
|
||||
devSetRes.getBoolean(R.bool.devset_default_pickup_code),
|
||||
devSetRes.getBoolean(R.bool.devset_default_pass_code));
|
||||
this.machine = new MachineStructure("");
|
||||
this.esunpay = new EsunpayStructure("", "", "");
|
||||
this.greenInvoice = new GreenInvoiceStructure("", "", "", "");
|
||||
@ -348,6 +353,15 @@ public class MyApp extends Application {
|
||||
MyApp.getInstance().getDevSet().setDevTapPay(jsonObject.getBoolean("DevTapPay"));
|
||||
MyApp.getInstance().getDevSet().setDevCash(jsonObject.getBoolean("DevCash"));
|
||||
MyApp.getInstance().getDevSet().setDevLinePay(jsonObject.getBoolean("DevLinePay"));
|
||||
// TapPay30~34 與取貨碼/通行碼:用 optBoolean 容忍舊紀錄缺 key,避免整段載入中斷
|
||||
MyApp.getInstance().getDevSet().setTapPay30(jsonObject.optBoolean("TapPay30", false));
|
||||
MyApp.getInstance().getDevSet().setTapPay31(jsonObject.optBoolean("TapPay31", false));
|
||||
MyApp.getInstance().getDevSet().setTapPay32(jsonObject.optBoolean("TapPay32", false));
|
||||
MyApp.getInstance().getDevSet().setTapPay33(jsonObject.optBoolean("TapPay33", false));
|
||||
MyApp.getInstance().getDevSet().setTapPay34(jsonObject.optBoolean("TapPay34", false));
|
||||
MyApp.getInstance().getDevSet().setPickupCode(jsonObject.optBoolean("PickupCode", false));
|
||||
MyApp.getInstance().getDevSet().setPassCode(jsonObject.optBoolean("PassCode", false));
|
||||
MyApp.getInstance().setShoppingMode(jsonObject.optString("ShoppingMode", "basic"));
|
||||
} catch (JSONException e) {
|
||||
new Logs(MyApp.class).warning(e);
|
||||
}
|
||||
@ -383,6 +397,14 @@ public class MyApp extends Application {
|
||||
systemSetByJsonObject.put("DevTapPay", MyApp.getInstance().getDevSet().isDevTapPay());
|
||||
systemSetByJsonObject.put("DevCash", MyApp.getInstance().getDevSet().isDevCash());
|
||||
systemSetByJsonObject.put("DevLinePay", MyApp.getInstance().getDevSet().isDevLinePay());
|
||||
systemSetByJsonObject.put("TapPay30", MyApp.getInstance().getDevSet().isTapPay30());
|
||||
systemSetByJsonObject.put("TapPay31", MyApp.getInstance().getDevSet().isTapPay31());
|
||||
systemSetByJsonObject.put("TapPay32", MyApp.getInstance().getDevSet().isTapPay32());
|
||||
systemSetByJsonObject.put("TapPay33", MyApp.getInstance().getDevSet().isTapPay33());
|
||||
systemSetByJsonObject.put("TapPay34", MyApp.getInstance().getDevSet().isTapPay34());
|
||||
systemSetByJsonObject.put("PickupCode", MyApp.getInstance().getDevSet().isPickupCode());
|
||||
systemSetByJsonObject.put("PassCode", MyApp.getInstance().getDevSet().isPassCode());
|
||||
systemSetByJsonObject.put("ShoppingMode", MyApp.getInstance().getShoppingMode());
|
||||
|
||||
JSONObject cashSetByJsonObject = new JSONObject();
|
||||
cashSetByJsonObject.put("BillF1000", MyApp.getInstance().getCashSetStructure().isBillF1000());
|
||||
@ -888,6 +910,14 @@ public class MyApp extends Application {
|
||||
return devSet;
|
||||
}
|
||||
|
||||
public String getShoppingMode() {
|
||||
return shoppingMode;
|
||||
}
|
||||
|
||||
public void setShoppingMode(String shoppingMode) {
|
||||
this.shoppingMode = (shoppingMode == null || shoppingMode.isEmpty()) ? "basic" : shoppingMode;
|
||||
}
|
||||
|
||||
|
||||
public ReportAccessCodeStructure getReportAccessCodeStructure() {
|
||||
return reportAccessCodeStructure;
|
||||
|
||||
@ -46,14 +46,16 @@ public enum ComPort {
|
||||
}
|
||||
|
||||
public String getFilepath() {
|
||||
// 下位機接線:晟崴(Chengwai) 為最新硬體,使用 ttyS7/ttyS6;
|
||||
// 通用版(General) 與中國醫(Cmuh) 下位機相同,使用 ttyS1/ttyS2。
|
||||
boolean isChengwai = "Chengwai".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project);
|
||||
// 下位機接線由硬體 flavor(IPC 世代 / 安卓版本)決定,與購買流程 shopping_mode 正交:
|
||||
// S12XYU(新板,原晟崴 10")→ ttyS7/ttyS6;
|
||||
// S9XYU (Android 9 板,原中國醫/通用 21.5")→ ttyS1/ttyS2。
|
||||
// 單一 flavor 維度(hardware)下,AGP 僅產生 BuildConfig.FLAVOR(值為 S12XYU / S9XYU)。
|
||||
boolean isS12 = "S12XYU".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR);
|
||||
if (this == COM_PORT_S1) {
|
||||
return isChengwai ? "/dev/ttyS7" : "/dev/ttyS1";
|
||||
return isS12 ? "/dev/ttyS7" : "/dev/ttyS1";
|
||||
}
|
||||
if (this == COM_PORT_S2) {
|
||||
return isChengwai ? "/dev/ttyS6" : "/dev/ttyS2";
|
||||
return isS12 ? "/dev/ttyS6" : "/dev/ttyS2";
|
||||
}
|
||||
return filepath;
|
||||
}
|
||||
|
||||
@ -15,6 +15,8 @@ import com.unibuy.smartdevice.MyApp;
|
||||
import com.unibuy.smartdevice.R;
|
||||
import com.unibuy.smartdevice.database.ProductsDao;
|
||||
import com.unibuy.smartdevice.database.SettingsDao;
|
||||
import com.unibuy.smartdevice.structure.CashSetStructure;
|
||||
import com.unibuy.smartdevice.structure.DevSetStructure;
|
||||
import com.unibuy.smartdevice.structure.ProductStructure;
|
||||
import com.unibuy.smartdevice.structure.ReportSlotListStructure;
|
||||
import com.unibuy.smartdevice.structure.SlotStructure;
|
||||
@ -804,7 +806,52 @@ public class StarCloudAPI {
|
||||
MyApp.getInstance().getGreenInvoice().setGreenHashIV(config.optString("t050v36", ""));
|
||||
MyApp.getInstance().getGreenInvoice().setGreenEmail(config.optString("t050v38", ""));
|
||||
|
||||
// 5. 重要:將所有設定同步回資料庫,防止 App 重啟後消失
|
||||
// 5. 解析雲端功能開關(缺漏「欄位」→ false;缺漏「整個物件」→ 不動本地,避免連線異常把現場全關)
|
||||
JSONObject devSet = config.optJSONObject("DevSet");
|
||||
if (devSet != null) {
|
||||
DevSetStructure d = MyApp.getInstance().getDevSet();
|
||||
d.setShoppingCar(devSet.optBoolean("ShoppingCar", false));
|
||||
d.setInvoice(devSet.optBoolean("Invoice", false));
|
||||
d.setDevNFCPay(devSet.optBoolean("DevNFCPay", false));
|
||||
d.setDevEsunPay(devSet.optBoolean("DevEsunPay", false));
|
||||
d.setDevTapPay(devSet.optBoolean("DevTapPay", false));
|
||||
d.setDevCash(devSet.optBoolean("DevCash", false));
|
||||
d.setDevLinePay(devSet.optBoolean("DevLinePay", false));
|
||||
d.setTapPay30(devSet.optBoolean("TapPay30", false));
|
||||
d.setTapPay31(devSet.optBoolean("TapPay31", false));
|
||||
d.setTapPay32(devSet.optBoolean("TapPay32", false));
|
||||
d.setTapPay33(devSet.optBoolean("TapPay33", false));
|
||||
d.setTapPay34(devSet.optBoolean("TapPay34", false));
|
||||
// VMC/Electic 雲端不下發 → 不碰,保留本地值
|
||||
}
|
||||
JSONObject cashSet = config.optJSONObject("CashSet");
|
||||
if (cashSet != null) {
|
||||
CashSetStructure c = MyApp.getInstance().getCashSetStructure();
|
||||
c.setBillF1000(cashSet.optBoolean("BillF1000", false));
|
||||
c.setBillE500(cashSet.optBoolean("BillE500", false));
|
||||
c.setBillD100(cashSet.optBoolean("BillD100", false));
|
||||
c.setCoinF50(cashSet.optBoolean("CoinF50", false));
|
||||
c.setCoinE10(cashSet.optBoolean("CoinE10", false));
|
||||
c.setCoinD5(cashSet.optBoolean("CoinD5", false));
|
||||
c.setCoinC1(cashSet.optBoolean("CoinC1", false));
|
||||
}
|
||||
// 6. FunctionSet 取貨碼/通行碼(本次只接這兩項;迎賓禮/會員/環溫留待未來)
|
||||
JSONObject funcSet = config.optJSONObject("FunctionSet");
|
||||
if (funcSet != null) {
|
||||
DevSetStructure d = MyApp.getInstance().getDevSet();
|
||||
d.setPickupCode(funcSet.optBoolean("PickupCode", false));
|
||||
d.setPassCode(funcSet.optBoolean("PassCode", false));
|
||||
}
|
||||
// 6b. ShoppingMode:購買流程模式(basic/employee_card/pickup_sheet),取代舊 project flavor
|
||||
boolean hasShoppingMode = config.has("ShoppingMode");
|
||||
if (hasShoppingMode) {
|
||||
MyApp.getInstance().setShoppingMode(config.optString("ShoppingMode", "basic"));
|
||||
}
|
||||
if (devSet != null || cashSet != null || funcSet != null || hasShoppingMode) {
|
||||
MyApp.saveSystemSet(handlerMain.getContext());
|
||||
}
|
||||
|
||||
// 7. 重要:將所有設定同步回資料庫,防止 App 重啟後消失
|
||||
persistCurrentSettings();
|
||||
MyApp.getInstance().initMqtt();
|
||||
}
|
||||
|
||||
@ -15,10 +15,14 @@ public class DevSetStructure {
|
||||
private boolean isTapPay32;
|
||||
private boolean isTapPay33;
|
||||
private boolean isTapPay34;
|
||||
// 取貨碼/通行碼:改由雲端 B014 FunctionSet 下發控制(預設值來自 flavor devset_defaults.xml)
|
||||
private boolean isPickupCode;
|
||||
private boolean isPassCode;
|
||||
|
||||
public DevSetStructure(boolean isVMC, boolean isElectic, boolean isShoppingCar, boolean isInvoice,
|
||||
boolean isDevNFCPay, boolean isDevEsunPay, boolean isDevTapPay, boolean isDevCash, boolean isDevLinePay,
|
||||
boolean isTapPay30,boolean isTapPay31,boolean isTapPay32,boolean isTapPay33, boolean isTapPay34) {
|
||||
boolean isTapPay30,boolean isTapPay31,boolean isTapPay32,boolean isTapPay33, boolean isTapPay34,
|
||||
boolean isPickupCode, boolean isPassCode) {
|
||||
this.isVMC = isVMC;
|
||||
this.isElectic = isElectic;
|
||||
this.isShoppingCar = isShoppingCar;
|
||||
@ -33,6 +37,8 @@ public class DevSetStructure {
|
||||
this.isTapPay32 = isTapPay32;
|
||||
this.isTapPay33 = isTapPay33;
|
||||
this.isTapPay34 = isTapPay34;
|
||||
this.isPickupCode = isPickupCode;
|
||||
this.isPassCode = isPassCode;
|
||||
}
|
||||
|
||||
public boolean isVMC() {
|
||||
@ -124,4 +130,20 @@ public class DevSetStructure {
|
||||
public void setTapPay33(boolean tapPay33) {isTapPay33 = tapPay33;}
|
||||
public void setTapPay34(boolean tapPay34) {isTapPay34 = tapPay34;}
|
||||
|
||||
public boolean isPickupCode() {
|
||||
return isPickupCode;
|
||||
}
|
||||
|
||||
public void setPickupCode(boolean pickupCode) {
|
||||
isPickupCode = pickupCode;
|
||||
}
|
||||
|
||||
public boolean isPassCode() {
|
||||
return isPassCode;
|
||||
}
|
||||
|
||||
public void setPassCode(boolean passCode) {
|
||||
isPassCode = passCode;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.unibuy.smartdevice.ui;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.blankj.utilcode.util.ToastUtils;
|
||||
import com.unibuy.smartdevice.R;
|
||||
import com.unibuy.smartdevice.structure.SlotStructure;
|
||||
@ -11,52 +12,42 @@ import com.unibuy.v2.UnibuyHelper;
|
||||
import com.unibuy.v2.ui.ShoppingCartFragment;
|
||||
|
||||
/**
|
||||
* General (通用版) 銷售流程處理器。
|
||||
* basic 模式(原 General 通用版)銷售流程。
|
||||
* 結帳時彈出標準付款 Dialog (BuyDialog);購物車浮鈕則開啟多品項購物車 Fragment。
|
||||
*/
|
||||
public class SaleFlowHandler {
|
||||
|
||||
protected final Context context;
|
||||
protected final SaleFlowCallback callback;
|
||||
protected final HandlerMain handlerMain;
|
||||
public class BasicSaleFlow extends SaleFlowHandler {
|
||||
|
||||
/** 多品項購物車 Fragment(懶建立,重複開啟時沿用同一實例)。 */
|
||||
private ShoppingCartFragment cartFragment;
|
||||
|
||||
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 BasicSaleFlow(Context context, HandlerMain handlerMain, SaleFlowCallback callback) {
|
||||
super(context, handlerMain, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuyRequested() {
|
||||
new BuyDialog(context, handlerMain).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* 點選 VMC 商品:加入多品項購物車並停留商品列表(不做取貨口檢查,出貨時才檢查)。
|
||||
* @return true 表示已消化此次點擊(General 一律 true)。
|
||||
* @return true 表示已消化此次點擊。
|
||||
*/
|
||||
@Override
|
||||
public boolean handleProductSelected(SlotStructure slot) {
|
||||
UnibuyHelper.INSTANCE.getCartViewModel().addToCart(slot);
|
||||
ToastUtils.showShort(R.string.cart_added);
|
||||
return true;
|
||||
}
|
||||
|
||||
/** General 使用 Luzui 移植的現金強化版(卡鈔/找零保障/自癒),上報接 TaiwanStar 新 MQTT。 */
|
||||
/** basic 使用 Luzui 移植的現金強化版(卡鈔/找零保障/自癒),上報接 TaiwanStar 新 MQTT。 */
|
||||
@Override
|
||||
public Class<? extends com.unibuy.smartdevice.DialogAbstract> cashDialogClass() {
|
||||
return com.unibuy.smartdevice.ui.dialog.CashPayDialogGeneral.class;
|
||||
}
|
||||
|
||||
/** 購物車浮鈕:開啟多品項購物車 Fragment。 */
|
||||
@Override
|
||||
public void onShoppingCartRequested() {
|
||||
if (!(context instanceof FontendActivity)) {
|
||||
return;
|
||||
@ -67,12 +58,8 @@ public class SaleFlowHandler {
|
||||
((FontendActivity) context).openFragment(cartFragment);
|
||||
}
|
||||
|
||||
public void onBarcodeReceived(String rawData) {
|
||||
// 通用版不處理掃碼
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPickupRequested() {
|
||||
// 通用版取貨碼:開啟取貨碼輸入 Dialog
|
||||
new FontendPickupCodeDialog(context, handlerMain).show();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.unibuy.smartdevice.ui;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.unibuy.smartdevice.tools.HandlerMain;
|
||||
import com.unibuy.smartdevice.ui.dialog.MemberCardPickupDialog;
|
||||
|
||||
/**
|
||||
* employee_card 模式(原 Chengwai 晟崴版)銷售流程。
|
||||
* 點選商品/購物車時直接進入員工卡刷卡取貨 Dialog (MemberCardPickupDialog, B680)。
|
||||
* 商品點擊不消化、現金走標準 Dialog、無取貨碼入口 —— 皆沿用基底預設。
|
||||
*/
|
||||
public class EmployeeCardSaleFlow extends SaleFlowHandler {
|
||||
|
||||
public EmployeeCardSaleFlow(Context context, HandlerMain handlerMain, SaleFlowCallback callback) {
|
||||
super(context, handlerMain, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuyRequested() {
|
||||
new MemberCardPickupDialog(context, handlerMain).show();
|
||||
}
|
||||
}
|
||||
@ -426,7 +426,7 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
// 初始化銷售流程處理器
|
||||
saleFlowHandler = new SaleFlowHandler(this, getHandlerMain(), this);
|
||||
saleFlowHandler = SaleFlowHandler.create(this, getHandlerMain(), this);
|
||||
saleFlowHandler.onSetupUI();
|
||||
|
||||
httpAPI = new HttpAPI(getHandlerMain());
|
||||
@ -713,9 +713,10 @@ public class FontendActivity extends AppCompatActivityAbstract implements SaleFl
|
||||
}
|
||||
});
|
||||
|
||||
// 前台取貨碼浮鈕:由 flavor 設定決定是否顯示(General 顯示,其餘維持隱藏)
|
||||
// 前台取貨碼浮鈕:改由雲端 B014 FunctionSet.PickupCode 下發控制(DevSet.isPickupCode);
|
||||
// 雲端配好前由 flavor devset_defaults 提供預設(General=true)。
|
||||
binding.floatButtonPickupCode.setVisibility(
|
||||
getResources().getBoolean(R.bool.show_pickup_code_button) ? VISIBLE : View.GONE);
|
||||
MyApp.getInstance().getDevSet().isPickupCode() ? VISIBLE : View.GONE);
|
||||
|
||||
binding.floatButtonShoppingCart.setOnClickListener(view -> {
|
||||
// 改由 flavor 的 SaleFlowHandler 決定:General 開多品項購物車 Fragment,
|
||||
|
||||
@ -36,7 +36,7 @@ import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* Cmuh (中國醫版) 銷售流程處理器。
|
||||
* pickup_sheet 模式(原 Cmuh 中國醫版)銷售流程。
|
||||
* 負責:
|
||||
* 1. 綁定「40秒內刷條碼」按鈕 (scanbarcodebtn)
|
||||
* 2. 連接 USB 掃描器背景讀取
|
||||
@ -44,64 +44,37 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
* 4. 庫存核對與自動加入購物車
|
||||
* 5. 顯示領藥與錯誤 Dialog
|
||||
*/
|
||||
public class SaleFlowHandler {
|
||||
public class PickupSheetSaleFlow extends SaleFlowHandler {
|
||||
private static final String ORDC_EMPTY = "ORDC_EMPTY";
|
||||
private static final String MATERIAL_NOT_FOUND = "MATERIAL_NOT_FOUND";
|
||||
private static final String STOCK_EMPTY = "STOCK_EMPTY";
|
||||
private static final String STOCK_NOT_ENOUGH = "STOCK_NOT_ENOUGH";
|
||||
|
||||
protected final Context context;
|
||||
protected final SaleFlowCallback callback;
|
||||
protected final HandlerMain handlerMain;
|
||||
|
||||
private boolean openBarcode = true;
|
||||
private boolean haveValuestate = false;
|
||||
private long lastClickTime = 0;
|
||||
|
||||
public SaleFlowHandler(Context context, HandlerMain handlerMain, SaleFlowCallback callback) {
|
||||
this.context = context;
|
||||
this.handlerMain = handlerMain;
|
||||
this.callback = callback;
|
||||
public PickupSheetSaleFlow(Context context, HandlerMain handlerMain, SaleFlowCallback callback) {
|
||||
super(context, handlerMain, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 Activity 的 onResume() 中會被呼叫,此時尋找並綁定 VmcFragment 中的掃碼按鈕
|
||||
*/
|
||||
public void onSetupUI() {
|
||||
onSetupUI(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetupUI(View rootView) {
|
||||
if (context instanceof Activity) {
|
||||
View btnScan = rootView != null ? rootView.findViewById(R.id.scanbarcodebtn) : ((Activity) context).findViewById(R.id.scanbarcodebtn);
|
||||
if (btnScan != null) {
|
||||
btnScan.setOnClickListener(new BtnQrCodeOnClick(handlerMain, context));
|
||||
MyApp.getInstance().getLogs().info("✅ 成功在 Cmuh SaleFlowHandler 綁定掃碼按鈕!");
|
||||
MyApp.getInstance().getLogs().info("✅ 成功在 PickupSheetSaleFlow 綁定掃碼按鈕!");
|
||||
} else {
|
||||
MyApp.getInstance().getLogs().info("❌ 在 Cmuh SaleFlowHandler 找不到掃碼按鈕 R.id.scanbarcodebtn!");
|
||||
MyApp.getInstance().getLogs().info("❌ 在 PickupSheetSaleFlow 找不到掃碼按鈕 R.id.scanbarcodebtn!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBuyRequested() {
|
||||
// 中國醫版沒有購物車付款,此方法不執行
|
||||
}
|
||||
|
||||
public void onShoppingCartRequested() {
|
||||
// 中國醫版維持原行為(不處理購物車浮鈕)
|
||||
onBuyRequested();
|
||||
}
|
||||
|
||||
/** 中國醫版不消化商品點擊,交回 adapter 既有流程。 */
|
||||
public boolean handleProductSelected(com.unibuy.smartdevice.structure.SlotStructure slot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 中國醫版使用標準現金 Dialog。 */
|
||||
public Class<? extends com.unibuy.smartdevice.DialogAbstract> cashDialogClass() {
|
||||
return com.unibuy.smartdevice.ui.dialog.CashPayDialog.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBarcodeReceived(String rawData) {
|
||||
// 若是經由其他方式傳入的 Barcode 可在此處直接處理
|
||||
try {
|
||||
@ -114,8 +87,9 @@ public class SaleFlowHandler {
|
||||
}
|
||||
|
||||
/**
|
||||
* CMUH 點選取貨按鈕,開啟輸入取貨碼的 Dialog
|
||||
* 點選取貨按鈕,開啟輸入取貨碼的 Dialog(pickup_sheet 走舊 API 驗證,見 FontendPickupCodeDialog)。
|
||||
*/
|
||||
@Override
|
||||
public void onPickupRequested() {
|
||||
new FontendPickupCodeDialog(context, handlerMain).show();
|
||||
}
|
||||
@ -144,7 +118,7 @@ public class SaleFlowHandler {
|
||||
UsbDev usbDev = new UsbDev(DeviceType.QRCODE_SCANNER);
|
||||
byte[] buffer = new byte[1000];
|
||||
usbDev.read(buffer, 100); // 預讀清除緩衝
|
||||
|
||||
|
||||
for (int i = 0; i < 40; i++) { // 總計約 40 秒
|
||||
if (isRun() && openBarcode) {
|
||||
int size = usbDev.read(buffer, 1000); // 超時為 1 秒
|
||||
@ -231,7 +205,7 @@ public class SaleFlowHandler {
|
||||
if (encryptedBytes.length % 16 != 0) {
|
||||
throw new IllegalArgumentException("錯誤:加密數據長度不是 16 的倍數,可能需要填充");
|
||||
}
|
||||
|
||||
|
||||
byte[] keyBytes = keyString.getBytes(StandardCharsets.UTF_8);
|
||||
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
|
||||
|
||||
@ -246,7 +220,7 @@ public class SaleFlowHandler {
|
||||
|
||||
String decryptedStr = MyApp.getInstance().getDecryptedData();
|
||||
MyApp.getInstance().getLogs().info("decryptedStr=" + decryptedStr);
|
||||
|
||||
|
||||
JSONObject jsonObject = new JSONObject(decryptedStr);
|
||||
if (checkORDCDispatchNumber(decryptedStr)) {
|
||||
return false;
|
||||
@ -305,7 +279,7 @@ public class SaleFlowHandler {
|
||||
JSONObject itemObject = itemsArray.getJSONObject(i);
|
||||
String ordc = itemObject.optString("ORDC", "").trim();
|
||||
int requestQty = safeParseInt(itemObject.optString("QTY"), 0);
|
||||
|
||||
|
||||
// 重新初始化這些變數
|
||||
MedicineName = "";
|
||||
MedicineCount = 0;
|
||||
@ -332,10 +306,10 @@ public class SaleFlowHandler {
|
||||
for (int i = 0; i < itemsArray.length(); i++) {
|
||||
JSONObject itemObject = itemsArray.getJSONObject(i);
|
||||
int requestQty = safeParseInt(itemObject.optString("QTY"), 0);
|
||||
|
||||
|
||||
if (requestQty <= medicineList.get(i).getMedicineCount()) {
|
||||
haveValuestate = true;
|
||||
|
||||
|
||||
if (requestQty > 1) {
|
||||
class TempSlot {
|
||||
int slotNo;
|
||||
@ -0,0 +1,79 @@
|
||||
package com.unibuy.smartdevice.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.unibuy.smartdevice.DialogAbstract;
|
||||
import com.unibuy.smartdevice.MyApp;
|
||||
import com.unibuy.smartdevice.structure.SlotStructure;
|
||||
import com.unibuy.smartdevice.tools.HandlerMain;
|
||||
|
||||
/**
|
||||
* 銷售流程處理器(抽象基底)。
|
||||
*
|
||||
* 三種購買流程改由雲端 B014 {@code shopping_mode} 執行期切換,取代舊 project flavor 分家:
|
||||
* <ul>
|
||||
* <li>{@code basic} → {@link BasicSaleFlow}(多品項購物車,原 General)</li>
|
||||
* <li>{@code employee_card}→ {@link EmployeeCardSaleFlow}(員工卡 B680,原 Chengwai)</li>
|
||||
* <li>{@code pickup_sheet} → {@link PickupSheetSaleFlow}(掃碼醫令自動結帳,原 Cmuh)</li>
|
||||
* </ul>
|
||||
* 由 {@link #create} 依 {@link MyApp#getShoppingMode()} 建立對應策略。
|
||||
*/
|
||||
public abstract class SaleFlowHandler {
|
||||
|
||||
protected final Context context;
|
||||
protected final SaleFlowCallback callback;
|
||||
protected final HandlerMain handlerMain;
|
||||
|
||||
protected SaleFlowHandler(Context context, HandlerMain handlerMain, SaleFlowCallback callback) {
|
||||
this.context = context;
|
||||
this.handlerMain = handlerMain;
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
/** 依雲端 shopping_mode 建立對應的銷售流程策略(預設 basic)。 */
|
||||
public static SaleFlowHandler create(Context context, HandlerMain handlerMain, SaleFlowCallback callback) {
|
||||
String mode = MyApp.getInstance().getShoppingMode();
|
||||
if ("employee_card".equals(mode)) {
|
||||
return new EmployeeCardSaleFlow(context, handlerMain, callback);
|
||||
}
|
||||
if ("pickup_sheet".equals(mode)) {
|
||||
return new PickupSheetSaleFlow(context, handlerMain, callback);
|
||||
}
|
||||
return new BasicSaleFlow(context, handlerMain, callback);
|
||||
}
|
||||
|
||||
public void onSetupUI() {
|
||||
onSetupUI(null);
|
||||
}
|
||||
|
||||
public void onSetupUI(View rootView) {
|
||||
// 預設無動作
|
||||
}
|
||||
|
||||
public void onBuyRequested() {
|
||||
// 預設無動作
|
||||
}
|
||||
|
||||
public void onShoppingCartRequested() {
|
||||
// 預設:購物車浮鈕等同進入購買流程
|
||||
onBuyRequested();
|
||||
}
|
||||
|
||||
public boolean handleProductSelected(SlotStructure slot) {
|
||||
// 預設:不消化商品點擊,交回 adapter 既有流程
|
||||
return false;
|
||||
}
|
||||
|
||||
public Class<? extends DialogAbstract> cashDialogClass() {
|
||||
return com.unibuy.smartdevice.ui.dialog.CashPayDialog.class;
|
||||
}
|
||||
|
||||
public void onBarcodeReceived(String rawData) {
|
||||
// 預設無動作
|
||||
}
|
||||
|
||||
public void onPickupRequested() {
|
||||
// 預設無動作
|
||||
}
|
||||
}
|
||||
@ -50,11 +50,10 @@ public class SystemSetActivity extends AppCompatActivityAbstract {
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
updateCheckBoxes();
|
||||
lockAllReadOnly();
|
||||
|
||||
binding.buttonBack.setOnClickListener(v -> {
|
||||
updateSystemSet();
|
||||
finish();
|
||||
});
|
||||
// 設定改以雲端 star-cloud B014 為唯一真相,主控台僅供檢視(唯讀),返回不回寫覆蓋雲端值。
|
||||
binding.buttonBack.setOnClickListener(v -> finish());
|
||||
|
||||
// IC 卡讀取功能已移除
|
||||
}
|
||||
@ -85,6 +84,32 @@ public class SystemSetActivity extends AppCompatActivityAbstract {
|
||||
binding.checkboxDevCoinC.setChecked(MyApp.getInstance().getCashSetStructure().isCoinC1());
|
||||
}
|
||||
|
||||
/** 全部設定鎖為唯讀:值由雲端 B014 下發,現場僅能檢視不可修改。 */
|
||||
private void lockAllReadOnly() {
|
||||
binding.checkboxVMC.setEnabled(false);
|
||||
binding.checkboxElectic.setEnabled(false);
|
||||
binding.checkboxShoppingCar.setEnabled(false);
|
||||
binding.checkboxInvoice.setEnabled(false);
|
||||
binding.checkboxDevNFCPay.setEnabled(false);
|
||||
binding.checkboxDevEsunPay.setEnabled(false);
|
||||
binding.checkboxDevTapPay.setEnabled(false);
|
||||
binding.checkboxDevCash.setEnabled(false);
|
||||
binding.checkboxDevLinePay.setEnabled(false);
|
||||
binding.checkboxDevTapPay30.setEnabled(false);
|
||||
binding.checkboxDevTapPay31.setEnabled(false);
|
||||
binding.checkboxDevTapPay32.setEnabled(false);
|
||||
binding.checkboxDevTapPay33.setEnabled(false);
|
||||
binding.checkboxDevTapPay34.setEnabled(false);
|
||||
|
||||
binding.checkboxDevBillF.setEnabled(false);
|
||||
binding.checkboxDevBillE.setEnabled(false);
|
||||
binding.checkboxDevBillD.setEnabled(false);
|
||||
binding.checkboxDevCoinF.setEnabled(false);
|
||||
binding.checkboxDevCoinE.setEnabled(false);
|
||||
binding.checkboxDevCoinD.setEnabled(false);
|
||||
binding.checkboxDevCoinC.setEnabled(false);
|
||||
}
|
||||
|
||||
private void updateSystemSet() {
|
||||
MyApp.getInstance().getDevSet().setVMC(binding.checkboxVMC.isChecked());
|
||||
MyApp.getInstance().getDevSet().setElectic(binding.checkboxElectic.isChecked());
|
||||
|
||||
@ -163,6 +163,11 @@ public class VmcFragment extends FragmentAbstract {
|
||||
binding.recyclerBuy.setAdapter(recyclerVmcBuyListAdpter);
|
||||
binding.recyclerBuy.setLayoutManager(new GridLayoutManager(getContext(), 3));
|
||||
|
||||
// pickup_sheet(原 Cmuh)模式:顯示大掃描單據按鈕、隱藏商品清單;其餘模式相反。
|
||||
boolean isPickupSheet = "pickup_sheet".equals(com.unibuy.smartdevice.MyApp.getInstance().getShoppingMode());
|
||||
binding.layoutScanBarcode.setVisibility(isPickupSheet ? View.VISIBLE : View.GONE);
|
||||
binding.recyclerBuy.setVisibility(isPickupSheet ? View.GONE : View.VISIBLE);
|
||||
|
||||
// for (int i = 0; i < MyApp.getInstance().getSlotList(SlotField.VMC.getField()).size(); i++) {
|
||||
// SlotStructure slot = MyApp.getInstance().getSlotList(SlotField.VMC.getField()).get(i);
|
||||
// if (!slot.getProduct().getProductID().isEmpty() && !slot.getProduct().getProductName().isEmpty() && slot.getProduct().getSellingPrice() > 0) {
|
||||
|
||||
@ -158,6 +158,11 @@ public class BuyDialog extends DialogAbstract {
|
||||
closeDialogCountdown.setCountdown(40);
|
||||
});
|
||||
|
||||
// 通行碼入口:改由雲端 B014 FunctionSet.PassCode 下發控制(DevSet.isPassCode)。
|
||||
// 關閉時隱藏「通行碼」分頁鈕,避免進入 layoutVerificationPickup 的 buttonAccessCode(flowType 5)。
|
||||
this.binding.buttonVerificationPickup.setVisibility(
|
||||
MyApp.getInstance().getDevSet().isPassCode() ? View.VISIBLE : View.GONE);
|
||||
|
||||
this.binding.buttonMemberTransactions.setOnClickListener(view -> {
|
||||
|
||||
});
|
||||
|
||||
@ -258,7 +258,7 @@ public class DispatchDialog extends DialogAbstract {
|
||||
httpAPI = new HttpAPI(getHandlerMain());
|
||||
// 初始化 Finalize Builder(在支付完成、進入出貨流程時建立)
|
||||
finalizeBuilder = new TransactionFinalizeBuilder();
|
||||
if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project)) {
|
||||
if (com.unibuy.smartdevice.AppEntry.isCmuh()) {
|
||||
MyApp.getInstance().getReportFlowInfoData().setFlowType(42);
|
||||
}
|
||||
|
||||
@ -311,7 +311,7 @@ public class DispatchDialog extends DialogAbstract {
|
||||
|
||||
getLogs().info("DispatchList size:" + this.dispatchList.size());
|
||||
|
||||
if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project)) {
|
||||
if (com.unibuy.smartdevice.AppEntry.isCmuh()) {
|
||||
try {
|
||||
String patName = "未知患者";
|
||||
String sn = "";
|
||||
@ -529,7 +529,7 @@ public class DispatchDialog extends DialogAbstract {
|
||||
R.string.check_whether_goods_have_been_taken_out_before_confirming_collection));// 請檢查商品有無取出再確認取貨
|
||||
}
|
||||
} else {
|
||||
boolean isCmuh = "Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project);
|
||||
boolean isCmuh = com.unibuy.smartdevice.AppEntry.isCmuh();
|
||||
int maxRetries = isCmuh ? 10 : 3;
|
||||
if (checkSlotRunCount >= maxRetries) {
|
||||
buttonControlCountdown.shutdown();
|
||||
@ -940,7 +940,7 @@ public class DispatchDialog extends DialogAbstract {
|
||||
isFinalizing = true;
|
||||
|
||||
getLogs().info("所有商品出貨完畢,發送 MQTT Finalize 結案訊息。");
|
||||
if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project)) {
|
||||
if (com.unibuy.smartdevice.AppEntry.isCmuh()) {
|
||||
insertDrgnoToMemo();
|
||||
}
|
||||
publishFinalizeUnlessPassCode();
|
||||
@ -1052,9 +1052,9 @@ public class DispatchDialog extends DialogAbstract {
|
||||
|
||||
addFailedDispenseRecordsFromCurrent();
|
||||
|
||||
if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project) && hasAnySuccessfulDispatch()) {
|
||||
if (com.unibuy.smartdevice.AppEntry.isCmuh() && hasAnySuccessfulDispatch()) {
|
||||
insertDrgnoToMemo();
|
||||
} else if ("Cmuh".equalsIgnoreCase(com.unibuy.smartdevice.BuildConfig.FLAVOR_project)) {
|
||||
} else if (com.unibuy.smartdevice.AppEntry.isCmuh()) {
|
||||
getLogs().info("CMUH 整單未出貨成功,不寫入 SN/DRGNO Memo,允許重新掃碼領藥");
|
||||
}
|
||||
|
||||
|
||||
@ -282,7 +282,10 @@ public class FontendPickupCodeDialog extends DialogAbstract {
|
||||
|
||||
// 進入頁面時立即檢查取貨口 (使用貨道1作為代表)
|
||||
// callback 為 null,表示檢查通過後不做額外動作,僅在檢查失敗時顯示警告
|
||||
checkSlotHaveProduct(1, 1, null);
|
||||
// pickup_sheet(原 Cmuh)流程不做取貨口檢查,維持原行為。
|
||||
if (!"pickup_sheet".equals(MyApp.getInstance().getShoppingMode())) {
|
||||
checkSlotHaveProduct(1, 1, null);
|
||||
}
|
||||
}
|
||||
|
||||
public class ReadBarCodeOnScheduler extends HandlerMainScheduler {
|
||||
@ -313,7 +316,11 @@ public class FontendPickupCodeDialog extends DialogAbstract {
|
||||
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));//收到條碼,送出交易
|
||||
|
||||
submitPickupCode(barCodeString.trim());
|
||||
if ("pickup_sheet".equals(MyApp.getInstance().getShoppingMode())) {
|
||||
submitViaMemberVerification(barCodeString.trim());
|
||||
} else {
|
||||
submitPickupCode(barCodeString.trim());
|
||||
}
|
||||
|
||||
openBarcode = false;
|
||||
}
|
||||
@ -350,6 +357,11 @@ public class FontendPickupCodeDialog extends DialogAbstract {
|
||||
|
||||
public void changeOk() {
|
||||
String number = binding.editTextNumber.getText().toString().trim();
|
||||
// pickup_sheet(原 Cmuh):取貨碼走舊 memberVerification API,維持原行為。
|
||||
if ("pickup_sheet".equals(MyApp.getInstance().getShoppingMode())) {
|
||||
submitViaMemberVerification(number);
|
||||
return;
|
||||
}
|
||||
// 長度檢查
|
||||
if (number.isEmpty() || number.length() < 8) {
|
||||
getHandlerMain().start(getClass().getSimpleName(), "取貨碼長度不能少於 8 碼");
|
||||
@ -358,6 +370,19 @@ public class FontendPickupCodeDialog extends DialogAbstract {
|
||||
submitPickupCode(number);
|
||||
}
|
||||
|
||||
/** pickup_sheet 模式:取貨碼/條碼走舊 {@code httpAPI.memberVerification}(沿用 Cmuh 行為)。 */
|
||||
private void submitViaMemberVerification(String code) {
|
||||
try {
|
||||
MyApp.getInstance().getMemberVerificationStructure().setProductId("0");
|
||||
MyApp.getInstance().getMemberVerificationStructure().setProductPrice(0);
|
||||
MyApp.getInstance().getMemberVerificationStructure().setMemberBarCode(code);
|
||||
MyApp.getInstance().getMemberVerificationStructure().setVerificationType(2);
|
||||
httpAPI.memberVerification(MyApp.getInstance().getMemberVerificationStructure());
|
||||
} catch (LogsParseException | LogsSettingEmptyException e) {
|
||||
getLogs().warning(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 取貨碼驗證改走 StarCloudAPI B660(邏輯比照員工卡 B680)。
|
||||
* 成功回傳 code_id 與要出貨的 slot_no:據此建立 BuyList、保存 code_id 供 MQTT
|
||||
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 732 B After Width: | Height: | Size: 732 B |
@ -152,6 +152,17 @@
|
||||
android:visibility="gone"
|
||||
android:textSize="28sp"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonRetryEscrow"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="200dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="#FF9800"
|
||||
android:text="嘗試退鈔並重試"
|
||||
android:visibility="gone"
|
||||
android:textSize="28sp"
|
||||
android:textColor="@color/white" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -14,4 +14,29 @@
|
||||
android:background="@color/light_gray"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<!-- pickup_sheet(原 Cmuh)模式專用:掃描單據按鈕;其餘模式隱藏(由 VmcFragment 依 shopping_mode 切換可見性)。 -->
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutScanBarcode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="500dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<Button
|
||||
android:id="@+id/scanbarcodebtn"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="400dp"
|
||||
android:layout_marginTop="-50dp"
|
||||
android:padding="10dp"
|
||||
android:text="掃描單據\n按鈕"
|
||||
android:textColor="@android:color/white"
|
||||
android:textSize="70sp"
|
||||
app:strokeColor="@color/gray"
|
||||
app:strokeWidth="5dp" />
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@ -401,4 +401,16 @@
|
||||
<string name="cmuh_pickup_failed_contact_counter">Pickup failed\nPlease contact the counter</string>
|
||||
<string name="close_40">Close(40)</string>
|
||||
<string name="close_countdown">Close(%1$d)</string>
|
||||
<!-- Multi-item shopping cart (merged from General flavor) -->
|
||||
<string name="shopping_cart_title">Shopping Cart</string>
|
||||
<string name="add_more_items">Add More Items</string>
|
||||
<string name="go_to_pay">Proceed to Checkout</string>
|
||||
<string name="cancel_button_format">Cancel (%d sec)</string>
|
||||
<string name="total_items_format">Items: (%d)</string>
|
||||
<string name="total_amount_format">NT$ %d</string>
|
||||
<string name="cart_out_of_stock">Out of Stock</string>
|
||||
<string name="cart_cannot_decrease">Cannot Decrease Further</string>
|
||||
<string name="cart_max_items">Up to 8 items per cart</string>
|
||||
<string name="cart_added">Added to cart</string>
|
||||
<string name="data_error">Data Error</string>
|
||||
</resources>
|
||||
|
||||
@ -403,4 +403,16 @@
|
||||
<string name="cmuh_pickup_failed_contact_counter">受け取り失敗\nカウンターへお問い合わせください</string>
|
||||
<string name="close_40">閉じる(40)</string>
|
||||
<string name="close_countdown">閉じる(%1$d)</string>
|
||||
<!-- 多品項購物車(merged from General flavor) -->
|
||||
<string name="shopping_cart_title">ショッピングカート</string>
|
||||
<string name="add_more_items">商品を追加</string>
|
||||
<string name="go_to_pay">お会計へ進む</string>
|
||||
<string name="cancel_button_format">キャンセル(%d秒)</string>
|
||||
<string name="total_items_format">商品数:(%d)</string>
|
||||
<string name="total_amount_format">NT$ %d</string>
|
||||
<string name="cart_out_of_stock">在庫切れ</string>
|
||||
<string name="cart_cannot_decrease">これ以上減らせません</string>
|
||||
<string name="cart_max_items">カートは最大8点まで購入できます</string>
|
||||
<string name="cart_added">カートに追加しました</string>
|
||||
<string name="data_error">データ異常</string>
|
||||
</resources>
|
||||
|
||||
@ -1,25 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
機台功能開關 DevSet 的「初始預設值」(首次開機、DB 尚無設定時套用)。
|
||||
各 flavor 可於自身 res 覆寫;main 此處維持原本寫死的預設,確保 Chengwai/Cmuh 行為不變。
|
||||
實機上由主控台「系統設定」寫入後存 DB,之後以 DB 值為準。
|
||||
專案維度已收斂為單一 App + 執行期 shopping_mode(雲端 B014 下發),此處為「雲端配好前」的
|
||||
bootstrap 預設,採 basic(多品項購物車)體驗;雲端 B014 一到即覆蓋。
|
||||
-->
|
||||
<resources>
|
||||
<bool name="devset_default_vmc">true</bool>
|
||||
<bool name="devset_default_electric">false</bool>
|
||||
<bool name="devset_default_shopping_cart">false</bool>
|
||||
<bool name="devset_default_shopping_cart">true</bool>
|
||||
<bool name="devset_default_invoice">true</bool>
|
||||
<bool name="devset_default_nfc_pay">true</bool>
|
||||
<bool name="devset_default_esun_pay">true</bool>
|
||||
<bool name="devset_default_tap_pay">true</bool>
|
||||
<bool name="devset_default_cash">false</bool>
|
||||
<bool name="devset_default_line_pay">false</bool>
|
||||
<bool name="devset_default_cash">true</bool>
|
||||
<bool name="devset_default_line_pay">true</bool>
|
||||
<bool name="devset_default_tap_pay_30">true</bool>
|
||||
<bool name="devset_default_tap_pay_31">true</bool>
|
||||
<bool name="devset_default_tap_pay_32">true</bool>
|
||||
<bool name="devset_default_tap_pay_33">true</bool>
|
||||
<bool name="devset_default_tap_pay_34">true</bool>
|
||||
<!-- 取貨碼/通行碼:改由雲端 B014 FunctionSet 下發控制;bootstrap 預設開啟,雲端一到即覆蓋 -->
|
||||
<bool name="devset_default_pickup_code">true</bool>
|
||||
<bool name="devset_default_pass_code">true</bool>
|
||||
|
||||
<!-- 前台「取貨碼」浮鈕是否顯示(各 flavor 可覆寫;main 預設隱藏,維持原行為) -->
|
||||
<bool name="show_pickup_code_button">false</bool>
|
||||
<!-- 前台「取貨碼」浮鈕舊資源(已改吃 DevSet.isPickupCode,保留避免殘留引用編譯失敗) -->
|
||||
<bool name="show_pickup_code_button">true</bool>
|
||||
</resources>
|
||||
|
||||
@ -320,4 +320,16 @@
|
||||
<string name="cmuh_pickup_failed_contact_counter">取物失敗\n請洽櫃台</string>
|
||||
<string name="close_40">關閉(40)</string>
|
||||
<string name="close_countdown">關閉(%1$d)</string>
|
||||
<!-- 多品項購物車(原 General flavor strings,合併進 main) -->
|
||||
<string name="shopping_cart_title">購物車</string>
|
||||
<string name="add_more_items">加購商品</string>
|
||||
<string name="go_to_pay">前往結帳</string>
|
||||
<string name="cancel_button_format">取消(%d秒)</string>
|
||||
<string name="total_items_format">商品數量:(%d)</string>
|
||||
<string name="total_amount_format">NT$ %d</string>
|
||||
<string name="cart_out_of_stock">超出庫存</string>
|
||||
<string name="cart_cannot_decrease">不能減少了</string>
|
||||
<string name="cart_max_items">購物車最多可購買 8 件商品</string>
|
||||
<string name="cart_added">已加入購物車</string>
|
||||
<string name="data_error">數據異常</string>
|
||||
</resources>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user