核心功能增强 - 数据和工具类修改

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
wchino
2026-04-05 22:28:38 +08:00
parent a61f468fcf
commit 23e372ae93
6 changed files with 269 additions and 93 deletions

View File

@@ -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<BankInfo> 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<List<BankInfo>>() {
@Override
public void onSuccess(List<BankInfo> 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<String> bankNames = new ArrayList<>();
bankNames.add("请选择银行账户");
for (BankInfo bank : bankList) {
bankNames.add(bank.getDisplayName());
}
ArrayAdapter<String> 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;
}
}
}
}
}

View File

@@ -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<MessageInfo> messageInfos = GsonUtils.getListFromJSON(messages, MessageInfo.class);
if (messageInfos == null) {
return new ArrayList<>();
}
return messageInfos;
}
public static void deleteNotiBean(Context context, MessageInfo appInfo) {
List<MessageInfo> 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<MessageInfo> 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<MessageInfo> 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<MessageInfo> 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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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解析工具类 其实对于数组解析有一些问题
* 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> 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 <T>
* @return
*/
public static <T> List<T> getListFromJSON(String str, Class<T> cls)
{
Type type = new TypeToken<ArrayList<JsonObject>>()
{}.getType();
ArrayList<JsonObject> jsonObjects = gson.fromJson(str, type);
ArrayList<T> arrayList = new ArrayList<>();
for (JsonObject jsonObject : jsonObjects)
{
arrayList.add(gson.fromJson(jsonObject, cls));
public static <T> List<T> getListFromJSON(String str, Class<T> cls) {
if (TextUtils.isEmpty(str)) {
return new ArrayList<>();
}
try {
Type type = new TypeToken<ArrayList<JsonObject>>()
{}.getType();
ArrayList<JsonObject> jsonObjects = gson.fromJson(str, type);
if (jsonObjects == null) {
return new ArrayList<>();
}
ArrayList<T> 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> T getObjFromJSON(String str, Class<T> 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 "";
}
}

View File

@@ -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;
}
public String getBankInfoId() {
return bankInfoId;
}
public void setBankInfoId(String bankInfoId) {
this.bankInfoId = bankInfoId;
}
}