[REFACTOR] 重構銷售流程至雙維度 Flavor 架構、統一忙碌狀態並修正版本規範
1. 引進 SaleFlowHandler 與 SaleFlowCallback 介面,分離通用版 (General)、晟崴版 (Chengwai) 與中國醫版 (Cmuh) 的前台銷售進入點,降低 FontendActivity 耦合度。 2. 建立 src/Chengwai/、src/Cmuh/、src/General/ 目錄,將客製 dialog(如 FontendPickupCodeDialog、TakeMedicineDialog)與 layout 移入對應的專屬目錄。 3. 統一狀態檢查機制,將全專案背景排程、定時重啟與日誌備份中的舊 isShowDialog() 替換為更安全的 isMachineBusy();於 NFC 交易與鍵盤 Enter 防誤觸中特意保留原 isShowDialog() 以守護安全性防線。 4. 修正英文 values-en/strings.xml 缺漏與預設 locale 對齊的 sync_product/sync_product_warning,解決 lintVital 報告的 ExtraTranslation 錯誤,使 Release 打包 100% 成功。 5. 微調 app/build.gradle,將起點版號重置為 10_01_1,自動連動 versionCode 100101 與 versionName 10_01_1_R。 6. 修正 .agents/rules/versioning.md 規範,將 S 修正為「來源 (Source)」,將 verMajor 修正為「螢幕大小」,並補齊官方命名規範對照圖表與欄位解析。 7. 升級一鍵編譯 /compiler-apk(新增中文與英文別名智慧防呆映射)與新建發版 /release-apk 工作流。
This commit is contained in:
parent
6b3e05051f
commit
3409f93f07
@ -13,7 +13,7 @@ trigger: always_on
|
||||
> [!IMPORTANT]
|
||||
> **開發人員與 AI 助手在修改版號時,嚴禁直接寫死 `versionCode` 或 `versionName`,必須透過修改 Gradle 頂部的變數來進行。**
|
||||
|
||||
* `verMajor` (大版號):代表重大架構變更或大型系統改版(例如 `10`)。
|
||||
* **`verMajor` (螢幕大小)**:代表實體機台的螢幕吋數(例如 `10` 代表 10 吋螢幕)。
|
||||
* `verMinor` (次版號):代表新增商業功能、硬體支援,但向下相容(例如 `8`)。
|
||||
* `verPatch` (修補版號):代表 Bug 修復、小優化(例如 `5`)。
|
||||
|
||||
@ -23,7 +23,29 @@ trigger: always_on
|
||||
|
||||
---
|
||||
|
||||
## 2. 實體機台 OTA 自動更新鐵律
|
||||
## 2. 公司官方命名規範對照
|
||||
|
||||
依據公司標準規範,實體機台發佈之 APK 與版本名稱需嚴格符合以下欄位對照:
|
||||
|
||||
```
|
||||
來源 _ 安卓號 _ 主板號 _ 機器系列 _ 專案名稱 _ 螢幕大小 _ 次版號 _ 修補版號 _ 環境
|
||||
S _ 12 _ XY _ U _ Chengwai _ 10 _ 08 _ 8 _ R
|
||||
```
|
||||
|
||||
### 欄位解析:
|
||||
* **`S` (來源 / Source)**:代表程式碼與專案來源(**非螢幕尺寸**)。
|
||||
* **`12` (安卓號)**:Android 系統主版本號。
|
||||
* **`XY` (主板號)**:主板型號規格。
|
||||
* **`U` (機器系列 / 機型規格)**:例如代表 U 系列機型。
|
||||
* **`Chengwai` (專案名稱)**:客戶/銷售場景專案代碼(如 `General`, `Chengwai`, `Cmuh`)。
|
||||
* **`10` (螢幕大小)**:對應 Gradle 中的 `verMajor`。
|
||||
* **`08` (次版號)**:對應 Gradle 中的 `verMinor`(格式化為雙位數)。
|
||||
* **`8` (修補版號)**:對應 Gradle 中的 `verPatch`。
|
||||
* **`R` (環境)**:環境代碼,例如 `R` 代表 Release 生產環境。
|
||||
|
||||
---
|
||||
|
||||
## 3. 實體機台 OTA 自動更新鐵律
|
||||
|
||||
因為本 App 部署於實體販賣機,更新主要透過後台進行遠端自動下載覆蓋安裝 (OTA)。
|
||||
|
||||
@ -33,18 +55,34 @@ trigger: always_on
|
||||
|
||||
---
|
||||
|
||||
## 3. 機台套件與產品風味 (Product Flavors) 命名定義
|
||||
## 4. 雙維度產品風味 (Product Flavors) 與 APK 命名規範
|
||||
|
||||
* **命名結構**:`[螢幕尺寸]_[Android版本]_[主板與機型規格]`
|
||||
* **現行標準 Flavor**:`S_12_XY_U_Standard_`
|
||||
* `S`:代表 Small 尺寸螢幕(一般 S 號)。
|
||||
* `12`:代表針對 Android 12 系統優化之版本。
|
||||
* `XY_U_Standard_`:代表 XY 主板、U 規格標準版。
|
||||
* **擴充原則**:未來引進新硬體時,必須新增 Flavor 並遵循此命名格式(例如:`M_12_XY_U_Standard_`)。
|
||||
本專案採用**雙維度 Flavor** 架構,以完美分離「客戶銷售場景」與「底層硬體規格」,方便後續快速擴充。
|
||||
|
||||
### 4.1 雙維度定義 (flavorDimensions)
|
||||
本專案於 `app/build.gradle` 中定義了兩個 Flavor 維度:
|
||||
|
||||
1. **`project` 維度 (客戶/銷售場景區隔)**:
|
||||
* **`General`** (通用主幹):預設顯示付款方式選單(彈出 `BuyDialog`)。未來新客戶最常用此通用主幹。
|
||||
* **`Chengwai`** (晟崴客製):直接跳出員工卡刷卡畫面(彈出 `MemberCardPickupDialog`),不顯示付款選單。
|
||||
* **`Cmuh`** (中國醫客製):支援 QR Code 掃碼、處方籤醫令離線 AES 解密與解析領藥流程。
|
||||
2. **`hardware` 維度 (硬體與系統規格區隔)**:
|
||||
* **`S12XYU`**:代表 `S` (來源) + `12` (安卓號) + `XY` (主板號) + `U` (機器系列/機型規格)。
|
||||
|
||||
### 4.2 實體機台 APK 命名鐵律
|
||||
為了符合實體機台 OTA 平台發佈規範,打包輸出的 APK 檔名必須嚴格連動雙維度,並遵循以下格式:
|
||||
`app-[格式化硬體規格]_[專案名稱]-[版本號]-[BuildType].apk`
|
||||
|
||||
* **格式化規則**:`S12XYU` 會在 Gradle 打包時被自動展開格式化為 `S_12_XY_U`。
|
||||
* **輸出檔名範例**:
|
||||
* `app-S_12_XY_U_General-10_08_8_R-release.apk`
|
||||
* `app-S_12_XY_U_Chengwai-10_08_8_R-release.apk`
|
||||
* `app-S_12_XY_U_Cmuh-10_08_8_R-release.apk`
|
||||
* **注意**:此檔名轉換規則已完全由 `app/build.gradle` 頂部 Gradle 變數與 `applicationVariants.all` 自動連動生成,**嚴禁手動修改輸出檔名**。
|
||||
|
||||
---
|
||||
|
||||
## 4. Git 發版配合流程
|
||||
## 5. Git 發版配合流程
|
||||
|
||||
1. 自 `develop` 拉出 `release/10.8.5` 發版分支。
|
||||
2. 遞增 `app/build.gradle` 中的 `verPatch` 變數並 Commit。
|
||||
|
||||
@ -6,6 +6,12 @@ description: 自動編譯 Android APK、輸出至專屬目錄並安裝至實體
|
||||
|
||||
這個 Workflow 主要是協助您一鍵完成 Android 專案的編譯、匯出與安裝。當您呼叫 `/compiler-apk` 時,AI 會自動為您執行以下步驟:
|
||||
|
||||
> [!TIP]
|
||||
> **支援參數化指定編譯變體:**
|
||||
> * 預設(不帶參數):編譯 **`Chengwai` (晟崴客製版)**。
|
||||
> * 您可以呼叫 `/compiler-apk General`(或 `general`)編譯 **`General` (通用版)**。
|
||||
> * 您可以呼叫 `/compiler-apk Cmuh`(或 `cmuh`)編譯 **`Cmuh` (中國醫客製版)**。
|
||||
|
||||
## 步驟零:自動檢查變更與升級版號
|
||||
在開始編譯之前,AI 助手會自動檢查是否有程式碼變更,以落實實體機台的 OTA 升級規範:
|
||||
1. AI 助手將檢查 `git status --porcelain app/` 的輸出,以偵測是否有未提交的程式碼修改。
|
||||
@ -13,7 +19,7 @@ description: 自動編譯 Android APK、輸出至專屬目錄並安裝至實體
|
||||
3. 遞增完成後,向使用者呈報最新的版本號資訊,接著繼續執行步驟一。
|
||||
|
||||
## 步驟一:設定環境並編譯 Debug APK
|
||||
使用 Java 17 與 Gradle 編譯專屬 Flavor (`S_12_XY_U_Standard_Debug`) 的程式碼。
|
||||
使用 Java 17 與 Gradle 編譯對應 Flavor 與硬體規格的 Debug 程式碼。
|
||||
|
||||
```bash
|
||||
// turbo
|
||||
@ -23,9 +29,37 @@ description: 自動編譯 Android APK、輸出至專屬目錄並安裝至實體
|
||||
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
|
||||
cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai
|
||||
|
||||
# 取得目標 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
|
||||
# 若非預設之中文別名,則嘗試做首字母大寫轉換(相容英文輸入,如 cmuh -> Cmuh)
|
||||
TARGET_FLAVOR=$(echo "$RAW_INPUT" | awk '{print toupper(substr($0,1,1))tolower(substr($0,2))}')
|
||||
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}S12XYUDebug"
|
||||
|
||||
# 使用 clean 確保完整重新編譯,避免舊快取遮蔽 import 缺失等錯誤
|
||||
# --no-daemon:強制使用單次 JVM 進程,避免 Daemon 初始化期間被殘留訊號中斷
|
||||
./gradlew clean :app:assembleS_12_XY_U_Standard_Debug --no-daemon
|
||||
./gradlew clean :app:assemble${TARGET_FLAVOR}S12XYUDebug --no-daemon
|
||||
```
|
||||
|
||||
## 步驟二:導出 APK 到 outputs 資料夾
|
||||
@ -35,8 +69,26 @@ cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai
|
||||
// turbo
|
||||
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
|
||||
|
||||
# 動態尋找編譯產出的最新 APK 檔案,避免因版號變更導致寫死路徑失效
|
||||
APK_SOURCE=$(find app/build/outputs/apk/S_12_XY_U_Standard_/debug/ -name "app-S_12_XY_U_Standard_-*-debug.apk" | head -n 1)
|
||||
APK_SOURCE=$(find app/build/outputs/apk/${TARGET_FLAVOR}S12XYU/debug/ -name "app-S_12_XY_U_${TARGET_FLAVOR}-*-debug.apk" | head -n 1)
|
||||
|
||||
if [ -n "$APK_SOURCE" ]; then
|
||||
APK_NAME=$(basename "$APK_SOURCE")
|
||||
@ -50,7 +102,7 @@ if [ -n "$APK_SOURCE" ]; then
|
||||
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="S_12_XY_U_Standard_"
|
||||
FLAVOR="${TARGET_FLAVOR}S12XYU"
|
||||
|
||||
echo "=================================================="
|
||||
echo "📢 後台更新 keyin 參考資料:"
|
||||
@ -73,12 +125,29 @@ 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
|
||||
|
||||
# 檢查是否有設備連接
|
||||
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-S_12_XY_U_Standard_-*-debug.apk" | head -n 1)
|
||||
APK_TARGET=$(find outputs/ -name "app-S_12_XY_U_${TARGET_FLAVOR}-*-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..."
|
||||
|
||||
117
.agents/workflows/release-apk.md
Normal file
117
.agents/workflows/release-apk.md
Normal file
@ -0,0 +1,117 @@
|
||||
---
|
||||
description: 自動編譯正式發行 Release APK、升級 Patch 版本號、並產出公司 OTA 平台必備之 Keyin 參考資料
|
||||
---
|
||||
|
||||
# 生產環境發版打包工作流 (release-apk)
|
||||
|
||||
這個 Workflow 專門為應用程式「生產環境上線/發版」設計。當您準備好將特定客戶的最新功能派送至實體販賣機時,呼叫 `/release-apk` 將自動為您完成嚴格的發行檢查、版號遞增、Release 簽章打包與發版資訊生成。
|
||||
|
||||
> [!TIP]
|
||||
> **支援參數化指定編譯變體:**
|
||||
> * 預設(不帶參數):打包 **`Chengwai` (晟崴客製正式版)**。
|
||||
> * 您可以呼叫 `/release-apk General`(或 `general`)打包 **`General` (通用正式版)**。
|
||||
> * 您可以呼叫 `/release-apk Cmuh`(或 `cmuh`)打包 **`Cmuh` (中國醫客製正式版)**。
|
||||
|
||||
---
|
||||
|
||||
## 步驟零:發行檢查與版號自動遞增 (OTA 鐵律)
|
||||
為了確保實體機台能夠順利觸發 OTA 自動下載覆蓋安裝,新編譯的 `versionCode` 必須嚴格大於現場的舊版號:
|
||||
1. AI 助手將讀取 `app/build.gradle` 並自動將 `verPatch`(修補版號)**遞增 1**(例如從 `8` 自動升級為 `9`),確保 versionCode 順利變大。
|
||||
2. 自動在 terminal 呈報即將打包的最新版本號資訊。
|
||||
|
||||
## 步驟一:清理快取並進行正式簽章編譯
|
||||
採用單次 JVM 進程強制重新編譯,以確保代碼 100% 乾淨,並執行 Release 模式下的**代碼優化、混淆、與嚴格的 `lintVital` 審查**。
|
||||
|
||||
```bash
|
||||
// turbo
|
||||
# 終止殘留的 Gradle Daemon 程序,避免多個 Daemon 互搶資源,同時避免誤殺 VS Code Gradle 擴充功能
|
||||
./gradlew --stop 2>/dev/null; pkill -9 -f "org.gradle.launcher.daemon.bootstrap.GradleDaemon" 2>/dev/null; sleep 3
|
||||
|
||||
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))}')
|
||||
fi
|
||||
|
||||
# 安全防護:檢查是否為支援的客製項目
|
||||
if [ "$TARGET_FLAVOR" != "General" ] && [ "$TARGET_FLAVOR" != "Chengwai" ] && [ "$TARGET_FLAVOR" != "Cmuh" ]; then
|
||||
echo "❌ 不支援的 Flavor: $TARGET_FLAVOR,請使用 General, Chengwai 或 Cmuh。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "📦 [Release] 開始編譯正式版變體: ${TARGET_FLAVOR}S12XYURelease"
|
||||
|
||||
# 使用 clean 確保完整重新編譯,避開 incremental 快取問題
|
||||
./gradlew clean :app:assemble${TARGET_FLAVOR}S12XYURelease --no-daemon
|
||||
```
|
||||
|
||||
## 步驟二:導出正式 APK 並產出後台 Keyin 派送參考資料
|
||||
將封裝完畢、具備正式簽章的 APK 導出至最外層 `outputs/` 資料夾,並自動計算產出公司 OTA 派送後台所需的全部對照資料。
|
||||
|
||||
```bash
|
||||
// turbo
|
||||
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))}')
|
||||
fi
|
||||
|
||||
# 動態尋找編譯產出的最新正式版 APK 檔案
|
||||
APK_SOURCE=$(find app/build/outputs/apk/${TARGET_FLAVOR}S12XYU/release/ -name "app-S_12_XY_U_${TARGET_FLAVOR}-*-release.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 "=================================================="
|
||||
echo "✅ 正式版 APK 封裝成功,已導出至:$APK_DEST"
|
||||
echo "=================================================="
|
||||
|
||||
# 提取與計算版本資訊
|
||||
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}')
|
||||
VERSION_NAME="${MAJOR}_0${MINOR}_${PATCH}_R"
|
||||
VERSION_CODE=$((MAJOR * 10000 + MINOR * 100 + PATCH))
|
||||
FLAVOR="${TARGET_FLAVOR}S12XYU"
|
||||
|
||||
echo "🚀 實體販賣機 OTA 後台新增版本 Keyin 參考資料:"
|
||||
echo "--------------------------------------------------"
|
||||
echo "1. 專案/客戶風味 (Flavor) : $FLAVOR"
|
||||
echo "2. 版本代碼 (versionCode) : $VERSION_CODE"
|
||||
echo "3. 版本名稱 (versionName) : $VERSION_NAME"
|
||||
echo "4. 螢幕吋數 (Screen Size) : ${MAJOR} 吋"
|
||||
echo "5. 正式版 APK 檔名 : $APK_NAME"
|
||||
echo "--------------------------------------------------"
|
||||
echo "📢 [溫馨提醒] 請將此 APK 上傳至 OTA 平台,並設定為對應機型的強迫升級。"
|
||||
echo "=================================================="
|
||||
else
|
||||
echo "❌ 找不到編譯產出的正式版 APK 檔案,封裝失敗!"
|
||||
exit 1
|
||||
fi
|
||||
```
|
||||
|
||||
## 步驟三:自動化 Git 發版標記引導
|
||||
為了落實公司發版規範第 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}"
|
||||
|
||||
echo "=================================================="
|
||||
echo "🔔 Git 發版追溯標記指南"
|
||||
echo "--------------------------------------------------"
|
||||
echo "請在專案合併回 main 分支後,執行以下指令打上發版 Tag:"
|
||||
echo "git tag -a $TAG_NAME -m \"TaiwanStar 發版 - 版本 $TAG_NAME\""
|
||||
echo "git push origin $TAG_NAME"
|
||||
echo "=================================================="
|
||||
```
|
||||
@ -9,8 +9,8 @@ plugins {
|
||||
// versionCode 就會自動變大為 100806,以正確觸發實體機台的 OTA 自動更新。
|
||||
// =========================================================================
|
||||
def verMajor = 10
|
||||
def verMinor = 8
|
||||
def verPatch = 8
|
||||
def verMinor = 1
|
||||
def verPatch = 1
|
||||
|
||||
android {
|
||||
namespace 'com.unibuy.smartdevice'
|
||||
@ -26,14 +26,26 @@ android {
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
// 設定不同場域
|
||||
flavorDimensions "app_type"
|
||||
// =====================================================================
|
||||
// 雙維度 Flavor:project(客戶專案)× hardware(硬體規格)
|
||||
// =====================================================================
|
||||
flavorDimensions "project", "hardware"
|
||||
|
||||
productFlavors {
|
||||
S_12_XY_U_Standard_ {
|
||||
dimension "app_type"
|
||||
versionCode verMajor * 10000 + verMinor * 100 + verPatch
|
||||
versionName "${verMajor}_0${verMinor}_${verPatch}_R"
|
||||
General {
|
||||
dimension "project"
|
||||
}
|
||||
|
||||
Chengwai {
|
||||
dimension "project"
|
||||
}
|
||||
|
||||
Cmuh {
|
||||
dimension "project"
|
||||
}
|
||||
|
||||
S12XYU {
|
||||
dimension "hardware"
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,16 +57,20 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
// 自定義打包檔案名稱,包含版本號
|
||||
// 自定義打包檔案名稱,遵循公司命名規範:來源_安卓號_主板號_機器系列_專案名稱_螢幕大小_次版號_修補版號_環境
|
||||
applicationVariants.all { variant ->
|
||||
variant.outputs.each { output ->
|
||||
def versionCode = variant.versionCode
|
||||
def versionName = variant.versionName
|
||||
def flavor = variant.productFlavors[0].name
|
||||
def project = variant.productFlavors[0].name // project dimension
|
||||
def hardware = variant.productFlavors[1].name // hardware dimension
|
||||
def buildType = variant.buildType.name
|
||||
|
||||
// 設定打包檔案名稱:app-<flavor>-<versionName>-<buildType>.apk
|
||||
output.outputFileName = "app-${flavor}-${versionName}-${buildType}.apk"
|
||||
// 將 hardware flavor 名稱展開為公司規範格式 S12XYU → S_12_XY_U
|
||||
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}-${versionName}-${buildType}.apk"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
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 onBuyRequested() {
|
||||
new MemberCardPickupDialog(context, handlerMain).show();
|
||||
}
|
||||
|
||||
public void onBarcodeReceived(String rawData) {
|
||||
// 晟崴版不處理掃碼
|
||||
}
|
||||
|
||||
public void onPickupRequested() {
|
||||
// 晟崴版不處理取貨碼
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package com.unibuy.smartdevice.structure;
|
||||
|
||||
public class MedicineStructure {
|
||||
|
||||
String MaterialCode;
|
||||
int MedicineCount;
|
||||
String MedicineName;
|
||||
String MedicineSlot;
|
||||
String MedicineImg;
|
||||
String productId;
|
||||
String MemoSnDrgno;
|
||||
|
||||
// 建構子
|
||||
public MedicineStructure(String MaterialCode, int MedicineCount, String MedicineName, String MedicineSlot, String MedicineImg, String productId, String MemoSnDrgno) {
|
||||
this.MaterialCode = MaterialCode;
|
||||
this.MedicineCount = MedicineCount;
|
||||
this.MedicineName = MedicineName;
|
||||
this.MedicineSlot = MedicineSlot;
|
||||
this.MedicineImg = MedicineImg;
|
||||
this.productId = productId;
|
||||
this.MemoSnDrgno = MemoSnDrgno;
|
||||
}
|
||||
|
||||
public String getMaterialCode() {
|
||||
return MaterialCode;
|
||||
}
|
||||
|
||||
public void setMaterialCode(String MaterialCode) {
|
||||
this.MaterialCode = MaterialCode;
|
||||
}
|
||||
|
||||
public int getMedicineCount() {
|
||||
return MedicineCount;
|
||||
}
|
||||
|
||||
public void setMedicineCount(int MedicineCount) {
|
||||
this.MedicineCount = MedicineCount;
|
||||
}
|
||||
|
||||
public String getMedicineName() {
|
||||
return MedicineName;
|
||||
}
|
||||
|
||||
public void setMedicineName(String MedicineName) {
|
||||
this.MedicineName = MedicineName;
|
||||
}
|
||||
|
||||
public String getMedicineSlot() {
|
||||
return MedicineSlot;
|
||||
}
|
||||
|
||||
public void setMedicineSlot(String MedicineSlot) {
|
||||
this.MedicineSlot = MedicineSlot;
|
||||
}
|
||||
|
||||
public String getMedicineImg() {
|
||||
return MedicineImg;
|
||||
}
|
||||
|
||||
public void setMedicineImg(String MedicineImg) {
|
||||
this.MedicineImg = MedicineImg;
|
||||
}
|
||||
|
||||
public String getProductId() {
|
||||
return productId;
|
||||
}
|
||||
|
||||
public void setProductId(String productId) {
|
||||
this.productId = productId;
|
||||
}
|
||||
|
||||
public String getMemoSnDrgno() {
|
||||
return MemoSnDrgno;
|
||||
}
|
||||
|
||||
public void setMemoSnDrgno(String MemoSnDrgno) {
|
||||
this.MemoSnDrgno = MemoSnDrgno;
|
||||
}
|
||||
}
|
||||
387
app/src/Cmuh/java/com/unibuy/smartdevice/ui/SaleFlowHandler.java
Normal file
387
app/src/Cmuh/java/com/unibuy/smartdevice/ui/SaleFlowHandler.java
Normal file
@ -0,0 +1,387 @@
|
||||
package com.unibuy.smartdevice.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.util.Base64;
|
||||
import android.view.View;
|
||||
import com.unibuy.smartdevice.MyApp;
|
||||
import com.unibuy.smartdevice.R;
|
||||
import com.unibuy.smartdevice.devices.DeviceType;
|
||||
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.LogsUnsupportedOperationException;
|
||||
import com.unibuy.smartdevice.structure.BuyStructure;
|
||||
import com.unibuy.smartdevice.structure.MedicineStructure;
|
||||
import com.unibuy.smartdevice.structure.ProductStructure;
|
||||
import com.unibuy.smartdevice.tools.HandlerMain;
|
||||
import com.unibuy.smartdevice.tools.HandlerMainSchedulerOnClick;
|
||||
import com.unibuy.smartdevice.ui.dialog.FontendPickupCodeDialog;
|
||||
import com.unibuy.smartdevice.ui.dialog.PickupErrorTipDialog;
|
||||
import com.unibuy.smartdevice.ui.dialog.TakeMedicineDialog;
|
||||
import com.unibuy.smartdevice.ui.devs.vmc.VmcFragment;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* Cmuh (中國醫版) 銷售流程處理器。
|
||||
* 負責:
|
||||
* 1. 綁定「40秒內刷條碼」按鈕 (scanbarcodebtn)
|
||||
* 2. 連接 USB 掃描器背景讀取
|
||||
* 3. 解密與解析醫令 (AES 128 ECB)
|
||||
* 4. 庫存核對與自動加入購物車
|
||||
* 5. 顯示領藥與錯誤 Dialog
|
||||
*/
|
||||
public class SaleFlowHandler {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 在 Activity 的 onResume() 中會被呼叫,此時尋找並綁定 VmcFragment 中的掃碼按鈕
|
||||
*/
|
||||
public void onSetupUI() {
|
||||
if (context instanceof Activity) {
|
||||
View btnScan = ((Activity) context).findViewById(R.id.scanbarcodebtn);
|
||||
if (btnScan != null) {
|
||||
btnScan.setOnClickListener(new BtnQrCodeOnClick(handlerMain, context));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void onBuyRequested() {
|
||||
// 中國醫版沒有購物車付款,此方法不執行
|
||||
}
|
||||
|
||||
public void onBarcodeReceived(String rawData) {
|
||||
// 若是經由其他方式傳入的 Barcode 可在此處直接處理
|
||||
try {
|
||||
barcodeAESDecryption(rawData);
|
||||
} catch (Exception e) {
|
||||
if (callback != null) {
|
||||
callback.onSaleFlowError("掃碼處理失敗:" + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* CMUH 點選取貨按鈕,開啟輸入取貨碼的 Dialog
|
||||
*/
|
||||
public void onPickupRequested() {
|
||||
new FontendPickupCodeDialog(context, handlerMain).show();
|
||||
}
|
||||
|
||||
// ── 內部掃碼 Button Click 監聽器與背景 Task ─────────────────────────────
|
||||
|
||||
private class BtnQrCodeOnClick extends HandlerMainSchedulerOnClick {
|
||||
private final Context context;
|
||||
|
||||
public BtnQrCodeOnClick(HandlerMain srcHandlerMain, Context context) {
|
||||
super(srcHandlerMain);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandlerMain setHandlerMain() {
|
||||
return getSrcHandlerMain();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(HandlerMain handlerMain) {
|
||||
handlerMain.start(getClass().getSimpleName(), "@40秒內刷條碼");
|
||||
openBarcode = true;
|
||||
try {
|
||||
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 秒
|
||||
if (size > 5) {
|
||||
if (!openBarcode) continue;
|
||||
openBarcode = false; // 立即標記,防止重複進入解碼邏輯
|
||||
MyApp.getInstance().getLogs().info("✅ 觸發掃碼源頭防呆,此筆掃碼僅處理一次");
|
||||
|
||||
String barCodeString = new String(buffer, 0, size).trim();
|
||||
if (barCodeString.isEmpty()) {
|
||||
openBarcode = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
MyApp.getInstance().getLogs().info("掃描成功: [" + barCodeString + "]");
|
||||
|
||||
try {
|
||||
barcodeAESDecryption(barCodeString);
|
||||
} catch (LogsParseException e) {
|
||||
MyApp.getInstance().getLogs().warning(e);
|
||||
handlerMain.send(getClass().getSimpleName(), VmcFragment.Option.DECRYPTION_ERROR.getOption(), e.getLocalizedMessage());
|
||||
openBarcode = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (haveValuestate) {
|
||||
((Activity) context).runOnUiThread(() -> new TakeMedicineDialog(context, handlerMain).show());
|
||||
} else {
|
||||
((Activity) context).runOnUiThread(() -> new PickupErrorTipDialog(context, handlerMain).show());
|
||||
openBarcode = true;
|
||||
}
|
||||
break; // 處理完成,跳出掃描迴圈
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
usbDev.closePort();
|
||||
} catch (LogsUnsupportedOperationException | LogsIOException | LogsEmptyException e) {
|
||||
MyApp.getInstance().getLogs().warning(e);
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (System.currentTimeMillis() - lastClickTime < 3000) {
|
||||
MyApp.getInstance().getLogs().info("⚠️ 點擊太快,觸發防呆");
|
||||
return;
|
||||
}
|
||||
lastClickTime = System.currentTimeMillis();
|
||||
|
||||
setRun(true);
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(View v) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> setCls() {
|
||||
return getClass();
|
||||
}
|
||||
}
|
||||
|
||||
// ── 醫令 AES 解密與解析庫存邏輯 ───────────────────────────────────────
|
||||
|
||||
private boolean barcodeAESDecryption(String scanBarcode) throws JSONException, LogsParseException {
|
||||
try {
|
||||
String keyString = "cmuhch@mis@drug1"; // 16字節的 key
|
||||
String encryptedBase64 = scanBarcode;
|
||||
|
||||
if (encryptedBase64 == null || encryptedBase64.isEmpty()) {
|
||||
throw new IllegalArgumentException("錯誤:加密數據為 null 或空,無法解密");
|
||||
}
|
||||
byte[] encryptedBytes = Base64.decode(encryptedBase64, Base64.DEFAULT);
|
||||
if (encryptedBytes == null || encryptedBytes.length == 0) {
|
||||
throw new IllegalArgumentException("錯誤:Base64 解碼後的數據為 null 或空");
|
||||
}
|
||||
|
||||
if (encryptedBytes.length % 16 != 0) {
|
||||
throw new IllegalArgumentException("錯誤:加密數據長度不是 16 的倍數,可能需要填充");
|
||||
}
|
||||
|
||||
byte[] keyBytes = keyString.getBytes(StandardCharsets.UTF_8);
|
||||
SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
|
||||
|
||||
Cipher cipher = Cipher.getInstance("AES/ECB/NoPadding"); // 無填充模式
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey);
|
||||
|
||||
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
|
||||
|
||||
MyApp.getInstance().setDecryptedData("");
|
||||
MyApp.getInstance().setDecryptedData(new String(decryptedBytes, StandardCharsets.UTF_8).trim());
|
||||
|
||||
String decryptedStr = MyApp.getInstance().getDecryptedData();
|
||||
MyApp.getInstance().getLogs().info("decryptedStr=" + decryptedStr);
|
||||
|
||||
JSONObject jsonObject = new JSONObject(decryptedStr);
|
||||
addMedicineToByulist(jsonObject);
|
||||
checkORDCDispatchNumber(decryptedStr); // 判斷領藥號是否已經領過
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new LogsParseException(MyApp.getInstance().getLogs(), "錯誤:加密數據長度不是 16 的倍數,可能需要填充");
|
||||
}
|
||||
return haveValuestate;
|
||||
}
|
||||
|
||||
public void addMedicineToByulist(JSONObject jsonObject) {
|
||||
JSONArray itemsArray = null;
|
||||
try {
|
||||
itemsArray = jsonObject.getJSONArray("ITEM");
|
||||
// 清空 BuyList,確保每次掃描都是新的出貨清單
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
|
||||
int MedicineCount = 0; // 同一種商品全部貨道的總數量
|
||||
String MedicineName = "", MedicineSlot = "", MedicineImg = "", MedicinePId = "", MemoSnDrgno = "";
|
||||
ArrayList<MedicineStructure> medicineList = new ArrayList<>();
|
||||
|
||||
// 先進行預判,確認所有 ITEM 都能成功出貨才繼續後面流程
|
||||
boolean stockCheckPassed = true; // 預設檢查通過
|
||||
|
||||
for (int i = 0; i < itemsArray.length(); i++) {
|
||||
JSONObject itemObject = itemsArray.getJSONObject(i);
|
||||
String ordc = itemObject.optString("ORDC");
|
||||
int requestQty = Integer.parseInt(itemObject.optString("QTY"));
|
||||
int totalStock = 0;
|
||||
|
||||
for (int j = 0; j < MyApp.getInstance().getSlotList(0).size(); j++) {
|
||||
if (ordc.equals(MyApp.getInstance().getSlotList(0).get(j).getProduct().getMaterialCode())
|
||||
&& MyApp.getInstance().getSlotList(0).get(j).getCount() > 0
|
||||
&& MyApp.getInstance().getSlotList(0).get(j).getCount() <= 20) {
|
||||
totalStock += MyApp.getInstance().getSlotList(0).get(j).getCount();
|
||||
}
|
||||
}
|
||||
|
||||
if (totalStock == 0) {
|
||||
MyApp.getInstance().getLogs().info("預判失敗:ORDC = " + ordc + " 找不到貨道或庫存為0");
|
||||
String storename = "@商品 [" + ordc + "] 無庫存或未設定貨道,請確認。";
|
||||
handlerMain.start("無庫存或未設定貨道,請確認", storename);
|
||||
stockCheckPassed = false;
|
||||
break;
|
||||
} else if (totalStock < requestQty) {
|
||||
MyApp.getInstance().getLogs().info("預判失敗:ORDC = " + ordc + " 庫存不足,總庫存:" + totalStock + ",需求:" + requestQty);
|
||||
String storename = "@商品 [" + ordc + "] 庫存不足,請補貨後再掃描。";
|
||||
handlerMain.start("庫存不足", storename);
|
||||
stockCheckPassed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!stockCheckPassed) {
|
||||
return; // 直接結束,不進入後續流程
|
||||
}
|
||||
|
||||
MyApp.getInstance().getLogs().info("itemsArray.length" + itemsArray.length());
|
||||
for (int i = 0; i < itemsArray.length(); i++) {
|
||||
JSONObject itemObject = itemsArray.getJSONObject(i);
|
||||
|
||||
// 重新初始化這些變數
|
||||
MedicineName = "";
|
||||
MedicineCount = 0;
|
||||
MedicineSlot = "";
|
||||
MedicineImg = "";
|
||||
MedicinePId = "";
|
||||
MemoSnDrgno = "";
|
||||
|
||||
for (int j = 0; j < MyApp.getInstance().getSlotList(0).size(); j++) {
|
||||
if (itemObject.optString("ORDC").equals(MyApp.getInstance().getSlotList(0).get(j).getProduct().getMaterialCode())
|
||||
&& MyApp.getInstance().getSlotList(0).get(j).getCount() > 0
|
||||
&& MyApp.getInstance().getSlotList(0).get(j).getCount() <= 20) {
|
||||
MedicineCount += MyApp.getInstance().getSlotList(0).get(j).getCount();
|
||||
MedicineSlot += MyApp.getInstance().getSlotList(0).get(j).getSlot() + "-" + MyApp.getInstance().getSlotList(0).get(j).getCount() + ",";
|
||||
|
||||
if (MedicineCount >= Integer.valueOf(itemObject.optString("QTY"))) {
|
||||
MedicinePId = MyApp.getInstance().getSlotList(0).get(j).getProduct().getProductID();
|
||||
MedicineName = MyApp.getInstance().getSlotList(0).get(j).getProduct().getProductName();
|
||||
MedicineImg = MyApp.getInstance().getSlotList(0).get(j).getProduct().getProductImg();
|
||||
MemoSnDrgno = "{\"SN\": \"" + jsonObject.optString("SN") + "\", \"DRGNO\": \"" + jsonObject.optString("DRGNO") + "\"}";
|
||||
}
|
||||
}
|
||||
}
|
||||
medicineList.add(new MedicineStructure(itemObject.optString("ORDC"), MedicineCount, MedicineName, MedicineSlot, MedicineImg, MedicinePId, MemoSnDrgno));
|
||||
}
|
||||
|
||||
/* 這裡將商品依據領藥數量加入 BuyList */
|
||||
for (int i = 0; i < itemsArray.length(); i++) {
|
||||
JSONObject itemObject = itemsArray.getJSONObject(i);
|
||||
int requestQty = Integer.parseInt(itemObject.optString("QTY"));
|
||||
|
||||
if (requestQty <= medicineList.get(i).getMedicineCount()) {
|
||||
haveValuestate = true;
|
||||
|
||||
if (requestQty > 1) {
|
||||
int checkcount = 0;
|
||||
int slotnum = 0;
|
||||
for (int j = 0; j < requestQty; j++) {
|
||||
checkcount += 1;
|
||||
if (checkcount > Integer.valueOf(medicineList.get(i).getMedicineSlot().split(",")[0].split("-")[1])) {
|
||||
slotnum = Integer.valueOf(medicineList.get(i).getMedicineSlot().split(",")[1].split("-")[0]);
|
||||
} else {
|
||||
slotnum = Integer.valueOf(medicineList.get(i).getMedicineSlot().split(",")[0].split("-")[0]);
|
||||
}
|
||||
addMedicineToBuyList(
|
||||
medicineList.get(i).getProductId(),
|
||||
medicineList.get(i).getMedicineName(),
|
||||
slotnum,
|
||||
medicineList.get(i).getMedicineImg(),
|
||||
medicineList.get(i).getMemoSnDrgno(),
|
||||
1
|
||||
);
|
||||
}
|
||||
} else {
|
||||
addMedicineToBuyList(
|
||||
medicineList.get(i).getProductId(),
|
||||
medicineList.get(i).getMedicineName(),
|
||||
Integer.valueOf(medicineList.get(i).getMedicineSlot().split(",")[0].split("-")[0]),
|
||||
medicineList.get(i).getMedicineImg(),
|
||||
medicineList.get(i).getMemoSnDrgno(),
|
||||
1
|
||||
);
|
||||
}
|
||||
} else {
|
||||
if (medicineList.get(i).getMedicineCount() < 1) {
|
||||
handlerMain.start("這裡判斷貨道數量不足提示警語", "@領藥單有貨道數量問題,請洽藥局發藥窗口!");
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void addMedicineToBuyList(String MedicinePidStr, String MedicineNameStr, int SlotNumber, String MedicineImg, String MemoSnDrgno, int count) {
|
||||
ProductStructure productStructure = new ProductStructure(MedicinePidStr, MedicineImg, MedicineNameStr, 0, 0, 0, 0, MemoSnDrgno, "", "", "");
|
||||
MyApp.getInstance().getLogs().info("aaaaaa " + MedicinePidStr + " " + MemoSnDrgno + " " + MedicineNameStr);
|
||||
BuyStructure buyProduct = new BuyStructure(0, SlotNumber, productStructure, count);
|
||||
MyApp.getInstance().getBuyList().add(buyProduct);
|
||||
}
|
||||
|
||||
public void checkORDCDispatchNumber(String decryptedStr) throws JSONException {
|
||||
JSONObject jsonObject = new JSONObject(decryptedStr);
|
||||
JSONObject newItem = new JSONObject();
|
||||
newItem.put("SN", jsonObject.optString("SN"));
|
||||
newItem.put("DRGNO", jsonObject.optString("DRGNO"));
|
||||
|
||||
String jsonStr = MyApp.getInstance().getMemoMap().get("CMUH").get(0).getData().toString();
|
||||
JSONObject jsonObject2 = new JSONObject(jsonStr);
|
||||
JSONArray itemArray = jsonObject2.getJSONArray("ITEM");
|
||||
|
||||
boolean exists = false;
|
||||
for (int i = 0; i < itemArray.length(); i++) {
|
||||
JSONObject item = itemArray.getJSONObject(i);
|
||||
if (item.getString("SN").equals(newItem.getString("SN")) && item.getString("DRGNO").equals(newItem.getString("DRGNO"))) {
|
||||
exists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (exists) {
|
||||
MyApp.getInstance().getLogs().info("⚠️ 此項目已存在,不新增!");
|
||||
openBarcode = false;
|
||||
handlerMain.start(getClass().getSimpleName(), "@此單已取物完成,請洽藥局發藥窗口!");
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,263 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,429 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,175 @@
|
||||
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.DialogPickupErrorTipBinding;
|
||||
import com.unibuy.smartdevice.structure.BuyStructure;
|
||||
import com.unibuy.smartdevice.structure.ProductStructure;
|
||||
import com.unibuy.smartdevice.tools.HandlerMain;
|
||||
import com.unibuy.smartdevice.tools.HandlerMainCountdown;
|
||||
import com.unibuy.smartdevice.tools.ToastHandlerMain;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class PickupErrorTipDialog 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 PickupErrorTipDialog(Context context, HandlerMain srcHandlerMain) {
|
||||
super(context, srcHandlerMain);
|
||||
getLogs().info("PickupErrorTipDialog:" + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
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 this.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:
|
||||
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 DialogPickupErrorTipBinding binding;
|
||||
//private RecyclerDialogTakeMedicineListAdpter recyclerDialogTakeMedicineListAdpter;
|
||||
private CloseDialogOnThreadHandler closeDialogCountdown;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Context context) {
|
||||
binding = DialogPickupErrorTipBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
String decryptedStr = MyApp.getInstance().getDecryptedData();
|
||||
getLogs().info(decryptedStr);
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(decryptedStr);
|
||||
binding.tvDrgnoid.setText("領藥號:"+jsonObject.optString("DRGNO"));
|
||||
} catch (JSONException e) {
|
||||
getLogs().warning(e);
|
||||
}
|
||||
|
||||
closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain());
|
||||
closeDialogCountdown.start(40);
|
||||
binding.btnNonPerson.setOnClickListener(v -> dialogCancel());
|
||||
}
|
||||
|
||||
public void addMedicineToBuyList(String MedicinePidStr,String MedicineNameStr,int SlotNumber,String MedicineImg){
|
||||
ProductStructure productStructure = new ProductStructure(MedicinePidStr,MedicineImg,MedicineNameStr,0, 0, 0, 0, "", "", "", "");
|
||||
//getLogs().info("aaaa"+SlotNumber);
|
||||
BuyStructure buyProduct = new BuyStructure(0, SlotNumber, productStructure, 1);
|
||||
MyApp.getInstance().getBuyList().add(buyProduct);
|
||||
}
|
||||
|
||||
public void dialogCancel() {
|
||||
closeDialogCountdown.shutdown();
|
||||
/*int buyListSize = MyApp.getInstance().getBuyList().size();
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
if (buyListSize > 0) {
|
||||
recyclerDialogTakeMedicineListAdpter.notifyItemRangeRemoved(0, buyListSize);
|
||||
}*/
|
||||
cancel();
|
||||
}
|
||||
|
||||
public void cancelOnThreadCountdown() {
|
||||
/*int buyListSize = MyApp.getInstance().getBuyList().size();
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
if (buyListSize > 0) {
|
||||
recyclerDialogTakeMedicineListAdpter.notifyItemRangeRemoved(0, buyListSize);
|
||||
}*/
|
||||
getTools().dialogClose();
|
||||
cancel();
|
||||
}
|
||||
|
||||
public void setButtonCancelText(long countdown) {
|
||||
binding.btnNonPerson.setText("否("+countdown+")");
|
||||
}
|
||||
|
||||
public static class CloseDialogOnThreadHandler extends HandlerMainCountdown {
|
||||
public CloseDialogOnThreadHandler(HandlerMain handlerMain) {
|
||||
super(handlerMain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandlerMain setHandlerMain() {
|
||||
return getSrcHandlerMain();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close(HandlerMain handlerMain) {
|
||||
handlerMain.start(getClass().getSimpleName(), Option.COUNTDOWN_CANCEL.getOption(), "close dialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(long countdown, HandlerMain handlerMain) {
|
||||
handlerMain.start(getClass().getSimpleName(), Option.SET_COUNTDOWN_TEXT.getOption(), String.valueOf(countdown));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> setCls() {
|
||||
return getClass();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,273 @@
|
||||
package com.unibuy.smartdevice.ui.dialog;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.unibuy.smartdevice.DialogAbstract;
|
||||
import com.unibuy.smartdevice.MyApp;
|
||||
import com.unibuy.smartdevice.databinding.DialogCmuhTakeMedicineBinding;
|
||||
import com.unibuy.smartdevice.external.HttpAPI;
|
||||
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.RecyclerDialogTakeMedicineListAdpter;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TakeMedicineDialog extends DialogAbstract {
|
||||
public enum Option {
|
||||
TOAST(0),
|
||||
COUNTDOWN_CANCEL(1),
|
||||
SET_COUNTDOWN_TEXT(2),
|
||||
SET_PRICE(3),
|
||||
RESET_COUNTDOWM(4),
|
||||
RESET_COUNTDOWM_TOAST(5),
|
||||
CHANGE_DISPATCH(6),
|
||||
REPORT_FLOW(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();
|
||||
}
|
||||
|
||||
private boolean isChangeDispatch;
|
||||
private boolean isRun;
|
||||
|
||||
boolean isResponse;
|
||||
private HttpAPI httpAPI;
|
||||
|
||||
@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) {
|
||||
getSrcHandlerMain().start("這裡會出現貨道無貨提示警語","@再刷一次,若不行,請洽發藥窗口!");
|
||||
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:
|
||||
closeDialogCountdown.setCountdown(40);
|
||||
break;
|
||||
case RESET_COUNTDOWM:
|
||||
closeDialogCountdown.setCountdown(40);
|
||||
break;
|
||||
case RESET_COUNTDOWM_TOAST:
|
||||
super.execute(context, commandCode, message);
|
||||
closeDialogCountdown.setCountdown(40);
|
||||
break;
|
||||
case CHANGE_DISPATCH:
|
||||
changeDispatch();
|
||||
break;
|
||||
case REPORT_FLOW:
|
||||
getLogs().info(MyApp.getInstance().getReportFlowInfoData().toString());
|
||||
if (MyApp.getInstance().getReportFlowInfoData().getFlowStatusType() == 0) {
|
||||
start(getSrcClassName(), CheckoutCashDialog.Option.CHANGE_BUY.getOption(), "change buy");
|
||||
} else {
|
||||
if (MyApp.getInstance().getReportFlowInfoData().getInvoiceInfo().isEmpty()) {
|
||||
start(getSrcClassName(), CheckoutCashDialog.Option.CHANGE_DISPATCH.getOption(), "change dispatch");
|
||||
} else {
|
||||
start(getSrcClassName(), CheckoutCashDialog.Option.POST_INVOICE.getOption(), "post invoice");
|
||||
}
|
||||
}
|
||||
|
||||
// new HandlerMainCountdown(this) {
|
||||
// @Override
|
||||
// protected Class<?> setCls() {
|
||||
// return this.getClass();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected HandlerMain setHandlerMain() {
|
||||
// return getSrcHandlerMain();
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void execute(long countdown, HandlerMain handlerMain) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// protected void close(HandlerMain handlerMain) {
|
||||
// if (MyApp.getInstance().getReportFlowInfoData().getFlowStatusType() == 0) {
|
||||
// handlerMain.start(getSrcClassName(), CheckoutCashDialog.Option.CHANGE_BUY.getOption(), "change buy");
|
||||
// } else {
|
||||
// if (MyApp.getInstance().getReportFlowInfoData().getInvoiceInfo().isEmpty()) {
|
||||
// handlerMain.start(getSrcClassName(), CheckoutCashDialog.Option.CHANGE_DISPATCH.getOption(), "change dispatch");
|
||||
// } else {
|
||||
// handlerMain.start(getSrcClassName(), CheckoutCashDialog.Option.POST_INVOICE.getOption(), "post invoice");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }.start(2);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private DialogCmuhTakeMedicineBinding binding;
|
||||
private RecyclerDialogTakeMedicineListAdpter recyclerDialogTakeMedicineListAdpter;
|
||||
private CloseDialogOnThreadHandler closeDialogCountdown;
|
||||
|
||||
public TakeMedicineDialog(Context context, HandlerMain srcHandlerMain) {
|
||||
super(context, srcHandlerMain);
|
||||
getLogs().info("open:" + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Context context) {
|
||||
binding = DialogCmuhTakeMedicineBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
this.isChangeDispatch = true;
|
||||
this.isRun = true;
|
||||
isResponse = true;
|
||||
this.httpAPI = new HttpAPI(getHandlerMain());
|
||||
|
||||
this.recyclerDialogTakeMedicineListAdpter = new RecyclerDialogTakeMedicineListAdpter(getCtx(), MyApp.getInstance().getBuyList(), getHandlerMain());
|
||||
this.binding.recyclerDialogBuyList.setAdapter(recyclerDialogTakeMedicineListAdpter);
|
||||
this.binding.recyclerDialogBuyList.setLayoutManager(new LinearLayoutManager(context));
|
||||
|
||||
MyApp.getInstance().initialReportData();
|
||||
MyApp.getInstance().getReportFlowInfoData().setOrderId(Tools.generateTimeBasedUUID());
|
||||
MyApp.getInstance().getPostInvoiceByGreenData().setOrderId(MyApp.getInstance().getReportFlowInfoData().getOrderId());
|
||||
|
||||
String decryptedStr = MyApp.getInstance().getDecryptedData();
|
||||
JSONObject jsonObject = null;
|
||||
try {
|
||||
jsonObject = new JSONObject(decryptedStr);
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
binding.tvHostId.setText("管理單位ID:"+jsonObject.optString("HOSP_ID"));
|
||||
binding.tvSn.setText("序號:"+jsonObject.optString("SN"));
|
||||
binding.tvName.setText("姓名:"+jsonObject.optString("NAME"));
|
||||
binding.tvDrgno.setText("領藥號:"+jsonObject.optString("DRGNO"));
|
||||
binding.tvData.setText("備註:"+jsonObject.optString("DATA"));
|
||||
binding.tvPrintTime.setText("列印時間:"+jsonObject.optString("PRINT_TIME"));
|
||||
|
||||
this.binding.buttonAddProduct.setOnClickListener(v -> gotoAddProduct());
|
||||
//this.binding.buttonCancel.setOnClickListener(v -> dialogCancel());
|
||||
|
||||
closeDialogCountdown = new CloseDialogOnThreadHandler(getHandlerMain());
|
||||
closeDialogCountdown.start(40);
|
||||
|
||||
// if (MyApp.getInstance().getDevSet().isShoppingCar()) {
|
||||
// binding.buttonAddProduct.setVisibility(View.VISIBLE);
|
||||
// } else {
|
||||
// binding.buttonAddProduct.setVisibility(View.GONE);
|
||||
// }
|
||||
}
|
||||
|
||||
public void gotoAddProduct() {
|
||||
//getSrcHandlerMain().start(getClass().getSimpleName(), FontendActivity.Option.ADD_PRODUCT.getOption(), "請在40秒內完成加購動作");
|
||||
changeDispatch();
|
||||
closeDialogCountdown.shutdown();
|
||||
cancel();
|
||||
}
|
||||
|
||||
public void changeDispatch() {
|
||||
if (isChangeDispatch){
|
||||
isRun = false;
|
||||
isChangeDispatch = false;
|
||||
//closeMoneyType();
|
||||
closeDialogCountdown.shutdown();
|
||||
changeDialog(DispatchDialog.class);
|
||||
}
|
||||
}
|
||||
|
||||
public void dialogCancel() {
|
||||
closeDialogCountdown.shutdown();
|
||||
int buyListSize = MyApp.getInstance().getBuyList().size();
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
if (buyListSize > 0) {
|
||||
recyclerDialogTakeMedicineListAdpter.notifyItemRangeRemoved(0, buyListSize);
|
||||
}
|
||||
cancel();
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void cancelOnThreadCountdown() {
|
||||
int buyListSize = MyApp.getInstance().getBuyList().size();
|
||||
MyApp.getInstance().getBuyList().clear();
|
||||
if (buyListSize > 0) {
|
||||
recyclerDialogTakeMedicineListAdpter.notifyItemRangeRemoved(0, buyListSize);
|
||||
}
|
||||
getTools().dialogClose();
|
||||
cancel();
|
||||
}
|
||||
|
||||
public void setButtonCancelText(long countdown) {
|
||||
binding.buttonCancel.setText("取消("+countdown+")");
|
||||
}
|
||||
|
||||
public static class CloseDialogOnThreadHandler extends HandlerMainCountdown {
|
||||
public CloseDialogOnThreadHandler(HandlerMain handlerMain) {
|
||||
super(handlerMain);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected HandlerMain setHandlerMain() {
|
||||
return getSrcHandlerMain();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void close(HandlerMain handlerMain) {
|
||||
handlerMain.start(getClass().getSimpleName(), Option.COUNTDOWN_CANCEL.getOption(), "close dialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void execute(long countdown, HandlerMain handlerMain) {
|
||||
handlerMain.start(getClass().getSimpleName(), Option.SET_COUNTDOWN_TEXT.getOption(), String.valueOf(countdown));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Class<?> setCls() {
|
||||
return getClass();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,193 @@
|
||||
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));
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package com.unibuy.smartdevice.ui.recycler;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
|
||||
import com.unibuy.smartdevice.databinding.RecyclerDialogTakeMedicineListBinding;
|
||||
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.tools.ImageGlide;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RecyclerDialogTakeMedicineListAdpter extends RecyclerView.Adapter<RecyclerDialogTakeMedicineListAdpter.ViewHolder> {
|
||||
private Logs logs;
|
||||
private Context context;
|
||||
private final List<BuyStructure> buyList;
|
||||
private HandlerMain handlerMain;
|
||||
|
||||
public RecyclerDialogTakeMedicineListAdpter(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 {
|
||||
RecyclerDialogTakeMedicineListBinding binding;
|
||||
|
||||
public ViewHolder(@NonNull RecyclerDialogTakeMedicineListBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
// 使用 ViewBinding 來綁定佈局
|
||||
RecyclerDialogTakeMedicineListBinding binding = RecyclerDialogTakeMedicineListBinding.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("點我選擇商品");
|
||||
}
|
||||
|
||||
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 count = buyProduct.getCount();
|
||||
|
||||
holder.binding.textCount.setText("x"+String.valueOf(count));
|
||||
|
||||
logs.info("field:" + buyProduct.getField());
|
||||
|
||||
try {
|
||||
SlotField slotField = SlotField.getSlotField(buyProduct.getField());
|
||||
logs.info("slotField:" + slotField.isCanAddPurchased());
|
||||
} catch (LogsEmptyException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return buyList.size();
|
||||
}
|
||||
|
||||
}
|
||||
198
app/src/Cmuh/res/layout/dialog_cmuh_take_medicine.xml
Normal file
198
app/src/Cmuh/res/layout/dialog_cmuh_take_medicine.xml
Normal file
@ -0,0 +1,198 @@
|
||||
<?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/linearLayout1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="360dp"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
android:layout_marginTop="50dp"
|
||||
tools:ignore="MissingConstraints">
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_host_id"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TextView"
|
||||
android:textSize="36dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="100dp"
|
||||
/>
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_sn"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TextView"
|
||||
android:textSize="36dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="100dp"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TextView"
|
||||
android:textSize="36dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="100dp"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/tv_drgno"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TextView"
|
||||
android:textSize="36dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="100dp"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/tv_data"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TextView"
|
||||
android:textSize="36dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="100dp"
|
||||
/>
|
||||
<TextView
|
||||
android:id="@+id/tv_print_time"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="TextView"
|
||||
android:textSize="36dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:layout_marginStart="100dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout112"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="10dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginStart="50dp"
|
||||
android:layout_marginEnd="50dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/linearLayout1"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
android:background="#000000"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout1111"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="150dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toBottomOf="@id/linearLayout112"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="請確認領藥人的訊息內容是否正確\n再按下「確定取貨」按鈕"
|
||||
android:textSize="36dp"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout4"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/light_gray"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout1111"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="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="30sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerDialogBuyList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="750dp"
|
||||
/>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layoutNfcPay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal">
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="80dp"
|
||||
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:visibility="gone"
|
||||
android:text="取消(40)"
|
||||
android:textSize="30sp"
|
||||
android:textColor="@color/black" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/buttonAddProduct"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="120dp"
|
||||
android:layout_weight="1"
|
||||
android:backgroundTint="@color/white"
|
||||
android:text="確定取貨"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="60sp"
|
||||
android:visibility="visible" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
104
app/src/Cmuh/res/layout/dialog_pickup_code_buy.xml
Normal file
104
app/src/Cmuh/res/layout/dialog_pickup_code_buy.xml
Normal file
@ -0,0 +1,104 @@
|
||||
<?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>
|
||||
194
app/src/Cmuh/res/layout/dialog_pickup_code_fontend.xml
Normal file
194
app/src/Cmuh/res/layout/dialog_pickup_code_fontend.xml
Normal file
@ -0,0 +1,194 @@
|
||||
<?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>
|
||||
84
app/src/Cmuh/res/layout/dialog_pickup_error_tip.xml
Normal file
84
app/src/Cmuh/res/layout/dialog_pickup_error_tip.xml
Normal file
@ -0,0 +1,84 @@
|
||||
<?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">
|
||||
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout211"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="150dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
tools:ignore="MissingConstraints">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_drgnoid"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="領藥號:"
|
||||
android:textSize="60dp"
|
||||
android:layout_gravity="bottom"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout212"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="300dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintTop_toBottomOf="@+id/linearLayout211"
|
||||
tools:ignore="MissingConstraints">
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="取物失敗\n請洽櫃台\n#104"
|
||||
android:textSize="60dp"
|
||||
android:gravity="center"
|
||||
android:textAlignment="center"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/linearLayout1121"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="10dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginStart="50dp"
|
||||
android:layout_marginEnd="50dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/linearLayout212"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="2dp"
|
||||
android:background="#B0B0B0"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="8dp"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="100dp"
|
||||
android:gravity="center"
|
||||
android:orientation="horizontal"
|
||||
android:layout_marginStart="50dp"
|
||||
android:layout_marginEnd="50dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/linearLayout1121"
|
||||
app:layout_constraintStart_toStartOf="parent">
|
||||
<Button
|
||||
android:id="@+id/btn_non_person"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:text="否(40)"
|
||||
android:textSize="30sp"
|
||||
tools:ignore="MissingConstraints" />
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
40
app/src/Cmuh/res/layout/fragment_vmc.xml
Normal file
40
app/src/Cmuh/res/layout/fragment_vmc.xml
Normal file
@ -0,0 +1,40 @@
|
||||
<?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:orientation="horizontal"
|
||||
android:gravity="center"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
>
|
||||
<Button
|
||||
android:id="@+id/scanbarcodebtn"
|
||||
android:layout_width="400dp"
|
||||
android:layout_height="400dp"
|
||||
android:padding="10dp"
|
||||
android:text="掃描單據\n按鈕"
|
||||
android:textSize="70sp"
|
||||
android:textColor="@android:color/white"
|
||||
app:strokeWidth="5dp"
|
||||
app:strokeColor="@color/gray"
|
||||
android:layout_marginTop="-50dp"
|
||||
/>
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
109
app/src/Cmuh/res/layout/recycler_dialog_pickup_code_buy_list.xml
Normal file
109
app/src/Cmuh/res/layout/recycler_dialog_pickup_code_buy_list.xml
Normal file
@ -0,0 +1,109 @@
|
||||
<?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>
|
||||
102
app/src/Cmuh/res/layout/recycler_dialog_take_medicine_list.xml
Normal file
102
app/src/Cmuh/res/layout/recycler_dialog_take_medicine_list.xml
Normal file
@ -0,0 +1,102 @@
|
||||
<?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/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>
|
||||
@ -0,0 +1,38 @@
|
||||
package com.unibuy.smartdevice.ui;
|
||||
|
||||
import android.content.Context;
|
||||
import com.unibuy.smartdevice.tools.HandlerMain;
|
||||
import com.unibuy.smartdevice.ui.dialog.BuyDialog;
|
||||
|
||||
/**
|
||||
* General (通用版) 銷售流程處理器。
|
||||
* 點選商品/購物車時彈出標準付款 Dialog (BuyDialog)。
|
||||
*/
|
||||
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 onBuyRequested() {
|
||||
new BuyDialog(context, handlerMain).show();
|
||||
}
|
||||
|
||||
public void onBarcodeReceived(String rawData) {
|
||||
// 通用版不處理掃碼
|
||||
}
|
||||
|
||||
public void onPickupRequested() {
|
||||
// 通用版不處理取貨碼
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,264 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,591 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,194 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -343,7 +343,7 @@ public class HomeActivity extends AppCompatActivityAbstract {
|
||||
protected void execute(HandlerMain handlerMain) {
|
||||
for (int testIndex=0; testIndex < 30; testIndex++) {
|
||||
if (testCount < 3) {
|
||||
if (!MyApp.getInstance().isShowDialog()) {
|
||||
if (!MyApp.getInstance().isMachineBusy()) {
|
||||
getLogs().debug("RestartHost testCount:" + testCount);
|
||||
testCount++;
|
||||
} else {
|
||||
@ -389,16 +389,16 @@ public class HomeActivity extends AppCompatActivityAbstract {
|
||||
// 最多等待 10 分鐘(120 次 * 5 秒),確保機台有機會重啟
|
||||
for (int i = 0; i < 120; i++) {
|
||||
long idle = MyApp.getInstance().getIdleSeconds();
|
||||
boolean isDialog = MyApp.getInstance().isShowDialog();
|
||||
boolean isBusy = MyApp.getInstance().isMachineBusy();
|
||||
|
||||
if (idle >= 90 && !isDialog) {
|
||||
getLogs().info("RestartAPP2:閒置 " + idle + " 秒且無對話框,執行真重啟");
|
||||
if (idle >= 90 && !isBusy) {
|
||||
getLogs().info("RestartAPP2:閒置 " + idle + " 秒且機台不忙碌,執行真重啟");
|
||||
new android.os.Handler(android.os.Looper.getMainLooper()).post(() -> {
|
||||
ReSetAPP2();
|
||||
});
|
||||
return;
|
||||
}
|
||||
getLogs().debug("RestartAPP2:閒置 " + idle + " 秒,Dialog:" + isDialog + ",等待 5 秒後重新判斷 (" + (i+1) + "/120)");
|
||||
getLogs().debug("RestartAPP2:閒置 " + idle + " 秒,BusyReason:" + MyApp.getInstance().getBusyReason() + ",等待 5 秒後重新判斷 (" + (i+1) + "/120)");
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
@ -427,7 +427,7 @@ public class HomeActivity extends AppCompatActivityAbstract {
|
||||
protected void execute(HandlerMain handlerMain) {
|
||||
for (int testIndex=0; testIndex < 30; testIndex++) {
|
||||
if (testCount < 3) {
|
||||
if (!MyApp.getInstance().isShowDialog()) {
|
||||
if (!MyApp.getInstance().isMachineBusy()) {
|
||||
getLogs().debug("RestartHost testCount:" + testCount);
|
||||
testCount++;
|
||||
} else {
|
||||
@ -473,7 +473,7 @@ public class HomeActivity extends AppCompatActivityAbstract {
|
||||
protected void execute(HandlerMain handlerMain) {
|
||||
for (int testIndex=0; testIndex < 30; testIndex++) {
|
||||
if (testCount < 3) {
|
||||
if (!MyApp.getInstance().isShowDialog()) {
|
||||
if (!MyApp.getInstance().isMachineBusy()) {
|
||||
getLogs().debug("NfcCheckout testCount:" + testCount);
|
||||
testCount++;
|
||||
} else {
|
||||
@ -556,7 +556,7 @@ public class HomeActivity extends AppCompatActivityAbstract {
|
||||
Looper.prepare(); // ✅ 確保這個執行緒有 Looper
|
||||
for (int testIndex=0; testIndex < 30; testIndex++) {
|
||||
if (testCount < 3) {
|
||||
if (!MyApp.getInstance().isShowDialog()) {
|
||||
if (!MyApp.getInstance().isMachineBusy()) {
|
||||
getLogs().debug("CMUHAutoRun testCount:" + testCount);
|
||||
testCount++;
|
||||
} else {
|
||||
|
||||
@ -97,6 +97,8 @@ public class MyApp extends Application {
|
||||
private Map<Integer, List<SlotStructure>> slotMap;
|
||||
private List<MarketingPlanStructure> marketingPlanList;
|
||||
private List<BuyStructure> buyList;
|
||||
private String decryptedData;
|
||||
private Map<String, List<MemoStructure>> memoMap;
|
||||
private CashSetStructure cashSetStructure;
|
||||
private AccessCodeStructure accessCodeStructure;
|
||||
private FreeGiftStructure freeGiftStructure;
|
||||
@ -277,6 +279,8 @@ public class MyApp extends Application {
|
||||
initialSlotData();
|
||||
this.marketingPlanList = new ArrayList<>();
|
||||
this.buyList = new ArrayList<>();
|
||||
this.memoMap = new HashMap<>();
|
||||
initialMemoData();
|
||||
|
||||
PeriodicWorkRequest periodicWorkRequest = new PeriodicWorkRequest.Builder(
|
||||
UploadLogPeriodicWorker.class,
|
||||
@ -667,6 +671,62 @@ public class MyApp extends Application {
|
||||
return buyList;
|
||||
}
|
||||
|
||||
public Logs getLogs() {
|
||||
return logs;
|
||||
}
|
||||
|
||||
public void setDecryptedData(String decryptedData) {
|
||||
this.decryptedData = decryptedData;
|
||||
}
|
||||
|
||||
public String getDecryptedData() {
|
||||
return decryptedData;
|
||||
}
|
||||
|
||||
public Map<String, List<MemoStructure>> getMemoMap() {
|
||||
return memoMap;
|
||||
}
|
||||
|
||||
private void initialMemoData() {
|
||||
logs.debug("initialMemoData");
|
||||
|
||||
// 確保 memoMap 已初始化
|
||||
if (getInstance().getMemoMap() == null) {
|
||||
logs.info("memoMap初始化");
|
||||
getInstance().memoMap = new HashMap<>();
|
||||
}
|
||||
|
||||
// 開架構
|
||||
if (!getInstance().getMemoMap().containsKey("CMUH")) {
|
||||
getInstance().getMemoMap().put("CMUH", new ArrayList<>());
|
||||
logs.info("CMUH memo db open");
|
||||
}
|
||||
|
||||
// 查詢資料庫
|
||||
MemoDao memoDao = new MemoDao(this);
|
||||
try {
|
||||
Map<String, List<MemoStructure>> memoMapNew = memoDao.getAll();
|
||||
if (memoMapNew != null && memoMapNew.get("CMUH") != null && !memoMapNew.get("CMUH").isEmpty()) {
|
||||
getInstance().getMemoMap().get("CMUH").addAll(memoMapNew.get("CMUH"));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logs.info("📛 讀取 memo 資料庫失敗或無資料,跳過。");
|
||||
} finally {
|
||||
memoDao.close();
|
||||
}
|
||||
|
||||
// 無資料就初始化一筆資料
|
||||
if (getInstance().getMemoMap().get("CMUH").isEmpty()) {
|
||||
logs.info("初始化CMUH Memo資料表");
|
||||
String memoStr = "{\"ITEM\": [{\"SN\": \"00000000\", \"DRGNO\": \"0000\"}]}";
|
||||
try {
|
||||
getInstance().getMemoMap().get("CMUH").add(new MemoStructure("CMUH", new JSONObject(memoStr), new JSONObject()));
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public EsunpayStructure getEsunpay() {
|
||||
return esunpay;
|
||||
}
|
||||
|
||||
@ -108,7 +108,7 @@ public class MemoDao {
|
||||
}
|
||||
|
||||
// 獲取所有 Products
|
||||
private Map<String, List<MemoStructure>> getAll() {
|
||||
public Map<String, List<MemoStructure>> getAll() {
|
||||
Map<String, List<MemoStructure>> memoMap = new HashMap<>();
|
||||
|
||||
Cursor cursor = database.query(
|
||||
|
||||
@ -80,7 +80,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class FontendActivity extends AppCompatActivityAbstract {
|
||||
public class FontendActivity extends AppCompatActivityAbstract implements SaleFlowCallback {
|
||||
public enum Option {
|
||||
TOAST(0),
|
||||
BUY_DIALOG_OPEN(1),
|
||||
@ -130,6 +130,18 @@ public class FontendActivity extends AppCompatActivityAbstract {
|
||||
private Handler idleHandler = new Handler();
|
||||
private Runnable idleRunnable;
|
||||
|
||||
private SaleFlowHandler saleFlowHandler;
|
||||
|
||||
@Override
|
||||
public void onSaleFlowReset() {
|
||||
getHandlerMain().start(getClass().getSimpleName(), Option.RESET_DATA.getOption(), "reset data");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaleFlowError(String message) {
|
||||
getHandlerMain().start(getClass().getSimpleName(), Option.TOAST.getOption(), message);
|
||||
}
|
||||
|
||||
// ✅ 遠端庫存更新監聽器(輕量級,只刷新 Adapter 不重建 Fragment)
|
||||
private final BroadcastReceiver slotUpdateReceiver = new BroadcastReceiver() {
|
||||
@Override
|
||||
@ -188,7 +200,11 @@ public class FontendActivity extends AppCompatActivityAbstract {
|
||||
if (MyApp.getInstance().getBuyList().isEmpty()) {
|
||||
start(getClass().getSimpleName(), getString(R.string.no_products_purchased));
|
||||
} else {
|
||||
new BuyDialog(context, this).show();
|
||||
if (saleFlowHandler != null) {
|
||||
saleFlowHandler.onBuyRequested();
|
||||
} else {
|
||||
new BuyDialog(context, this).show();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ADD_PRODUCT:
|
||||
@ -391,6 +407,10 @@ public class FontendActivity extends AppCompatActivityAbstract {
|
||||
binding = ActivityFontendBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
|
||||
// 初始化銷售流程處理器
|
||||
saleFlowHandler = new SaleFlowHandler(this, getHandlerMain(), this);
|
||||
saleFlowHandler.onSetupUI();
|
||||
|
||||
httpAPI = new HttpAPI(getHandlerMain());
|
||||
|
||||
this.connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
@ -655,7 +675,11 @@ public class FontendActivity extends AppCompatActivityAbstract {
|
||||
}
|
||||
|
||||
binding.floatButtonPickupCode.setOnClickListener(view -> {
|
||||
new FontendPickupCodeDialog(getCtx(), getHandlerMain()).show();
|
||||
if (saleFlowHandler != null) {
|
||||
saleFlowHandler.onPickupRequested();
|
||||
} else {
|
||||
new FontendPickupCodeDialog(getCtx(), getHandlerMain()).show();
|
||||
}
|
||||
});
|
||||
|
||||
binding.floatButtonPickupCode.setVisibility(View.GONE);
|
||||
@ -881,7 +905,7 @@ public class FontendActivity extends AppCompatActivityAbstract {
|
||||
protected void execute(HandlerMain handlerMain) {
|
||||
for (int testIndex = 0; testIndex < 30; testIndex++) {
|
||||
if (testCount < 3) {
|
||||
if (!MyApp.getInstance().isShowDialog()) {
|
||||
if (!MyApp.getInstance().isMachineBusy()) {
|
||||
getLogs().debug("NfcCheckout testCount:" + testCount);
|
||||
testCount++;
|
||||
} else {
|
||||
@ -965,7 +989,7 @@ public class FontendActivity extends AppCompatActivityAbstract {
|
||||
Looper.prepare(); // ✅ 確保這個執行緒有 Looper
|
||||
for (int testIndex = 0; testIndex < 30; testIndex++) {
|
||||
if (testCount < 3) {
|
||||
if (!MyApp.getInstance().isShowDialog()) {
|
||||
if (!MyApp.getInstance().isMachineBusy()) {
|
||||
getLogs().debug("CMUHAutoRun testCount:" + testCount);
|
||||
testCount++;
|
||||
} else {
|
||||
|
||||
@ -0,0 +1,14 @@
|
||||
package com.unibuy.smartdevice.ui;
|
||||
|
||||
/**
|
||||
* SaleFlowHandler -> FontendActivity 的回呼介面。
|
||||
* Handler 完成特定工作後,透過此介面通知 Activity 更新 UI 或狀態。
|
||||
*/
|
||||
public interface SaleFlowCallback {
|
||||
|
||||
/** 當銷售流程完整結束(已出貨或取消),需要重置前台狀態 */
|
||||
void onSaleFlowReset();
|
||||
|
||||
/** 當流程中需要顯示錯誤訊息(Toast / Dialog) */
|
||||
void onSaleFlowError(String message);
|
||||
}
|
||||
@ -193,7 +193,7 @@ public class ElectricFragment extends FragmentAbstract {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
if (!MyApp.getInstance().isShowDialog()) {
|
||||
if (!MyApp.getInstance().isMachineBusy()) {
|
||||
try {
|
||||
httpAPI.reportElectricInfo(electricInfoByJsonArray);
|
||||
} catch (LogsParseException | LogsSettingEmptyException e) {
|
||||
|
||||
@ -186,7 +186,7 @@ public class RecyclerElectricBuyListAdpter extends RecyclerView.Adapter<Recycler
|
||||
public void onClick(View v) {
|
||||
SlotStructure slotProduct = slotList.get(position);
|
||||
if (slotProduct.isLock()) return;
|
||||
if (MyApp.getInstance().isShowDialog()) return;
|
||||
if (MyApp.getInstance().isMachineBusy()) return;
|
||||
|
||||
logs.info("marketingPlan:" + slotProduct.getProduct().getMarketingPlan());
|
||||
|
||||
|
||||
@ -156,7 +156,7 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter<RecyclerVmcBu
|
||||
SlotStructure slotProduct = SlotList.get(position);
|
||||
if (slotProduct.isLock())
|
||||
return;
|
||||
if (MyApp.getInstance().isShowDialog())
|
||||
if (MyApp.getInstance().isMachineBusy())
|
||||
return;
|
||||
// 防呆:正在檢查出貨口狀態時,不允許再次點擊
|
||||
if (isCheckingSlot)
|
||||
|
||||
@ -38,6 +38,7 @@ public class VmcFragment extends FragmentAbstract {
|
||||
ADD_PRODUCT_OPEN(3),
|
||||
ADD_PRODUCT_CLOSE(4),
|
||||
ADD_PRODUCT_FREE_GIFT(5),
|
||||
DECRYPTION_ERROR(6),
|
||||
;
|
||||
|
||||
private int option;
|
||||
@ -129,6 +130,11 @@ public class VmcFragment extends FragmentAbstract {
|
||||
sizeList.clear();
|
||||
}
|
||||
break;
|
||||
case ADD_PRODUCT_FREE_GIFT:
|
||||
break;
|
||||
case DECRYPTION_ERROR:
|
||||
super.execute(context, commandCode, "@掃碼失敗,請再掃描一次");
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -39,7 +39,7 @@ public class UploadLogPeriodicWorker extends Worker {
|
||||
public Result doWork() {
|
||||
logs.debug("進入背景工作:"+getClass().getSimpleName()+" "+getId());
|
||||
|
||||
if (MyApp.getInstance().isShowDialog()) {
|
||||
if (MyApp.getInstance().isMachineBusy()) {
|
||||
logs.debug("系統使用中,延後處理備份事項");
|
||||
|
||||
return Result.retry();
|
||||
|
||||
@ -390,7 +390,7 @@
|
||||
<string name="there_is_something_at_the_pickup_port">There is something at the pickup port, please remove it first</string>
|
||||
<string name="i_knew">knew</string>
|
||||
<string name="input_vehicle">Input Vehicle</string>
|
||||
<string name="reload_product">Reload Products</string>
|
||||
<string name="reload_product_warning">This will download all products from the cloud and overwrite any unsaved local inventory changes. Are you sure you want to continue?</string>
|
||||
<string name="sync_product">Reload Products</string>
|
||||
<string name="sync_product_warning">This will download all products from the cloud and overwrite any unsaved local inventory changes. Are you sure you want to continue?</string>
|
||||
<string name="downloading_product_data">Downloading product data...</string>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue
Block a user