diff --git a/.agents/workflows/compiler-apk.md b/.agents/workflows/compiler-apk.md index 4b97a0a..7009147 100644 --- a/.agents/workflows/compiler-apk.md +++ b/.agents/workflows/compiler-apk.md @@ -11,9 +11,14 @@ description: 自動編譯 Android APK、輸出至專屬目錄並安裝至實體 ```bash // turbo +# 終止殘留的 Gradle/Java 程序,避免多個 Daemon 互搶資源 +pkill -9 -f "gradle" 2>/dev/null; pkill -9 -f "GradleDaemon" 2>/dev/null; sleep 1 + export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 cd /home/mama/projects/TaiwanStar_general_size_s_Chengwai -./gradlew :app:assembleS_12_XY_U_Standard_Debug + +# 使用 clean 確保完整重新編譯,避免舊快取遮蔽 import 缺失等錯誤 +./gradlew clean :app:assembleS_12_XY_U_Standard_Debug ``` ## 步驟二:導出 APK 到 outputs 資料夾 diff --git a/app/src/main/java/com/unibuy/smartdevice/external/StarCloudAPI.java b/app/src/main/java/com/unibuy/smartdevice/external/StarCloudAPI.java index 978fa5a..c064e3b 100644 --- a/app/src/main/java/com/unibuy/smartdevice/external/StarCloudAPI.java +++ b/app/src/main/java/com/unibuy/smartdevice/external/StarCloudAPI.java @@ -648,6 +648,32 @@ public class StarCloudAPI { productsDao.close(); Log.i(TAG, "Synced and persisted " + products.length() + " products from star-cloud."); + + // 🔹 重要:同步更新 MyApp 貨道中的商品引用 + // 因為貨道資料是在 App 啟動時載入的,它們握著舊的 ProductStructure 物件。 + // 如果不手動更新,銷售平台會一直看到舊的圖片網址。 + for (SlotField slotField : SlotField.values()) { + List slotList = MyApp.getInstance().getSlotList(slotField.getField()); + if (slotList != null) { + for (SlotStructure slot : slotList) { + if (slot.getProduct() != null) { + String pid = slot.getProduct().getProductID(); + // 在新下載的清單中尋找匹配的商品 + for (ProductStructure newP : MyApp.getInstance().getProductList()) { + if (newP.getProductID().equals(pid)) { + slot.setProduct(newP); // 更新引用 + break; + } + } + } + } + } + } + Log.i(TAG, "Updated product references in all slots."); + + // 🔹 發送廣播通知所有監聽者(如銷售平台)商品與貨道資料已更新 + android.content.Intent intent = new android.content.Intent("com.unibuy.smartdevice.ACTION_SLOT_UPDATED"); + handlerMain.getContext().sendBroadcast(intent); } /** diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java index a249fe8..27eacd8 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/FontendActivity.java @@ -164,7 +164,7 @@ public class FontendActivity extends AppCompatActivityAbstract { switch (option) { case TOAST: - super.execute(context, commandCode, message); + super.execute(context, commandCode, message); break; case BUY_DIALOG_OPEN: start(getClass().getSimpleName(), Option.STOP_COUNTDOWN.getOption(), "stop countdown"); @@ -181,26 +181,32 @@ public class FontendActivity extends AppCompatActivityAbstract { } addProductOnCountdown = new AddProductOnCountdown(getHandlerMain()); addProductOnCountdown.start(40); - ((FragmentAbstract) vmcFragment).getHandlerMain().start(getClass().getSimpleName(), VmcFragment.Option.ADD_PRODUCT_OPEN.getOption(), "add product open"); + ((FragmentAbstract) vmcFragment).getHandlerMain().start(getClass().getSimpleName(), + VmcFragment.Option.ADD_PRODUCT_OPEN.getOption(), "add product open"); if (MyApp.getInstance().getDevSet().isElectic()) { - ((FragmentAbstract) electricFragment).getHandlerMain().start(getClass().getSimpleName(), ElectricFragment.Option.ADD_PRODUCT_OPEN.getOption(), "add product open"); + ((FragmentAbstract) electricFragment).getHandlerMain().start(getClass().getSimpleName(), + ElectricFragment.Option.ADD_PRODUCT_OPEN.getOption(), "add product open"); } break; case STOP_COUNTDOWN: if (addProductOnCountdown != null && addProductOnCountdown.getCountdown() > 0) addProductOnCountdown.shutdown(); - ((FragmentAbstract) vmcFragment).getHandlerMain().start(getClass().getSimpleName(), VmcFragment.Option.ADD_PRODUCT_CLOSE.getOption(), "add product close"); + ((FragmentAbstract) vmcFragment).getHandlerMain().start(getClass().getSimpleName(), + VmcFragment.Option.ADD_PRODUCT_CLOSE.getOption(), "add product close"); if (MyApp.getInstance().getDevSet().isElectic()) { - ((FragmentAbstract) electricFragment).getHandlerMain().start(getClass().getSimpleName(), ElectricFragment.Option.ADD_PRODUCT_CLOSE.getOption(), "add product close"); + ((FragmentAbstract) electricFragment).getHandlerMain().start(getClass().getSimpleName(), + ElectricFragment.Option.ADD_PRODUCT_CLOSE.getOption(), "add product close"); } break; case RESET_DATA: - ((FragmentAbstract) vmcFragment).getHandlerMain().start(getClass().getSimpleName(), VmcFragment.Option.SHIPPED_PRODUCT.getOption(), "shipped product"); + ((FragmentAbstract) vmcFragment).getHandlerMain().start(getClass().getSimpleName(), + VmcFragment.Option.SHIPPED_PRODUCT.getOption(), "shipped product"); if (MyApp.getInstance().getDevSet().isElectic()) { - ((FragmentAbstract) electricFragment).getHandlerMain().start(getClass().getSimpleName(), ElectricFragment.Option.SHIPPED_PRODUCT.getOption(), "shipped product"); + ((FragmentAbstract) electricFragment).getHandlerMain().start(getClass().getSimpleName(), + ElectricFragment.Option.SHIPPED_PRODUCT.getOption(), "shipped product"); } saveData(); - setBottomNavigation();//這一個方法是在消費完後,它會呼叫本方法,更新銷售平台的商品列長fragment + setBottomNavigation();// 這一個方法是在消費完後,它會呼叫本方法,更新銷售平台的商品列長fragment break; case REPORT_AND_SHOT_TIME: String ver = ""; @@ -211,7 +217,7 @@ public class FontendActivity extends AppCompatActivityAbstract { ver = "0"; } - binding.textVersion.setText(MyApp.getInstance().getMachine().getMachineID()+" Ver " + ver); + binding.textVersion.setText(MyApp.getInstance().getMachine().getMachineID() + " Ver " + ver); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()); String timeByNow = sdf.format(new Date()); @@ -238,21 +244,23 @@ public class FontendActivity extends AppCompatActivityAbstract { new RestartAPP2(this).start(); } + // 這裡判斷每5分鐘去跑連線下位機的狀態 + // String minutePart = timeByM.split(":")[1]; // 取得 "mm" 部分 + // if (minutePart.equals("00") || minutePart.equals("05") || + // minutePart.equals("10") || minutePart.equals("15") || + // minutePart.equals("20") || minutePart.equals("25") || minutePart.equals("30") + // || minutePart.equals("35")|| + // minutePart.equals("40") || minutePart.equals("45") || minutePart.equals("50") + // || minutePart.equals("55")) { + // getLogs().info("當前時間:" + timeByM + ",符合條件,執行 RunToADPage..."); + // new RunToADPage(this).start(); + // } - //這裡判斷每5分鐘去跑連線下位機的狀態 -// String minutePart = timeByM.split(":")[1]; // 取得 "mm" 部分 -// if (minutePart.equals("00") || minutePart.equals("05") || minutePart.equals("10") || minutePart.equals("15") || -// minutePart.equals("20") || minutePart.equals("25") || minutePart.equals("30") || minutePart.equals("35")|| -// minutePart.equals("40") || minutePart.equals("45") || minutePart.equals("50") || minutePart.equals("55")) { -// getLogs().info("當前時間:" + timeByM + ",符合條件,執行 RunToADPage..."); -// new RunToADPage(this).start(); -// } - - //每半小時主動連線下位機一次 + // 每半小時主動連線下位機一次 String minutePart = timeByM.split(":")[1]; // 取得 "mm" 部分 if (minutePart.equals("00") || minutePart.equals("30")) { getLogs().info("當前時間:" + timeByM + ",符合條件,執行 CMUHAutoRun..."); -// new CMUHAutoRun(this).start(); + // new CMUHAutoRun(this).start(); } getLogs().update("ThreadCount", String.valueOf(Tools.threadCount())); @@ -266,12 +274,12 @@ public class FontendActivity extends AppCompatActivityAbstract { String networkDevice = getNetworkDevice(); getLogs().update("Network", networkDevice); - binding.textNetwork.setText(getString(R.string.network)+":"+getLogs().getMessage()); + binding.textNetwork.setText(getString(R.string.network) + ":" + getLogs().getMessage()); getLogs().clearMessage(); DevXinYuanController.setVersion(ver); - getLogs().update("Temperature", String.valueOf(DevXinYuanController.getTemperature()),"℃"); - binding.textTemperature.setText(getString(R.string.temp)+":" + getLogs().getMessage()); + getLogs().update("Temperature", String.valueOf(DevXinYuanController.getTemperature()), "℃"); + binding.textTemperature.setText(getString(R.string.temp) + ":" + getLogs().getMessage()); getLogs().clearMessage(); try { @@ -281,25 +289,30 @@ public class FontendActivity extends AppCompatActivityAbstract { } String timeByS = new SimpleDateFormat("ss").format(new Date()); - timeByS = String.valueOf(Integer.valueOf(timeByS)/10*10); + timeByS = String.valueOf(Integer.valueOf(timeByS) / 10 * 10); getLogs().debug("timeByS:" + timeByS); -// if (timeByS.equals("0") || timeByS.equals("10") || timeByS.equals("20") || timeByS.equals("30") || timeByS.equals("40") || timeByS.equals("50")) { -// if (MyApp.getInstance().getDevSet().isElectic()) { -// ((FragmentAbstract) electricFragment).getHandlerMain().start(getClass().getSimpleName(), ElectricFragment.Option.BUY_ON_THREAD_TIMER_TASK.getOption(), "BUY_ON_THREAD_TIMER_TASK"); -// } -// } -// -// if (timeByS.equals("30")) { -// if (MyApp.getInstance().getDevSet().isElectic()) { -// ((FragmentAbstract) electricFragment).getHandlerMain().start(getClass().getSimpleName(), ElectricFragment.Option.SHIPPED_PRODUCT.getOption(), "BUY_ON_THREAD_TIMER_TASK"); -// } -// } - + // if (timeByS.equals("0") || timeByS.equals("10") || timeByS.equals("20") || + // timeByS.equals("30") || timeByS.equals("40") || timeByS.equals("50")) { + // if (MyApp.getInstance().getDevSet().isElectic()) { + // ((FragmentAbstract) + // electricFragment).getHandlerMain().start(getClass().getSimpleName(), + // ElectricFragment.Option.BUY_ON_THREAD_TIMER_TASK.getOption(), + // "BUY_ON_THREAD_TIMER_TASK"); + // } + // } + // + // if (timeByS.equals("30")) { + // if (MyApp.getInstance().getDevSet().isElectic()) { + // ((FragmentAbstract) + // electricFragment).getHandlerMain().start(getClass().getSimpleName(), + // ElectricFragment.Option.SHIPPED_PRODUCT.getOption(), + // "BUY_ON_THREAD_TIMER_TASK"); + // } + // } /// /機器B022 API - // 在的定時任務中: String[] timeParts = timeByM.split(":"); if (timeParts.length >= 2) { @@ -320,7 +333,7 @@ public class FontendActivity extends AppCompatActivityAbstract { break; case SET_NETWORK: - getLogs().info("網路:"+message); + getLogs().info("網路:" + message); binding.textNetwork.setText(message); break; case RESET_SLOTNO: @@ -331,11 +344,11 @@ public class FontendActivity extends AppCompatActivityAbstract { break; case HIDDEN_MULTILANGUAGE_BUTTON: getLogs().info("隱藏多國語按紐"); -// hiddenMultiLanguageButton(); + // hiddenMultiLanguageButton(); break; case SHOW_MULTILANGUAGE_BUTTON: getLogs().info("顯示多國語按紐"); -// showMultiLanguageButton(); + // showMultiLanguageButton(); break; } } @@ -348,7 +361,7 @@ public class FontendActivity extends AppCompatActivityAbstract { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 讀取最後設定語言的設定 -// LanguageHelper.applySavedLanguage(this); + // LanguageHelper.applySavedLanguage(this); binding = ActivityFontendBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); @@ -357,7 +370,7 @@ public class FontendActivity extends AppCompatActivityAbstract { this.connectivityManager = (ConnectivityManager) this.getSystemService(Context.CONNECTIVITY_SERVICE); setBottomNavigation(); -// startOnSwitchFragment(); + // startOnSwitchFragment(); getHandlerMain().getHandler().postDelayed(new Runnable() { @Override public void run() { @@ -376,7 +389,7 @@ public class FontendActivity extends AppCompatActivityAbstract { MyApp.getInstance().updateLastInteraction(); } - public void ReSetAPP(){ + public void ReSetAPP() { Intent intent = new Intent(getApplicationContext(), FontendActivity.class); // 清空所有 Task,重新啟動 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK @@ -389,7 +402,7 @@ public class FontendActivity extends AppCompatActivityAbstract { // 完全不需要 killProcess() } - public void ReSetAPP2(){ + public void ReSetAPP2() { Intent intent = new Intent(getApplicationContext(), FontendActivity.class); // 清空所有 Task,重新啟動 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK @@ -400,7 +413,7 @@ public class FontendActivity extends AppCompatActivityAbstract { finishAffinity(); // 建議先 log 再 kill getLogs().info("🔄MainActivity - App restarting..."); - + Process.killProcess(Process.myPid()); } @@ -422,7 +435,8 @@ public class FontendActivity extends AppCompatActivityAbstract { getHandlerMain().getHandler().postDelayed(() -> { if (videoEoxPlayer != null) { - // videoEoxPlayer.playMedia(binding.videoView, binding.imageAd); // ✅ 傳入 view 開始播放 + // videoEoxPlayer.playMedia(binding.videoView, binding.imageAd); // ✅ 傳入 view + // 開始播放 try { videoEoxPlayer.playMediaByFlag("1", binding.videoView, binding.imageAd); // ✅ 同時支援影片與圖片輪播 } catch (LogsFileNullException e) { @@ -442,7 +456,6 @@ public class FontendActivity extends AppCompatActivityAbstract { } } - private void resetIdleTimer() { if (idleHandler != null && idleRunnable != null) { idleHandler.removeCallbacks(idleRunnable); @@ -481,7 +494,8 @@ public class FontendActivity extends AppCompatActivityAbstract { signalStrength = capabilities.getSignalStrength(); } if (signalStrength > 0) { - signalStrengthString = signalStrength > -100 ? ":"+getString(R.string.powerful) : ":"+getString(R.string.weak); + signalStrengthString = signalStrength > -100 ? ":" + getString(R.string.powerful) + : ":" + getString(R.string.weak); signalStrengthString += "(" + signalStrength + ")"; } if (capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) { @@ -494,7 +508,8 @@ public class FontendActivity extends AppCompatActivityAbstract { } } } - } else if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { + } else if (ContextCompat.checkSelfPermission(this, + Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { networkDevice = getString(R.string.network_device_str_unsupport); } return networkDevice; @@ -502,7 +517,8 @@ public class FontendActivity extends AppCompatActivityAbstract { private boolean checkPermissionByAccessFineLocation() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) { - return ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; + return ContextCompat.checkSelfPermission(this, + Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED; } return true; // SDK < 23 時權限已在安裝時授予 } @@ -540,10 +556,13 @@ public class FontendActivity extends AppCompatActivityAbstract { ((FragmentAbstract) activeFragment).setUseStartOnSwitchFragment(true); - getSupportFragmentManager().beginTransaction().add(binding.fragmentContainer.getId(), vmcFragment, "VMC").hide(vmcFragment).commit(); + getSupportFragmentManager().beginTransaction().add(binding.fragmentContainer.getId(), vmcFragment, "VMC") + .hide(vmcFragment).commit(); if (MyApp.getInstance().getDevSet().isElectic()) { - getSupportFragmentManager().beginTransaction().add(binding.fragmentContainer.getId(), electricFragment, "ELECTRIC").hide(electricFragment).commit(); + getSupportFragmentManager().beginTransaction() + .add(binding.fragmentContainer.getId(), electricFragment, "ELECTRIC").hide(electricFragment) + .commit(); } getSupportFragmentManager().beginTransaction() @@ -568,31 +587,31 @@ public class FontendActivity extends AppCompatActivityAbstract { } } - binding.buttonVmc.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - binding.buttonVmc.setBackgroundColor(Color.BLACK); - binding.buttonVmc.setTextColor(Color.WHITE); - if (MyApp.getInstance().getDevSet().isElectic()) { - binding.buttonElectric.setBackgroundColor(Color.TRANSPARENT); - binding.buttonElectric.setTextColor(Color.BLACK); - } - switchFragment(activeFragment, vmcFragment); - } - }); + binding.buttonVmc.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + binding.buttonVmc.setBackgroundColor(Color.BLACK); + binding.buttonVmc.setTextColor(Color.WHITE); + if (MyApp.getInstance().getDevSet().isElectic()) { + binding.buttonElectric.setBackgroundColor(Color.TRANSPARENT); + binding.buttonElectric.setTextColor(Color.BLACK); + } + switchFragment(activeFragment, vmcFragment); + } + }); - binding.buttonElectric.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - binding.buttonVmc.setBackgroundColor(Color.TRANSPARENT); - binding.buttonVmc.setTextColor(Color.BLACK); - if (MyApp.getInstance().getDevSet().isElectic()) { - binding.buttonElectric.setBackgroundColor(Color.BLACK); - binding.buttonElectric.setTextColor(Color.WHITE); - } - switchFragment(activeFragment, electricFragment); - } - }); + binding.buttonElectric.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + binding.buttonVmc.setBackgroundColor(Color.TRANSPARENT); + binding.buttonVmc.setTextColor(Color.BLACK); + if (MyApp.getInstance().getDevSet().isElectic()) { + binding.buttonElectric.setBackgroundColor(Color.BLACK); + binding.buttonElectric.setTextColor(Color.WHITE); + } + switchFragment(activeFragment, electricFragment); + } + }); if (MyApp.getInstance().getDevSet().isElectic()) { binding.buttonElectric.setVisibility(VISIBLE); @@ -606,9 +625,10 @@ public class FontendActivity extends AppCompatActivityAbstract { binding.floatButtonPickupCode.setVisibility(View.GONE); - binding.floatButtonShoppingCart.setOnClickListener(view -> { - getHandlerMain().start(getClass().getSimpleName(), Option.BUY_DIALOG_OPEN.getOption(), getString(R.string.detect_shopping_cart)); - }); + binding.floatButtonShoppingCart.setOnClickListener(view -> { + getHandlerMain().start(getClass().getSimpleName(), Option.BUY_DIALOG_OPEN.getOption(), + getString(R.string.detect_shopping_cart)); + }); if (MyApp.getInstance().getDevSet().isShoppingCar()) { binding.floatButtonShoppingCart.setVisibility(VISIBLE); @@ -616,19 +636,18 @@ public class FontendActivity extends AppCompatActivityAbstract { binding.floatButtonShoppingCart.setVisibility(View.GONE); } - if (reportOnScheduler != null) { - reportOnScheduler.shutdown(); - } + if (reportOnScheduler != null) { + reportOnScheduler.shutdown(); + } reportOnScheduler = new ReportShotTimeByScheduler(getHandlerMain()); reportOnScheduler.start(1L, 10L, TimeUnit.SECONDS); -// binding.btnswitchlanTW.setOnClickListener(v -> switchLanguage("tw")); -// -// binding.btnswitchlanEn.setOnClickListener(v -> switchLanguage("en")); -// -// binding.btnswitchlanJa.setOnClickListener(v -> switchLanguage("ja")); - + // binding.btnswitchlanTW.setOnClickListener(v -> switchLanguage("tw")); + // + // binding.btnswitchlanEn.setOnClickListener(v -> switchLanguage("en")); + // + // binding.btnswitchlanJa.setOnClickListener(v -> switchLanguage("ja")); // 🛠️ 隱藏後門:長按「網路狀態」文字 2 秒進入主控台 final Handler networkHandler = new Handler(); @@ -670,13 +689,13 @@ public class FontendActivity extends AppCompatActivityAbstract { startOnSwitchFragment(); } - private void hiddenMultiLanguageButton(){ + private void hiddenMultiLanguageButton() { binding.btnswitchlanTW.setVisibility(View.GONE); binding.btnswitchlanEn.setVisibility(View.GONE); binding.btnswitchlanJa.setVisibility(View.GONE); } - private void showMultiLanguageButton(){ + private void showMultiLanguageButton() { binding.btnswitchlanTW.setVisibility(VISIBLE); binding.btnswitchlanEn.setVisibility(VISIBLE); binding.btnswitchlanJa.setVisibility(VISIBLE); @@ -684,6 +703,7 @@ public class FontendActivity extends AppCompatActivityAbstract { private class RestartHost extends HandlerMainScheduler { private int testCount = 0; + public RestartHost(HandlerMain handlerMain) { super(handlerMain); testCount = 0; @@ -696,7 +716,7 @@ public class FontendActivity extends AppCompatActivityAbstract { @Override protected void execute(HandlerMain handlerMain) { - for (int testIndex=0; testIndex < 30; testIndex++) { + for (int testIndex = 0; testIndex < 30; testIndex++) { if (testCount < 3) { if (!MyApp.getInstance().isMachineBusy()) { getLogs().debug("RestartHost testCount:" + testCount); @@ -706,7 +726,7 @@ public class FontendActivity extends AppCompatActivityAbstract { } try { - Thread.sleep(1000*20); + Thread.sleep(1000 * 20); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -736,15 +756,22 @@ public class FontendActivity extends AppCompatActivityAbstract { public RestartAPP(HandlerMain handlerMain) { super(handlerMain); } + @Override - protected HandlerMain setHandlerMain() { return getSrcHandlerMain(); } + protected HandlerMain setHandlerMain() { + return getSrcHandlerMain(); + } + @Override protected void execute(HandlerMain handlerMain) { // 直接委派給 RestartAPP2 的邏輯 new RestartAPP2(getSrcHandlerMain()).start(); } + @Override - protected Class setCls() { return getClass(); } + protected Class setCls() { + return getClass(); + } } /** @@ -756,8 +783,12 @@ public class FontendActivity extends AppCompatActivityAbstract { public RestartAPP2(HandlerMain handlerMain) { super(handlerMain); } + @Override - protected HandlerMain setHandlerMain() { return getSrcHandlerMain(); } + protected HandlerMain setHandlerMain() { + return getSrcHandlerMain(); + } + @Override protected void execute(HandlerMain handlerMain) { // 最多等待 10 分鐘(120 次 * 5 秒),確保機台有機會重啟 @@ -771,13 +802,13 @@ public class FontendActivity extends AppCompatActivityAbstract { Intent intent = new Intent(getApplicationContext(), com.unibuy.smartdevice.HomeActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(intent); - + android.os.Process.killProcess(android.os.Process.myPid()); System.exit(0); }); return; } - getLogs().debug("RestartAPP2:閒置 " + idle + " 秒,尚未達 90 秒,等待 5 秒後重新判斷(第 " + (i+1) + " 次)"); + getLogs().debug("RestartAPP2:閒置 " + idle + " 秒,尚未達 90 秒,等待 5 秒後重新判斷(第 " + (i + 1) + " 次)"); try { Thread.sleep(5000); // 每 5 秒檢查一次 } catch (InterruptedException e) { @@ -786,12 +817,16 @@ public class FontendActivity extends AppCompatActivityAbstract { } getLogs().info("RestartAPP2:等待超過 10 分鐘仍有使用者互動,放棄本次重啟"); } + @Override - protected Class setCls() { return getClass(); } + protected Class setCls() { + return getClass(); + } } private static class NfcCheckout extends HandlerMainScheduler { private int testCount = 0; + public NfcCheckout(HandlerMain handlerMain) { super(handlerMain); testCount = 0; @@ -804,7 +839,7 @@ public class FontendActivity extends AppCompatActivityAbstract { @Override protected void execute(HandlerMain handlerMain) { - for (int testIndex=0; testIndex < 30; testIndex++) { + for (int testIndex = 0; testIndex < 30; testIndex++) { if (testCount < 3) { if (!MyApp.getInstance().isShowDialog()) { getLogs().debug("NfcCheckout testCount:" + testCount); @@ -814,7 +849,7 @@ public class FontendActivity extends AppCompatActivityAbstract { } try { - Thread.sleep(1000*20); + Thread.sleep(1000 * 20); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -874,6 +909,7 @@ public class FontendActivity extends AppCompatActivityAbstract { private class CMUHAutoRun extends HandlerMainScheduler { private int testCount = 0; + public CMUHAutoRun(HandlerMain handlerMain) { super(handlerMain); testCount = 0; @@ -886,8 +922,8 @@ public class FontendActivity extends AppCompatActivityAbstract { @Override protected void execute(HandlerMain handlerMain) { - Looper.prepare(); // ✅ 確保這個執行緒有 Looper - for (int testIndex=0; testIndex < 30; testIndex++) { + Looper.prepare(); // ✅ 確保這個執行緒有 Looper + for (int testIndex = 0; testIndex < 30; testIndex++) { if (testCount < 3) { if (!MyApp.getInstance().isShowDialog()) { getLogs().debug("CMUHAutoRun testCount:" + testCount); @@ -897,7 +933,7 @@ public class FontendActivity extends AppCompatActivityAbstract { } try { - Thread.sleep(1000*20); + Thread.sleep(1000 * 20); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -911,10 +947,10 @@ public class FontendActivity extends AppCompatActivityAbstract { } getLogs().info("CMUHAutoRun - 11111111111111111"); MyApp.getInstance().getDevXinYuanController().addSendBuffer( - MyApp.getInstance().getDevXinYuanController().getDevXinYuan().syncPackNo((byte)0x01)); + MyApp.getInstance().getDevXinYuanController().getDevXinYuan().syncPackNo((byte) 0x01)); getLogs().info("CMUHAutoRun - 22222222222222222"); - //Looper.loop(); // ✅ 啟動事件迴圈 + // Looper.loop(); // ✅ 啟動事件迴圈 // 👇 安全地結束 Looper,釋放這個 Thread Objects.requireNonNull(Looper.myLooper()).quitSafely(); // 或 quit() 根據需求 @@ -964,9 +1000,9 @@ public class FontendActivity extends AppCompatActivityAbstract { @Override protected void execute(long countdown, HandlerMain handlerMain) { -// if (countdown == 20 || countdown == 10) { -// handlerMain.start(getClass().getSimpleName(), "剩餘"+countdown+"秒"); -// } + // if (countdown == 20 || countdown == 10) { + // handlerMain.start(getClass().getSimpleName(), "剩餘"+countdown+"秒"); + // } } @Override @@ -1063,6 +1099,13 @@ public class FontendActivity extends AppCompatActivityAbstract { } else { registerReceiver(slotUpdateReceiver, filter); } + + // 🔹 從設定頁面或其他頁面回到銷售平台時,強制刷新商品列表,確保新圖片能顯示 + // 先清除 Glide 記憶體快取,避免拿到舊的 Bitmap + Glide.get(this).clearMemory(); + if (vmcFragment instanceof VmcFragment) { + ((VmcFragment) vmcFragment).updateFlowerBuyList(); + } } @Override diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/ProductListActivity.java b/app/src/main/java/com/unibuy/smartdevice/ui/ProductListActivity.java index e51d62d..6419fc4 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/ProductListActivity.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/ProductListActivity.java @@ -138,6 +138,7 @@ public class ProductListActivity extends AppCompatActivityAbstract { binding.progressLayout.setVisibility(View.VISIBLE); updateProgressUI(0, getCtx().getString(R.string.downloading_product_data)); }); + ImageGlide.clearProductCache(ProductListActivity.this); starCloudAPI.downloadProductInfo(); } catch (Exception e) { getLogs().warning(e); diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/SettingActivity.java b/app/src/main/java/com/unibuy/smartdevice/ui/SettingActivity.java index 8b18f57..2b9d702 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/SettingActivity.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/SettingActivity.java @@ -21,6 +21,7 @@ import com.unibuy.smartdevice.tools.HandlerMainSchedulerOnClick; import com.unibuy.smartdevice.tools.ToastHandlerMain; import com.unibuy.smartdevice.tools.Tools; import com.unibuy.smartdevice.ui.recycler.RecyclerSettingListAdpter; +import com.unibuy.smartdevice.ui.tools.ImageGlide; import com.unibuy.smartdevice.ui.tools.VideoImageMixedPlayer; import com.unibuy.smartdevice.utils.LanguageHelper; @@ -108,13 +109,13 @@ public class SettingActivity extends AppCompatActivityAbstract { super.execute(context, commandCode, message); binding.progressLayout.setVisibility(View.GONE); binding.buttonBack.setVisibility(View.VISIBLE); - + // 綁定成功後,自動連動執行 B012 同步商品 if ("綁定成功".equals(message)) { binding.progressLayout.setVisibility(View.VISIBLE); // 顯示進度條,因為要同步商品 starCloudAPI.downloadProductInfo(); } - + checkSetting(); saveStatusList(context); break; @@ -141,7 +142,7 @@ public class SettingActivity extends AppCompatActivityAbstract { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 讀取最後設定語言的設定 -// LanguageHelper.applySavedLanguage(this); + // LanguageHelper.applySavedLanguage(this); binding = ActivitySettingBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); @@ -153,7 +154,8 @@ public class SettingActivity extends AppCompatActivityAbstract { recyclerSettingListAdpter = new RecyclerSettingListAdpter(statusList); binding.recyclerSettingList.setAdapter(recyclerSettingListAdpter); - binding.recyclerSettingList.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); + binding.recyclerSettingList + .setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)); MyApp.getInstance().initialSettingData(); @@ -164,13 +166,13 @@ public class SettingActivity extends AppCompatActivityAbstract { if (statusList.isEmpty()) { SettinglogsDao settinglogsDao = new SettinglogsDao(this); - for (String data: settinglogsDao.getAll()) { + for (String data : settinglogsDao.getAll()) { statusList.add(data); } } if (!statusList.isEmpty()) { - recyclerSettingListAdpter.notifyItemInserted(statusList.size() -1); + recyclerSettingListAdpter.notifyItemInserted(statusList.size() - 1); } binding.buttonBack.setOnClickListener(v -> { @@ -185,10 +187,11 @@ public class SettingActivity extends AppCompatActivityAbstract { } // 開始綁定 → 顯示進度條 -// showLoading(true); + // showLoading(true); binding.progressLayout.setVisibility(View.VISIBLE); VideoImageMixedPlayer.clearAdsCache(this); - getLogs().info("刪除廣告暫存檔"); + ImageGlide.clearProductCache(this); + getLogs().info("刪除廣告與商品暫存檔"); getHandlerMain().start("ButtonReloadOnClick", Option.DATA_RELOAD.getOption(), "data reload"); }); @@ -205,7 +208,8 @@ public class SettingActivity extends AppCompatActivityAbstract { public static void updateProgressUI(int percent, String message) { SettingActivity activity = instance.get(); - if (activity == null) return; + if (activity == null) + return; activity.runOnUiThread(() -> { if (activity.progressBar != null) { @@ -224,54 +228,52 @@ public class SettingActivity extends AppCompatActivityAbstract { if (!MyApp.getInstance().getMachine().getMachineID().isEmpty()) { statusList.add("download maching data:" - + MyApp.getInstance().getMachine().getMachineID() - +"/" - +Tools.randomReplace(MyApp.getInstance().getMachine().getApiKey(), 0.3) - ); + + MyApp.getInstance().getMachine().getMachineID() + + "/" + + Tools.randomReplace(MyApp.getInstance().getMachine().getApiKey(), 0.3)); } if (MyApp.getInstance().getGreenInvoice().getGreenMerchantID() != null) { statusList.add("download greenpay Invoice data:" - +Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenInvoice().getGreenMerchantID()), 0.3) - +"/" - +Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenInvoice().getGreenHashKey()), 0.3) - +"/" - +Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenInvoice().getGreenHashIV()), 0.3) - +"/" - +Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenInvoice().getGreenEmail()), 0.3) - ); + + Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenInvoice().getGreenMerchantID()), + 0.3) + + "/" + + Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenInvoice().getGreenHashKey()), 0.3) + + "/" + + Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenInvoice().getGreenHashIV()), 0.3) + + "/" + + Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenInvoice().getGreenEmail()), 0.3)); } if (!MyApp.getInstance().getEsunpay().getStoreID().isEmpty()) { statusList.add("download esunpay data:" - +Tools.randomReplace(MyApp.getInstance().getEsunpay().getStoreID(), 0.3) - +"/" - +Tools.randomReplace(MyApp.getInstance().getEsunpay().getTermID(), 0.3) - +"/" - +Tools.randomReplace(MyApp.getInstance().getEsunpay().getHash(), 0.3) - ); + + Tools.randomReplace(MyApp.getInstance().getEsunpay().getStoreID(), 0.3) + + "/" + + Tools.randomReplace(MyApp.getInstance().getEsunpay().getTermID(), 0.3) + + "/" + + Tools.randomReplace(MyApp.getInstance().getEsunpay().getHash(), 0.3)); } - if (MyApp.getInstance().getGreenpaySetting().getAppKey() != null && !MyApp.getInstance().getGreenpaySetting().getAppKey().isEmpty()) { + if (MyApp.getInstance().getGreenpaySetting().getAppKey() != null + && !MyApp.getInstance().getGreenpaySetting().getAppKey().isEmpty()) { statusList.add("download greenpay data:" - +Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenpaySetting().getAppId()), 0.3) - +"/" - +Tools.randomReplace(MyApp.getInstance().getGreenpaySetting().getAppKey(), 0.3) - +"/" - +Tools.randomReplace(MyApp.getInstance().getGreenpaySetting().getPartnerKey(), 0.3) - ); + + Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenpaySetting().getAppId()), 0.3) + + "/" + + Tools.randomReplace(MyApp.getInstance().getGreenpaySetting().getAppKey(), 0.3) + + "/" + + Tools.randomReplace(MyApp.getInstance().getGreenpaySetting().getPartnerKey(), 0.3)); statusList.add("download greenpay merchant data:" - +Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenpayMerchant().getLineMerchantId()), 0.3) - +"/" - +Tools.randomReplace(MyApp.getInstance().getGreenpayMerchant().getPsMerchantId(), 0.3) - +"/" - +Tools.randomReplace(MyApp.getInstance().getGreenpayMerchant().getEasyMerchantId(), 0.3) - +"/" - +Tools.randomReplace(MyApp.getInstance().getGreenpayMerchant().getPiMerchantId(), 0.3) - +"/" - +Tools.randomReplace(MyApp.getInstance().getGreenpayMerchant().getJkoMerchantId(), 0.3) - ); + + Tools.randomReplace(String.valueOf(MyApp.getInstance().getGreenpayMerchant().getLineMerchantId()), + 0.3) + + "/" + + Tools.randomReplace(MyApp.getInstance().getGreenpayMerchant().getPsMerchantId(), 0.3) + + "/" + + Tools.randomReplace(MyApp.getInstance().getGreenpayMerchant().getEasyMerchantId(), 0.3) + + "/" + + Tools.randomReplace(MyApp.getInstance().getGreenpayMerchant().getPiMerchantId(), 0.3) + + "/" + + Tools.randomReplace(MyApp.getInstance().getGreenpayMerchant().getJkoMerchantId(), 0.3)); } if (!MyApp.getInstance().getProductList().isEmpty()) { @@ -284,7 +286,7 @@ public class SettingActivity extends AppCompatActivityAbstract { statusList.add("download ad url data:" + size); } - recyclerSettingListAdpter.notifyItemInserted(statusList.size() -1); + recyclerSettingListAdpter.notifyItemInserted(statusList.size() - 1); updateProgressUI(100, "下載中..."); binding.progressLayout.setVisibility(View.GONE); diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/devs/vmc/RecyclerVmcBuyListAdpter.java b/app/src/main/java/com/unibuy/smartdevice/ui/devs/vmc/RecyclerVmcBuyListAdpter.java index 4b3e01e..1547c38 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/devs/vmc/RecyclerVmcBuyListAdpter.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/devs/vmc/RecyclerVmcBuyListAdpter.java @@ -37,7 +37,7 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter SlotList; private FragmentAbstract fragment; - private boolean haveThings,doorError; + private boolean haveThings, doorError; public RecyclerVmcBuyListAdpter(FragmentAbstract fragment, List slotList) { this.logs = new Logs(this.getClass()); @@ -59,7 +59,8 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter 0? machinePrice: sellingPrice; + int price = machinePrice > 0 ? machinePrice : sellingPrice; - holder.binding.textPrice.setText("NT$ "+price); + holder.binding.textPrice.setText("NT$ " + price); holder.binding.mask.setVisibility(View.GONE); holder.binding.textMask.setText(""); if (count <= 0) { holder.binding.mask.setVisibility(View.VISIBLE); - holder.binding.textMask.setText("補貨中");//已經售完 + holder.binding.textMask.setText("補貨中");// 已經售完 } if (isLock) { @@ -118,13 +118,13 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter 0 && !isLock) { holder.binding.imageProductPicture.setFocusable(false); - holder.binding.imageProductPicture.setFocusableInTouchMode(false); - holder.binding.imageProductPicture.setOnClickListener(new imageProductPictureOnClickListener(position)); + holder.binding.imageProductPicture.setFocusableInTouchMode(false); + holder.binding.imageProductPicture.setOnClickListener(new imageProductPictureOnClickListener(position)); } else { holder.binding.imageProductPicture.setOnClickListener(null); } - //devRunVMC(0); + // devRunVMC(0); } @Override @@ -134,7 +134,7 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter { AlertDialog dialog = new AlertDialog.Builder(context) @@ -228,7 +235,8 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter { d.dismiss(); - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(), "force close"); + handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(), + "force close"); }) .create(); @@ -238,11 +246,12 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter { if (dialog.isShowing()) { dialog.dismiss(); - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(), "force close"); + handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(), + "force close"); } }, 10_000); // 10 秒(10000 毫秒) }); - }else if (doorError) { + } else if (doorError) { // 取貨口門未關閉,提示使用者並阻止後續購買流程 doorError = false; handlerMain.start("RecycleFlowerBuyListAdpter", "取貨口門未正常關閉,請先確認門已關好"); @@ -254,7 +263,8 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter { d.dismiss(); - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(), "force close"); + handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(), + "force close"); }) .create(); @@ -264,7 +274,8 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter { if (dialog.isShowing()) { dialog.dismiss(); - handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(), "force close"); + handlerMain.start(getClass().getSimpleName(), BuyDialog.Option.FORCE_CLOSE.getOption(), + "force close"); } }, 10_000); // 10 秒(10000 毫秒) }); @@ -292,24 +303,28 @@ public class RecyclerVmcBuyListAdpter extends RecyclerView.Adapter 15) { - getSrcHandlerMain().start(getClass().getSimpleName(), DispatchDialog.Option.ERROR_PROCESS_GOODS.getOption(), "請檢查商品有無取出再確認取貨"); + getSrcHandlerMain().start(getClass().getSimpleName(), + DispatchDialog.Option.ERROR_PROCESS_GOODS.getOption(), "請檢查商品有無取出再確認取貨"); } else { -// getSrcHandlerMain().start(getClass().getSimpleName(), Option.SET_MESSAGE_TEXT.getOption(), "檢查中"); -// MyApp.getInstance().getDevXinYuanController().getReadBuffer(new DevSelectSlotByMICO(getHandlerMain(), slot, isMico, isHeading)); + // getSrcHandlerMain().start(getClass().getSimpleName(), + // Option.SET_MESSAGE_TEXT.getOption(), "檢查中"); + // MyApp.getInstance().getDevXinYuanController().getReadBuffer(new + // DevSelectSlotByMICO(getHandlerMain(), slot, isMico, isHeading)); checkCount++; } } } else { if (testCount > 15) { -// getSrcHandlerMain().start(getClass().getSimpleName(), DispatchDialog.Option.ERROR_PROCESS_GOODS.getOption(), "下位機無回應 請等3秒後再重試"); + // getSrcHandlerMain().start(getClass().getSimpleName(), + // DispatchDialog.Option.ERROR_PROCESS_GOODS.getOption(), "下位機無回應 請等3秒後再重試"); } else { -// MyApp.getInstance().getDevXinYuanController().getReadBufferByNow(new DevSelectSlotByMICO(getHandlerMain(), slot, isMico, isHeading)); -// MyApp.getInstance().getDevXinYuanController().addSendBuffer(MyApp.getInstance().getDevXinYuanController().getDevXinYuan().selectSlot(slot)); + // MyApp.getInstance().getDevXinYuanController().getReadBufferByNow(new + // DevSelectSlotByMICO(getHandlerMain(), slot, isMico, isHeading)); + // MyApp.getInstance().getDevXinYuanController().addSendBuffer(MyApp.getInstance().getDevXinYuanController().getDevXinYuan().selectSlot(slot)); testCount++; } } diff --git a/app/src/main/java/com/unibuy/smartdevice/ui/tools/ImageGlide.java b/app/src/main/java/com/unibuy/smartdevice/ui/tools/ImageGlide.java index cc027b5..3684b86 100644 --- a/app/src/main/java/com/unibuy/smartdevice/ui/tools/ImageGlide.java +++ b/app/src/main/java/com/unibuy/smartdevice/ui/tools/ImageGlide.java @@ -22,6 +22,8 @@ import com.unibuy.smartdevice.exception.LogsFileNullException; import com.unibuy.smartdevice.exception.LogsIOException; import com.unibuy.smartdevice.exception.LogsMalformedURLException; import com.unibuy.smartdevice.tools.HttpConnect; +import com.unibuy.smartdevice.tools.Tools; +import android.util.Log; import java.io.File; import java.security.MessageDigest; @@ -33,8 +35,9 @@ public class ImageGlide { this.context = context; } - public void fileload(String urlString, ImageView imageView) throws LogsEmptyException{ - if (imageView == null) return; + public void fileload(String urlString, ImageView imageView) throws LogsEmptyException { + if (imageView == null) + return; Context ctx = imageView.getContext(); if (ctx instanceof Activity) { @@ -45,7 +48,6 @@ public class ImageGlide { } } - HttpConnect httpConnect = new HttpConnect(); try { File imageFile = httpConnect.getFileFromUrl(urlString); @@ -158,4 +160,25 @@ public class ImageGlide { messageDigest.update((byte) alpha); } } + + /** + * 清理商品圖片暫存 (Tools.getExternalFilesDir()) + */ + public static void clearProductCache(Context context) { + File dir = Tools.getExternalFilesDir(); + if (dir != null && dir.isDirectory()) { + File[] files = dir.listFiles(); + if (files != null) { + for (File file : files) { + String name = file.getName().toLowerCase(); + if (name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") + || name.endsWith(".webp")) { + if (file.delete()) { + Log.i("ImageGlide", "Deleted product cache: " + name); + } + } + } + } + } + } }