diff --git a/app/src/main/java/com/miraclegarden/smsmessage/ActionConfirmDialog.java b/app/src/main/java/com/miraclegarden/smsmessage/ActionConfirmDialog.java index 6c1aa00..0b77b78 100644 --- a/app/src/main/java/com/miraclegarden/smsmessage/ActionConfirmDialog.java +++ b/app/src/main/java/com/miraclegarden/smsmessage/ActionConfirmDialog.java @@ -6,17 +6,22 @@ import android.os.Bundle; import android.text.TextUtils; import android.view.Gravity; import android.view.View; -import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; -import android.widget.ImageView; -import android.widget.TextView; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; import android.widget.Toast; import com.miraclegarden.smsmessage.databinding.DialogActionConfirmBinding; +import com.miraclegarden.smsmessage.model.ApiError; +import com.miraclegarden.smsmessage.model.BankInfo; +import com.miraclegarden.smsmessage.network.ApiService; + +import java.util.ArrayList; +import java.util.List; /** - * 通用弹窗 + * 选择银行账户弹窗(添加监听时使用) */ public class ActionConfirmDialog extends Dialog { private final Context context; @@ -24,65 +29,61 @@ public class ActionConfirmDialog extends Dialog { AppInfo appInfo; int index; DialogActionConfirmBinding actionConfirmBinding; + ApiService apiService; + List bankList = new ArrayList<>(); + BankInfo selectedBank = null; + public interface OnToActionListener { void toSumbit(AppInfo appInfo); void toCancel(); - } public void setOnToActionListener(OnToActionListener onNextCallListener) { this.onToActionListener = onNextCallListener; } - public ActionConfirmDialog(Context context, AppInfo appInfo) { super(context, R.style.MaterialDesignDialog); this.context = context; this.appInfo = appInfo; + this.apiService = new ApiService(context); } - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); actionConfirmBinding = DialogActionConfirmBinding.inflate(getLayoutInflater()); setContentView(actionConfirmBinding.getRoot()); + actionConfirmBinding.ivIcon.setImageDrawable(appInfo.getIcon()); actionConfirmBinding.tvAppname.setText(appInfo.getAppName()); actionConfirmBinding.tvPackage.setText(appInfo.getPackageName()); - if(!TextUtils.isEmpty(appInfo.getName())){ - actionConfirmBinding.tvName.setText(appInfo.getName()); - } - if(!TextUtils.isEmpty(appInfo.getCode())){ - actionConfirmBinding.tvCode.setText(appInfo.getCode()); - } - if(!TextUtils.isEmpty(appInfo.getRemark())){ - actionConfirmBinding.tvRemark.setText(appInfo.getRemark()); - } - actionConfirmBinding.sumbitTv.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - if(TextUtils.isEmpty(actionConfirmBinding.tvName.getText().toString().trim())){ - Toast.makeText(context,"名称不能为空",Toast.LENGTH_SHORT).show(); - return; - } - if(TextUtils.isEmpty(actionConfirmBinding.tvCode.getText().toString().trim())){ - Toast.makeText(context,"Code不能为空",Toast.LENGTH_SHORT).show(); - return; - } - appInfo.setName(actionConfirmBinding.tvName.getText().toString().trim()); - appInfo.setCode(actionConfirmBinding.tvCode.getText().toString().trim()); - appInfo.setRemark(actionConfirmBinding.tvRemark.getText().toString().trim()); - if(onToActionListener!=null){ - dismiss(); - onToActionListener.toSumbit(appInfo); - } + // 加载银行账户列表 + loadBankList(); + + actionConfirmBinding.sumbitTv.setOnClickListener(view -> { + if (selectedBank == null) { + Toast.makeText(context, "请选择关联的银行账户", Toast.LENGTH_SHORT).show(); + return; + } + + appInfo.setBankInfoId(selectedBank.getId()); + appInfo.setName(selectedBank.getBankName()); + appInfo.setCode(selectedBank.getAccount()); + if (selectedBank.getRemark() != null) { + appInfo.setRemark(selectedBank.getRemark()); + } + + if (onToActionListener != null) { + dismiss(); + onToActionListener.toSumbit(appInfo); } }); + actionConfirmBinding.cancelTv.setOnClickListener(view -> { dismiss(); - if(onToActionListener!=null){ + if (onToActionListener != null) { onToActionListener.toCancel(); } }); @@ -92,8 +93,83 @@ public class ActionConfirmDialog extends Dialog { wlp.gravity = Gravity.CENTER; wlp.width = WindowManager.LayoutParams.WRAP_CONTENT; wlp.height = WindowManager.LayoutParams.WRAP_CONTENT; - window.setAttributes(wlp); } + private void loadBankList() { + actionConfirmBinding.spinnerBank.setEnabled(false); + + apiService.getBankList(new ApiService.ApiCallback>() { + @Override + public void onSuccess(List result) { + if (context instanceof android.app.Activity) { + ((android.app.Activity) context).runOnUiThread(() -> { + bankList.clear(); + if (result != null) { + bankList.addAll(result); + } + + if (bankList.isEmpty()) { + Toast.makeText(context, "暂无银行账户,请先到银行账户管理添加", Toast.LENGTH_LONG).show(); + dismiss(); + return; + } + + setupSpinner(); + }); + } + } + + @Override + public void onError(ApiError error) { + if (context instanceof android.app.Activity) { + ((android.app.Activity) context).runOnUiThread(() -> { + Toast.makeText(context, "加载银行账户失败: " + error.getMessage(), Toast.LENGTH_LONG).show(); + dismiss(); + }); + } + } + }); + } + + private void setupSpinner() { + List bankNames = new ArrayList<>(); + bankNames.add("请选择银行账户"); + for (BankInfo bank : bankList) { + bankNames.add(bank.getDisplayName()); + } + + ArrayAdapter adapter = new ArrayAdapter<>(context, + android.R.layout.simple_spinner_item, bankNames); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + actionConfirmBinding.spinnerBank.setAdapter(adapter); + actionConfirmBinding.spinnerBank.setEnabled(true); + + actionConfirmBinding.spinnerBank.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position > 0) { + selectedBank = bankList.get(position - 1); + } else { + selectedBank = null; + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + selectedBank = null; + } + }); + + // 如果已有绑定的银行,选中它 + if (!TextUtils.isEmpty(appInfo.getBankInfoId())) { + for (int i = 0; i < bankList.size(); i++) { + if (appInfo.getBankInfoId().equals(bankList.get(i).getId())) { + actionConfirmBinding.spinnerBank.setSelection(i + 1); + selectedBank = bankList.get(i); + break; + } + } + } + } } diff --git a/app/src/main/java/com/miraclegarden/smsmessage/App.java b/app/src/main/java/com/miraclegarden/smsmessage/App.java index 9faf4bc..b1bf765 100644 --- a/app/src/main/java/com/miraclegarden/smsmessage/App.java +++ b/app/src/main/java/com/miraclegarden/smsmessage/App.java @@ -10,12 +10,21 @@ import android.text.TextUtils; import android.util.Base64; import android.util.Log; +import androidx.work.Constraints; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.NetworkType; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; + +import com.miraclegarden.smsmessage.service.NotificationHealthCheckWorker; + import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; public class App extends Application { private static final String TAG = "App"; @@ -28,6 +37,22 @@ public class App extends Application { MessageDigest messageDigest = getMessageDigest(); String signature = getSignature(this, packageName); Hash_value = getHashCode(packageName, messageDigest, signature); + scheduleHealthCheck(); + } + + private void scheduleHealthCheck() { + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build(); + + PeriodicWorkRequest healthCheck = new PeriodicWorkRequest.Builder( + NotificationHealthCheckWorker.class, 15, TimeUnit.MINUTES) + .setConstraints(constraints) + .build(); + WorkManager.getInstance(this).enqueueUniquePeriodicWork( + "notification_health_check", + ExistingPeriodicWorkPolicy.KEEP, + healthCheck); } public static MessageDigest getMessageDigest() { @@ -78,23 +103,26 @@ public class App extends Application { SharedPreferences sp = context.getSharedPreferences("server", Activity.MODE_PRIVATE); String messages = sp.getString("saveNotiList", ""); if (TextUtils.isEmpty(messages)) { - return null; + return new ArrayList<>(); } List messageInfos = GsonUtils.getListFromJSON(messages, MessageInfo.class); + if (messageInfos == null) { + return new ArrayList<>(); + } return messageInfos; } public static void deleteNotiBean(Context context, MessageInfo appInfo) { List messageInfos = getNotiList(context); - if (messageInfos == null) { + if (messageInfos.isEmpty()) { saveNotiList(context, ""); return; } for (int i = 0; i < messageInfos.size(); i++) { if (messageInfos.get(i).getPackageName().equals(appInfo.getPackageName())) { messageInfos.remove(i); - if (messageInfos.size() == 0) { + if (messageInfos.isEmpty()) { saveNotiList(context, ""); } else { saveNotiList(context, GsonUtils.beanToJSONString(messageInfos)); @@ -107,12 +135,6 @@ public class App extends Application { public static void saveNotiBean(Context context, AppInfo appInfo) { List messageInfos = getNotiList(context); - if (messageInfos == null) { - messageInfos = new ArrayList<>(); - messageInfos.add(MessageInfo.AppInfoToMessageInfo(appInfo)); - saveNotiList(context, GsonUtils.beanToJSONString(messageInfos)); - return; - } boolean isAdd = true; for (int i = 0; i < messageInfos.size(); i++) { if (messageInfos.get(i).getPackageName().equals(appInfo.getPackageName())) { @@ -120,10 +142,19 @@ public class App extends Application { messageInfos.get(i).setCode(appInfo.getCode()); messageInfos.get(i).setRemark(appInfo.getRemark()); isAdd = false; + break; } } - if(isAdd){ + if (isAdd) { messageInfos.add(MessageInfo.AppInfoToMessageInfo(appInfo)); + } else { + // 更新时也要同步 bankInfoId + for (int i = 0; i < messageInfos.size(); i++) { + if (messageInfos.get(i).getPackageName().equals(appInfo.getPackageName())) { + messageInfos.get(i).setBankInfoId(appInfo.getBankInfoId()); + break; + } + } } saveNotiList(context, GsonUtils.beanToJSONString(messageInfos)); } @@ -131,14 +162,12 @@ public class App extends Application { public static AppInfo getAppInfoByNotiList(Context context, AppInfo appInfo) { List messageInfos = getNotiList(context); - if (messageInfos == null) { - return appInfo; - } for (int i = 0; i < messageInfos.size(); i++) { if (messageInfos.get(i).getPackageName().equals(appInfo.getPackageName())) { appInfo.setName(messageInfos.get(i).getName()); appInfo.setCode(messageInfos.get(i).getCode()); appInfo.setRemark(messageInfos.get(i).getRemark()); + appInfo.setBankInfoId(messageInfos.get(i).getBankInfoId()); return appInfo; } } @@ -148,9 +177,6 @@ public class App extends Application { public static MessageInfo getMessageByNotiList(Context context, String packageName) { List messageInfos = getNotiList(context); - if (messageInfos == null) { - return null; - } for (int i = 0; i < messageInfos.size(); i++) { if (messageInfos.get(i).getPackageName().equals(packageName)) { return messageInfos.get(i); @@ -159,4 +185,30 @@ public class App extends Application { return null; } + // ========== 重试队列持久化辅助方法 ========== + + public static void saveRetryQueue(Context context, String value) { + SharedPreferences sp = context.getSharedPreferences("server", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + editor.putString("retry_queue", value); + editor.apply(); + } + + public static String getRetryQueue(Context context) { + SharedPreferences sp = context.getSharedPreferences("server", Activity.MODE_PRIVATE); + return sp.getString("retry_queue", ""); + } + + public static void saveUploadedCount(Context context, int count) { + SharedPreferences sp = context.getSharedPreferences("server", Activity.MODE_PRIVATE); + SharedPreferences.Editor editor = sp.edit(); + editor.putInt("uploaded_count", count); + editor.apply(); + } + + public static int getUploadedCount(Context context) { + SharedPreferences sp = context.getSharedPreferences("server", Activity.MODE_PRIVATE); + return sp.getInt("uploaded_count", 0); + } + } diff --git a/app/src/main/java/com/miraclegarden/smsmessage/AppInfo.java b/app/src/main/java/com/miraclegarden/smsmessage/AppInfo.java index b6bb4a2..d1d1906 100644 --- a/app/src/main/java/com/miraclegarden/smsmessage/AppInfo.java +++ b/app/src/main/java/com/miraclegarden/smsmessage/AppInfo.java @@ -17,6 +17,11 @@ public class AppInfo { private String name; private String code; private String remark; + private String bankInfoId; // 关联的银行账户ID + + public AppInfo() { + } + public AppInfo(String appName, String packageName, Drawable icon) { this.appName = appName; this.packageName = packageName; @@ -50,4 +55,12 @@ public class AppInfo { public void setRemark(String remark) { this.remark = remark; } + + public String getBankInfoId() { + return bankInfoId; + } + + public void setBankInfoId(String bankInfoId) { + this.bankInfoId = bankInfoId; + } } \ No newline at end of file diff --git a/app/src/main/java/com/miraclegarden/smsmessage/DeleteConfirmDialog.java b/app/src/main/java/com/miraclegarden/smsmessage/DeleteConfirmDialog.java index f1858a7..c8b6be9 100644 --- a/app/src/main/java/com/miraclegarden/smsmessage/DeleteConfirmDialog.java +++ b/app/src/main/java/com/miraclegarden/smsmessage/DeleteConfirmDialog.java @@ -3,50 +3,52 @@ package com.miraclegarden.smsmessage; import android.app.Dialog; import android.content.Context; import android.os.Bundle; -import android.text.TextUtils; import android.view.Gravity; -import android.view.View; -import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowManager; -import android.widget.TextView; import com.miraclegarden.smsmessage.databinding.DialogDeleteConfirmBinding; - /** - * 通用弹窗 + * 删除确认弹窗 */ public class DeleteConfirmDialog extends Dialog { DialogDeleteConfirmBinding dialogActionConfirmBinding; - OnToActionListener onToActionListener; - MessageInfo messageInfo; - public interface OnToActionListener { - void toSumbit(); + OnConfirmListener onConfirmListener; + private String title; + private String content; + + public interface OnConfirmListener { + void onConfirm(); } - public void setOnToActionListener(OnToActionListener onNextCallListener) { - this.onToActionListener = onNextCallListener; + public void setOnConfirmListener(OnConfirmListener listener) { + this.onConfirmListener = listener; } - public DeleteConfirmDialog(Context context, MessageInfo messageInfo) { super(context, R.style.MaterialDesignDialog); - this.messageInfo = messageInfo; + this.title = "删除确认"; + this.content = "删除" + messageInfo.getAppName() + "监听"; } + public DeleteConfirmDialog(Context context, String title, String content) { + super(context, R.style.MaterialDesignDialog); + this.title = title; + this.content = content; + } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); dialogActionConfirmBinding = DialogDeleteConfirmBinding.inflate(getLayoutInflater()); setContentView(dialogActionConfirmBinding.getRoot()); - dialogActionConfirmBinding.contentTv.setText("删除"+messageInfo.getAppName()+"监听"); + dialogActionConfirmBinding.contentTv.setText(content); dialogActionConfirmBinding.sumbitTv.setOnClickListener(v -> { dismiss(); - if(onToActionListener!=null){ - onToActionListener.toSumbit(); + if(onConfirmListener != null){ + onConfirmListener.onConfirm(); } }); dialogActionConfirmBinding.cancelTv.setOnClickListener(v -> { @@ -61,5 +63,4 @@ public class DeleteConfirmDialog extends Dialog { window.setAttributes(wlp); } - } diff --git a/app/src/main/java/com/miraclegarden/smsmessage/GsonUtils.java b/app/src/main/java/com/miraclegarden/smsmessage/GsonUtils.java index 6419c7f..6097583 100644 --- a/app/src/main/java/com/miraclegarden/smsmessage/GsonUtils.java +++ b/app/src/main/java/com/miraclegarden/smsmessage/GsonUtils.java @@ -1,6 +1,7 @@ package com.miraclegarden.smsmessage; import android.text.TextUtils; +import android.util.Log; import com.google.gson.Gson; import com.google.gson.JsonObject; @@ -11,11 +12,12 @@ import java.util.ArrayList; import java.util.List; /** - * json解析工具类 其实对于数组解析有一些问题 - * @author + * json解析工具类 + * @author */ public class GsonUtils { + private static final String TAG = "GsonUtils"; public static Gson gson = new Gson(); /** @@ -27,7 +29,12 @@ public class GsonUtils { */ public static T getListFromJSON(String str, Type type) { if (!TextUtils.isEmpty(str)) { - return gson.fromJson(str, type); + try { + return gson.fromJson(str, type); + } catch (Exception e) { + Log.e(TAG, "getListFromJSON(Type) parse error", e); + return null; + } } return null; } @@ -39,17 +46,26 @@ public class GsonUtils { * @param * @return */ - public static List getListFromJSON(String str, Class cls) - { - Type type = new TypeToken>() - {}.getType(); - ArrayList jsonObjects = gson.fromJson(str, type); - ArrayList arrayList = new ArrayList<>(); - for (JsonObject jsonObject : jsonObjects) - { - arrayList.add(gson.fromJson(jsonObject, cls)); + public static List getListFromJSON(String str, Class cls) { + if (TextUtils.isEmpty(str)) { + return new ArrayList<>(); + } + try { + Type type = new TypeToken>() + {}.getType(); + ArrayList jsonObjects = gson.fromJson(str, type); + if (jsonObjects == null) { + return new ArrayList<>(); + } + ArrayList arrayList = new ArrayList<>(); + for (JsonObject jsonObject : jsonObjects) { + arrayList.add(gson.fromJson(jsonObject, cls)); + } + return arrayList; + } catch (Exception e) { + Log.e(TAG, "getListFromJSON(Class) parse error", e); + return new ArrayList<>(); } - return arrayList; } /** @@ -62,11 +78,11 @@ public class GsonUtils { public static T getObjFromJSON(String str, Class cls) { try { if (!TextUtils.isEmpty(str)) { -// LogUtils.i("参数:"+str); return gson.fromJson(str, cls); } return null; - }catch (Exception e) { + } catch (Exception e) { + Log.e(TAG, "getObjFromJSON parse error", e); return null; } } @@ -76,7 +92,15 @@ public class GsonUtils { * @return */ public static String beanToJSONString(Object bean) { - return new Gson().toJson(bean); + if (bean == null) { + return ""; + } + try { + return gson.toJson(bean); + } catch (Exception e) { + Log.e(TAG, "beanToJSONString error", e); + return ""; + } } diff --git a/app/src/main/java/com/miraclegarden/smsmessage/MessageInfo.java b/app/src/main/java/com/miraclegarden/smsmessage/MessageInfo.java index 170c0dd..7b6aad8 100644 --- a/app/src/main/java/com/miraclegarden/smsmessage/MessageInfo.java +++ b/app/src/main/java/com/miraclegarden/smsmessage/MessageInfo.java @@ -10,11 +10,12 @@ package com.miraclegarden.smsmessage; */ public class MessageInfo { - private String appName; // 应用名 - private String packageName;// 包名 + private String appName; // 应用名 + private String packageName; // 包名 private String name; private String code; private String remark; + private String bankInfoId; // 关联的银行账户ID public MessageInfo() { } @@ -30,10 +31,11 @@ public class MessageInfo { public static MessageInfo AppInfoToMessageInfo(AppInfo appInfo) { MessageInfo messageInfo = new MessageInfo(); messageInfo.appName = appInfo.getAppName(); - messageInfo.packageName = appInfo.getPackageName(); - messageInfo.name = appInfo.getName(); - messageInfo.code = appInfo.getCode(); - messageInfo.remark = appInfo.getRemark(); + messageInfo.packageName = appInfo.getPackageName(); + messageInfo.name = appInfo.getName(); + messageInfo.code = appInfo.getCode(); + messageInfo.remark = appInfo.getRemark(); + messageInfo.bankInfoId = appInfo.getBankInfoId(); return messageInfo; } @@ -76,4 +78,12 @@ public class MessageInfo { public void setRemark(String remark) { this.remark = remark; } -} \ No newline at end of file + + public String getBankInfoId() { + return bankInfoId; + } + + public void setBankInfoId(String bankInfoId) { + this.bankInfoId = bankInfoId; + } +}