diff --git a/app/build.gradle b/app/build.gradle
index e2c31e7..d1d6963 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,7 +10,7 @@ plugins {
// =========================================================================
def verMajor = 10
def verMinor = 1
-def verPatch = 1
+def verPatch = 6
android {
namespace 'com.unibuy.smartdevice'
diff --git a/app/src/main/java/com/unibuy/smartdevice/MainActivity.java b/app/src/main/java/com/unibuy/smartdevice/MainActivity.java
index b7d1491..497f3e0 100644
--- a/app/src/main/java/com/unibuy/smartdevice/MainActivity.java
+++ b/app/src/main/java/com/unibuy/smartdevice/MainActivity.java
@@ -17,8 +17,11 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
+import android.view.LayoutInflater;
+import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
+import android.widget.TextView;
import android.widget.Toast;
import androidx.core.graphics.Insets;
@@ -69,6 +72,8 @@ public class MainActivity extends AppCompatActivityAbstract {
return this.getClass();
}
+ private AlertDialog activeLoginDialog;
+
@Override
protected HandlerMain setHandlerMain() {
return new ToastHandlerMain(getCtx(), getLogs()) {
@@ -76,6 +81,11 @@ public class MainActivity extends AppCompatActivityAbstract {
public void execute(Context context, int commandCode, String message) {
super.execute(context, commandCode, message);
if ("登入成功".equals(message)) {
+ updateLoginStatus();
+ if (activeLoginDialog != null && activeLoginDialog.isShowing()) {
+ activeLoginDialog.dismiss();
+ activeLoginDialog = null;
+ }
if (pendingAction != null) {
pendingAction.run();
pendingAction = null;
@@ -111,79 +121,66 @@ public class MainActivity extends AppCompatActivityAbstract {
// ---- 環境尺寸設定 ----
initialData();
+ updateLoginStatus();
+
+ binding.btnStatusAction.setOnClickListener(v -> {
+ if (MyApp.getInstance().getSuAccess().isEmpty()) {
+ showLoginDialog(getCtx(), this::updateLoginStatus);
+ } else {
+ MyApp.getInstance().setSuAccess("");
+ updateLoginStatus();
+ Toast.makeText(getCtx(), "已登出後台權限", Toast.LENGTH_SHORT).show();
+ }
+ });
binding.buttonGotoSetting.setOnClickListener(v -> {
- pendingAction = new Runnable() {
- @Override
- public void run() {
- getTools().gotoActivity(SettingActivity.class);
- }
- };
+ Runnable action = () -> getTools().gotoActivity(SettingActivity.class);
if (!MyApp.getInstance().getMachine().getMachineID().isEmpty()) {
if (MyApp.getInstance().getSuAccess().isEmpty()) {
- showLoginDialog(getCtx());
+ showLoginDialog(getCtx(), action);
return;
}
}
- pendingAction.run();
- pendingAction = null;
+ action.run();
});
binding.buttonGotoSystemSet.setOnClickListener(v -> {
- pendingAction = new Runnable() {
- @Override
- public void run() {
- getTools().gotoActivity(SystemSetActivity.class);
- }
- };
+ Runnable action = () -> getTools().gotoActivity(SystemSetActivity.class);
if (MyApp.getInstance().getMachine().getMachineID().isEmpty()) {
getHandlerMain().start("MainActivity", "@先綁定機器");
return;
}
if (MyApp.getInstance().getSuAccess().isEmpty()) {
- showLoginDialog(getCtx());
+ showLoginDialog(getCtx(), action);
return;
}
- pendingAction.run();
- pendingAction = null;
+ action.run();
});
binding.buttonGotoProductList.setOnClickListener(v -> {
- pendingAction = new Runnable() {
- @Override
- public void run() {
- getTools().gotoActivity(ProductListActivity.class);
- }
- };
+ Runnable action = () -> getTools().gotoActivity(ProductListActivity.class);
if (MyApp.getInstance().getMachine().getMachineID().isEmpty()) {
getHandlerMain().start("MainActivity", "@先綁定機器");
return;
}
if (MyApp.getInstance().getSuAccess().isEmpty()) {
- showLoginDialog(getCtx());
+ showLoginDialog(getCtx(), action);
return;
}
- pendingAction.run();
- pendingAction = null;
+ action.run();
});
binding.buttonGotoSlotList.setOnClickListener(v -> {
- pendingAction = new Runnable() {
- @Override
- public void run() {
- getTools().gotoActivity(VmcSlotListActivity.class);
- }
- };
+ Runnable action = () -> getTools().gotoActivity(VmcSlotListActivity.class);
if (MyApp.getInstance().getMachine().getMachineID().isEmpty()) {
getHandlerMain().start("MainActivity", "@先綁定機器");
return;
}
if (MyApp.getInstance().getSuAccess().isEmpty()) {
- showLoginDialog(getCtx());
+ showLoginDialog(getCtx(), action);
return;
}
- pendingAction.run();
- pendingAction = null;
+ action.run();
});
binding.buttonGotoShoppingPlatform.setOnClickListener(v -> {
@@ -247,17 +244,18 @@ public class MainActivity extends AppCompatActivityAbstract {
});
binding.buttonGotoXyshjTest.setOnClickListener(v -> {
+ Runnable action = () -> getTools().gotoActivity(XyshjTestActivity.class);
if (MyApp.getInstance().getMachine().getMachineID().isEmpty()) {
getHandlerMain().start("MainActivity", "@先綁定機器");
return;
}
if (MyApp.getInstance().getSuAccess().isEmpty()) {
- showLoginDialog(getCtx());
+ showLoginDialog(getCtx(), action);
return;
}
- getTools().gotoActivity(XyshjTestActivity.class);
+ action.run();
});
binding.buttonresetapp.setOnClickListener(v -> {
@@ -265,12 +263,15 @@ public class MainActivity extends AppCompatActivityAbstract {
ReSetAPP();
});
- binding.buttonOpenSystemSettings.setOnClickListener(v -> {
- // 開啟 Android 系統設定
- Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
+ binding.buttonGotoAndroidSettings.setOnClickListener(v -> {
+ try {
+ Intent intent = new Intent(android.provider.Settings.ACTION_SETTINGS);
+ startActivity(intent);
+ } catch (Exception e) {
+ Toast.makeText(getCtx(), "無法開啟安卓設定頁", Toast.LENGTH_SHORT).show();
+ }
});
+
}
private static class ButtonQrCodeTestOnClick extends HandlerMainSchedulerOnClick {
@@ -476,10 +477,16 @@ public class MainActivity extends AppCompatActivityAbstract {
}
private void initialData() {
- if (MyApp.getInstance().getMachine() != null || MyApp.getInstance().getMachine().getMachineID().isEmpty()) {
+ if (MyApp.getInstance().getMachine() != null && MyApp.getInstance().getMachine().getMachineID().isEmpty()) {
SettingsDao settingsDao = new SettingsDao(this);
SettingStructure setting = settingsDao.getOne(MyApp.getInstance().getMachine().getClass().getSimpleName());
- MyApp.getInstance().getMachine().setMachineID(setting.getData(1));
+ if (setting != null && setting.getDataCount() >= 2) {
+ MyApp.getInstance().getMachine().setApiKey(setting.getData(0));
+ MyApp.getInstance().getMachine().setMachineID(setting.getData(1));
+ if (setting.getDataCount() >= 3 && setting.getData(2) != null && !setting.getData(2).isEmpty()) {
+ MyApp.getInstance().getMachine().setMqttEnv(setting.getData(2));
+ }
+ }
settingsDao.close();
}
}
@@ -518,88 +525,94 @@ public class MainActivity extends AppCompatActivityAbstract {
}
public void showLoginDialog(Context context) {
- // 創建一個垂直的 LinearLayout
- LinearLayout layout = new LinearLayout(context);
- layout.setOrientation(LinearLayout.VERTICAL);
- layout.setPadding(50, 20, 50, 10);
+ showLoginDialog(context, null);
+ }
- // 帳號輸入框
- final EditText accessInput = new EditText(context);
- accessInput.setHint("帳號");
- layout.addView(accessInput);
+ public void showLoginDialog(Context context, Runnable onSuccess) {
+ // 使用 LayoutInflater 載入自訂佈局
+ View view = LayoutInflater.from(context).inflate(R.layout.dialog_admin_login, null);
+
+ final EditText accessInput = view.findViewById(R.id.etAccess);
+ final EditText passwordInput = view.findViewById(R.id.etPassword);
+ final CheckBox rememberAccountCb = view.findViewById(R.id.cbRemember);
+ androidx.appcompat.widget.AppCompatButton positiveButton = view.findViewById(R.id.btnLogin);
+ androidx.appcompat.widget.AppCompatButton negativeButton = view.findViewById(R.id.btnCancel);
getTools().setImmHideByFocusChange(accessInput);
getTools().setImmOpenByFocusChange(accessInput);
-
- // 密碼輸入框
- final EditText passwordInput = new EditText(context);
- passwordInput.setHint("密碼");
- passwordInput.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
- layout.addView(passwordInput);
-
getTools().setImmHideByFocusChange(passwordInput);
+ // 讀取紀錄的帳號
+ android.content.SharedPreferences prefs = context.getSharedPreferences("LoginPrefs", Context.MODE_PRIVATE);
+ String savedAccount = prefs.getString("saved_account", "");
+ if (!savedAccount.isEmpty()) {
+ accessInput.setText(savedAccount);
+ }
+ rememberAccountCb.setChecked(!savedAccount.isEmpty()); // 如果有存過帳號,預設打勾
+
+ // 建立對話框
AlertDialog dialog = new AlertDialog.Builder(context)
- .setTitle("登入")
- .setView(layout)
- .setPositiveButton("登入", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- String access = accessInput.getText().toString().trim();
- String password = passwordInput.getText().toString().trim();
+ .setView(view)
+ .create();
+ activeLoginDialog = dialog;
- if (access.isEmpty() || password.isEmpty()) {
- Toast.makeText(context, "請輸入帳號和密碼", Toast.LENGTH_SHORT).show();
- } else {
- Toast.makeText(context, "正在驗證登入...", Toast.LENGTH_SHORT).show();
- starCloudAPI.login(access, password);
- }
+ // 設定對話框視窗背景為透明,以便呈現圓角設計
+ if (dialog.getWindow() != null) {
+ dialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
+ }
- getTools().hideSystemBars((Activity) getCtx());
- }
- })
- .setNegativeButton("取消", new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- getTools().hideSystemBars((Activity) getCtx());
- dialog.dismiss();
- }
- }).create();
+ // 設定點擊事件
+ positiveButton.setOnClickListener(v -> {
+ String access = accessInput.getText().toString().trim();
+ String password = passwordInput.getText().toString().trim();
- // 🛠️ Debug Backdoor: 長按對話框標題 3 秒跳過登入
- dialog.setOnShowListener(d -> {
- int titleId = context.getResources().getIdentifier("alertTitle", "id", "android");
- View titleView = dialog.findViewById(titleId);
- if (titleView != null) {
- final Handler titleHandler = new Handler();
- final Runnable titleRunnable = () -> {
- Toast.makeText(context, "🛠️ Debug Mode: 跳過登入", Toast.LENGTH_SHORT).show();
- MyApp.getInstance().setSuAccess("DEBUG_MODE");
- if (pendingAction != null) {
- pendingAction.run();
- pendingAction = null;
- }
- dialog.dismiss();
- };
-
- titleView.setOnTouchListener((v, event) -> {
- switch (event.getAction()) {
- case android.view.MotionEvent.ACTION_DOWN:
- titleHandler.postDelayed(titleRunnable, 2000); // 2秒
- break;
- case android.view.MotionEvent.ACTION_UP:
- case android.view.MotionEvent.ACTION_CANCEL:
- titleHandler.removeCallbacks(titleRunnable);
- break;
- }
- return true;
- });
+ if (access.isEmpty() || password.isEmpty()) {
+ Toast.makeText(context, "請輸入帳號和密碼", Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(context, "正在驗證登入...", Toast.LENGTH_SHORT).show();
+
+ // 為了處理 StarCloudAPI 登入成功後的回調
+ // 我們重新設計 handlerMain 讓它能夠在 onSuccess 回調中跑 pendingAction
+ pendingAction = onSuccess;
+ starCloudAPI.login(access, password);
+
+ if (rememberAccountCb.isChecked()) {
+ prefs.edit().putString("saved_account", access).apply();
+ } else {
+ prefs.edit().remove("saved_account").apply();
+ }
+ // 🛠️ 關鍵優化:不再此處立即 dismiss(),改由 API 回報成功時關閉
}
+ getTools().hideSystemBars((Activity) getCtx());
+ });
+
+ negativeButton.setOnClickListener(v -> {
+ getTools().hideSystemBars((Activity) getCtx());
+ dialog.dismiss();
+ activeLoginDialog = null;
});
dialog.show();
}
+ /**
+ * 更新右上角登入狀態顯示
+ */
+ private void updateLoginStatus() {
+ String suAccess = MyApp.getInstance().getSuAccess();
+ if (suAccess.isEmpty()) {
+ binding.tvWelcomeTitle.setText("歡迎使用 AAT\n智能販賣機系統");
+ binding.tvWelcomeHint.setText("請點擊右上角登入帳號");
+ binding.btnStatusAction.setText("登入");
+ binding.btnStatusAction.setBackgroundTintList(android.content.res.ColorStateList.valueOf(getResources().getColor(R.color.primary)));
+ } else {
+ binding.tvWelcomeTitle.setText("歡迎使用 AAT\n" + suAccess);
+ binding.tvWelcomeHint.setText("請點擊下方按鈕操作");
+ binding.btnStatusAction.setText("登出");
+ binding.btnStatusAction.setBackgroundTintList(android.content.res.ColorStateList.valueOf(getResources().getColor(R.color.error)));
+ }
+ }
+
private void enterShoppingPlatform() {
MyApp.getInstance().setSuAccess("");
getTools().gotoActivity(FontendActivity.class);
@@ -634,15 +647,15 @@ public class MainActivity extends AppCompatActivityAbstract {
super.onStart();
if (MyApp.getInstance().getMachine().getMachineID().isEmpty()) {
- binding.buttonGotoSystemSet.setVisibility(View.GONE);
- binding.buttonGotoProductList.setVisibility(View.GONE);
- binding.buttonGotoSlotList.setVisibility(View.GONE);
- binding.buttonGotoShoppingPlatform.setVisibility(View.GONE);
+ binding.wrapButtonGotoSystemSet.setVisibility(View.GONE);
+ binding.wrapButtonGotoProductList.setVisibility(View.GONE);
+ binding.wrapButtonGotoSlotList.setVisibility(View.GONE);
+ binding.wrapButtonGotoShoppingPlatform.setVisibility(View.GONE);
} else {
- binding.buttonGotoSystemSet.setVisibility(View.VISIBLE);
- binding.buttonGotoProductList.setVisibility(View.VISIBLE);
- binding.buttonGotoSlotList.setVisibility(View.VISIBLE);
- binding.buttonGotoShoppingPlatform.setVisibility(View.VISIBLE);
+ binding.wrapButtonGotoSystemSet.setVisibility(View.VISIBLE);
+ binding.wrapButtonGotoProductList.setVisibility(View.VISIBLE);
+ binding.wrapButtonGotoSlotList.setVisibility(View.VISIBLE);
+ binding.wrapButtonGotoShoppingPlatform.setVisibility(View.VISIBLE);
}
}
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/bg_button_outline.xml b/app/src/main/res/drawable/bg_button_outline.xml
new file mode 100644
index 0000000..a48aff9
--- /dev/null
+++ b/app/src/main/res/drawable/bg_button_outline.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/bg_button_primary.xml b/app/src/main/res/drawable/bg_button_primary.xml
new file mode 100644
index 0000000..37dcebb
--- /dev/null
+++ b/app/src/main/res/drawable/bg_button_primary.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/bg_input_modern.xml b/app/src/main/res/drawable/bg_input_modern.xml
new file mode 100644
index 0000000..46ec401
--- /dev/null
+++ b/app/src/main/res/drawable/bg_input_modern.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/binding_machine.png b/app/src/main/res/drawable/binding_machine.png
new file mode 100644
index 0000000..b216654
Binary files /dev/null and b/app/src/main/res/drawable/binding_machine.png differ
diff --git a/app/src/main/res/drawable/card_reader_checkout.png b/app/src/main/res/drawable/card_reader_checkout.png
new file mode 100644
index 0000000..5baa1ea
Binary files /dev/null and b/app/src/main/res/drawable/card_reader_checkout.png differ
diff --git a/app/src/main/res/drawable/card_reader_reboot.png b/app/src/main/res/drawable/card_reader_reboot.png
new file mode 100644
index 0000000..c8a525e
Binary files /dev/null and b/app/src/main/res/drawable/card_reader_reboot.png differ
diff --git a/app/src/main/res/drawable/cargo_lane_management.png b/app/src/main/res/drawable/cargo_lane_management.png
new file mode 100644
index 0000000..4507ac9
Binary files /dev/null and b/app/src/main/res/drawable/cargo_lane_management.png differ
diff --git a/app/src/main/res/drawable/product_list.png b/app/src/main/res/drawable/product_list.png
new file mode 100644
index 0000000..00281d2
Binary files /dev/null and b/app/src/main/res/drawable/product_list.png differ
diff --git a/app/src/main/res/drawable/reboot_app.png b/app/src/main/res/drawable/reboot_app.png
new file mode 100644
index 0000000..e9d08f4
Binary files /dev/null and b/app/src/main/res/drawable/reboot_app.png differ
diff --git a/app/src/main/res/drawable/sales_page.png b/app/src/main/res/drawable/sales_page.png
new file mode 100644
index 0000000..64022fe
Binary files /dev/null and b/app/src/main/res/drawable/sales_page.png differ
diff --git a/app/src/main/res/drawable/scanner_test.png b/app/src/main/res/drawable/scanner_test.png
new file mode 100644
index 0000000..d69e0d5
Binary files /dev/null and b/app/src/main/res/drawable/scanner_test.png differ
diff --git a/app/src/main/res/drawable/system_settings.png b/app/src/main/res/drawable/system_settings.png
new file mode 100644
index 0000000..8c2a76d
Binary files /dev/null and b/app/src/main/res/drawable/system_settings.png differ
diff --git a/app/src/main/res/drawable/taiwan_star_tech_logo.png b/app/src/main/res/drawable/taiwan_star_tech_logo.png
new file mode 100644
index 0000000..cec04eb
Binary files /dev/null and b/app/src/main/res/drawable/taiwan_star_tech_logo.png differ
diff --git a/app/src/main/res/drawable/vending_machine_scenario_v1_remove_background.png b/app/src/main/res/drawable/vending_machine_scenario_v1_remove_background.png
new file mode 100644
index 0000000..a494bee
Binary files /dev/null and b/app/src/main/res/drawable/vending_machine_scenario_v1_remove_background.png differ
diff --git a/app/src/main/res/drawable/vending_machine_scenario_v2_remove_background.png b/app/src/main/res/drawable/vending_machine_scenario_v2_remove_background.png
new file mode 100644
index 0000000..d88a747
Binary files /dev/null and b/app/src/main/res/drawable/vending_machine_scenario_v2_remove_background.png differ
diff --git a/app/src/main/res/drawable/view_log_records.png b/app/src/main/res/drawable/view_log_records.png
new file mode 100644
index 0000000..a7dacb1
Binary files /dev/null and b/app/src/main/res/drawable/view_log_records.png differ
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index 8547462..d8860d3 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -6,276 +6,822 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
- android:background="@color/light_gray">
+ android:background="@color/white">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ app:layout_constraintTop_toBottomOf="@+id/headerBarrier">
+ android:padding="24dp">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ android:layout_height="1dp"
+ android:layout_marginTop="48dp"
+ android:layout_marginBottom="48dp"
+ android:background="#E0E0E0" />
-
+
-
\ No newline at end of file
+
diff --git a/app/src/main/res/layout/dialog_admin_login.xml b/app/src/main/res/layout/dialog_admin_login.xml
new file mode 100644
index 0000000..17b724e
--- /dev/null
+++ b/app/src/main/res/layout/dialog_admin_login.xml
@@ -0,0 +1,114 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-sw720dp/dimens.xml b/app/src/main/res/values-sw720dp/dimens.xml
new file mode 100644
index 0000000..4efbe87
--- /dev/null
+++ b/app/src/main/res/values-sw720dp/dimens.xml
@@ -0,0 +1,25 @@
+
+
+
+ 24dp
+ 24dp
+ 24dp
+ 68dp
+ 165dp
+
+
+ 26sp
+ 22sp
+ 28sp
+ 360dp
+ 255dp
+
+
+ 26sp
+
+
+ 26sp
+ 108dp
+ 12dp
+ 24dp
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index b4530cb..00f20fa 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -1,6 +1,6 @@
- #FF000000
+ #272727
#FFFFFFFF
#FFFF0000
#FFA9A9A9
@@ -8,4 +8,57 @@
#AA000000
#80FFFFFF
#00000000
+
+
+
+ #005DAD
+ #FFFFFF
+ #D1E4FF
+ #001D36
+
+
+ #535F70
+ #FFFFFF
+ #D7E3F7
+ #101C2B
+
+
+ #2E7D32
+ #FFFFFF
+ #C8E6C9
+ #003300
+
+
+ #EF6C00
+ #FFFFFF
+ #FFE0B2
+ #4A2600
+
+
+ #BA1A1A
+ #FFFFFF
+ #FFDAD6
+ #410002
+
+
+ #00639B
+ #FFFFFF
+ #D1E4FF
+ #001D35
+
+
+ #F5F7FA
+
+ #F8F9FF
+ #191C1E
+ #DFE2EB
+ #43474E
+
+ #F8F9FF
+ #191C1E
+
+
+ #73777F
+ #C3C7CF
+ #DFE2EB
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index 357afbc..4aefb6d 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -5,4 +5,20 @@
16dp
45dp
110dp
+
+
+ 18sp
+ 16sp
+ 20sp
+ 240dp
+ 170dp
+
+
+ 18sp
+
+
+ 18sp
+ 72dp
+ 8dp
+ 16dp
\ No newline at end of file