[FEAT] 實作遠端更新商品指令與圖片顯示優化

1. 在 MqttService 中新增 update_products 指令處理,支援遠端觸發商品資料同步。
2. 優化 ImageGlide 邏輯,移除從網路載入圖片時的淡化濾鏡,確保圖片即時顯示原色。
3. 改進 ProductListActivity 的圖片預載機制,支援逐張商品即時刷新 UI 提升互動感。
4. 更新 .gitignore 以排除 .idea 目錄及 Agent 產生的規劃檔案。
5. 配合環境調整 Gradle 配置與 SDK 路徑。
This commit is contained in:
sky121113 2026-05-14 11:13:42 +08:00
parent 8ae4ae9c14
commit 45853d0b9d
21 changed files with 95 additions and 81 deletions

7
.gitignore vendored
View File

@ -1,12 +1,7 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
/.idea/
.DS_Store
/build
/captures

View File

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="AndroidProjectSystem">
<option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
</component>
</project>

View File

@ -1,9 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
</GradleProjectSettings>
</option>
</component>
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MarkdownSettings">
<option name="previewPanelProviderInfo">
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
</option>
</component>
</project>

View File

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/TaiwanStar_general_size_s_Chengwai.iml" filepath="$PROJECT_DIR$/.idea/TaiwanStar_general_size_s_Chengwai.iml" />
</modules>
</component>
</project>

View File

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>

3
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "interactive"
}

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/smartdevice.iml" filepath="$PROJECT_DIR$/smartdevice.iml" />
</modules>
</component>
</project>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/../../../../../../.." vcs="Git" />
</component>
</project>

View File

@ -24,12 +24,15 @@ import com.unibuy.smartdevice.external.mqtt.MqttManager;
import com.unibuy.smartdevice.structure.BuyStructure;
import com.unibuy.smartdevice.structure.ReportFlowInfoStructure;
import com.unibuy.smartdevice.structure.SlotStructure;
import com.unibuy.smartdevice.structure.ProductStructure;
import com.unibuy.smartdevice.structure.mqtt.TransactionFinalizePayload;
import com.unibuy.smartdevice.tools.TransactionFinalizeBuilder;
import com.unibuy.smartdevice.ui.tools.ImageGlide;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.UUID;
@ -274,6 +277,62 @@ public class MqttService extends Service {
};
// 使用新架構 StarCloudAPI 執行同步
new com.unibuy.smartdevice.external.StarCloudAPI(adHandler, adHandler).downloadAdvertisement();
} else if ("update_products".equals(cmd)) {
logs.info("Executing remote product sync command...");
// 📢 在主執行緒顯示 Toast 提示使用者
new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
android.widget.Toast.makeText(getApplicationContext(),
"📢 商品更新中...", android.widget.Toast.LENGTH_SHORT).show());
com.unibuy.smartdevice.tools.HandlerMain productHandler = new com.unibuy.smartdevice.tools.HandlerMain(
getApplicationContext(), logs) {
@Override
protected void execute(Context context, int code, String message) {
logs.info("Product sync event: [" + code + "] " + message);
if (code == 200 && "商品下載成功".equals(message)) {
// 背景預載圖片
new Thread(() -> {
try {
List<ProductStructure> products = MyApp.getInstance().getProductList();
int total = products.size();
logs.info("Starting image preloading for " + total + " products...");
for (int i = 0; i < total; i++) {
ProductStructure p = products.get(i);
if (p.getProductImg() != null && !p.getProductImg().isEmpty()) {
try {
new ImageGlide(getApplicationContext()).preload(p.getProductImg());
} catch (Exception e) {
logs.info("Failed to preload image for product " + p.getProductID() + ": " + e.getMessage());
}
}
}
logs.info("Product sync and image preloading completed.");
mqttManager.publishCommandAck(cmdId, "success", "Product sync completed successfully");
new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
android.widget.Toast.makeText(context,
"✅ 商品更新完成", android.widget.Toast.LENGTH_SHORT).show());
} catch (Exception e) {
logs.error(com.unibuy.smartdevice.exception.ErrorCode.UNKNOWN_ERROR, "Error during image preloading: " + e.getMessage());
mqttManager.publishCommandAck(cmdId, "failed", "Image preloading error: " + e.getMessage());
}
}).start();
} else if (code >= 400) {
mqttManager.publishCommandAck(cmdId, "failed", "Product sync failed: " + message);
new android.os.Handler(android.os.Looper.getMainLooper()).post(() ->
android.widget.Toast.makeText(context,
"❌ 商品更新失敗: " + message, android.widget.Toast.LENGTH_LONG).show());
}
}
};
// 1. 清除舊圖片快取
ImageGlide.clearProductCache(getApplicationContext());
// 2. 開始下載資料
new com.unibuy.smartdevice.external.StarCloudAPI(productHandler, productHandler).downloadProductInfo();
} else if ("update_inventory".equals(cmd)) {
logs.info("Executing update_inventory command...");
try {

View File

@ -201,6 +201,9 @@ public class ProductListActivity extends AppCompatActivityAbstract {
try {
new ImageGlide(handlerMain.getContext()).preload(product.getProductImg());
// 下載完成後立即刷新該項目的 UI (使 ImageGlide 重新判斷檔案存在由淡變亮)
final int index = i;
runOnUiThread(() -> recyclerProductListAdpter.notifyItemChanged(index));
} catch (LogsEmptyException e) {
getLogs().warning(e);
}

View File

@ -62,7 +62,6 @@ public class ImageGlide {
Glide.with(imageView).load(urlString)
.placeholder(R.drawable.loading)
.error(R.drawable.loading)
.transform(new WhiteTintTransformation(128))
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.signature(new ObjectKey(System.currentTimeMillis())) // 強制更新
.into(imageView);
@ -71,7 +70,6 @@ public class ImageGlide {
Glide.with(imageView).load(urlString)
.placeholder(R.drawable.loading)
.error(R.drawable.loading)
.transform(new WhiteTintTransformation(128))
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.signature(new ObjectKey(System.currentTimeMillis())) // 強制更新
.into(imageView);
@ -103,7 +101,6 @@ public class ImageGlide {
Glide.with(context.getApplicationContext()).load(urlString)
.placeholder(R.drawable.loading)
.error(R.drawable.loading)
.transform(new WhiteTintTransformation(128))
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.preload();
}

View File

@ -1,2 +1,12 @@
#toolchainVendor=jetbrains
#toolchainVersion=21
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/65aaef917b9f394804f058f1861225c9/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/c728c5388b044fbdbbc44b0c6acee0df/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/65aaef917b9f394804f058f1861225c9/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/c728c5388b044fbdbbc44b0c6acee0df/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/dc463b4a8183dbcaa1b32544189c7f03/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/cb7dc109dd590ebca2d703734d23c9d3/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/65aaef917b9f394804f058f1861225c9/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/c728c5388b044fbdbbc44b0c6acee0df/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/43ee83889b87bacad5d3071ae7bbd349/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/2d57bdd1e17a18f83ff073919daa35ba/redirect
toolchainVersion=17

View File

@ -1,5 +1,5 @@
[versions]
agp = "8.9.0"
agp = "8.13.2"
junit = "4.13.2"
junitVersion = "1.2.1"
espressoCore = "3.6.1"

View File

@ -1,6 +1,6 @@
#Sat Mar 22 19:54:26 CST 2025
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists