第一次提交

This commit is contained in:
xuhuixiang
2026-03-21 10:50:34 +08:00
commit b68b389cd4
91 changed files with 3527 additions and 0 deletions

View File

@@ -0,0 +1,99 @@
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.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.miraclegarden.smsmessage.databinding.DialogActionConfirmBinding;
/**
* 通用弹窗
*/
public class ActionConfirmDialog extends Dialog {
private final Context context;
OnToActionListener onToActionListener;
AppInfo appInfo;
int index;
DialogActionConfirmBinding actionConfirmBinding;
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;
}
@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);
}
}
});
actionConfirmBinding.cancelTv.setOnClickListener(view -> {
dismiss();
if(onToActionListener!=null){
onToActionListener.toCancel();
}
});
Window window = getWindow();
WindowManager.LayoutParams wlp = window.getAttributes();
wlp.gravity = Gravity.CENTER;
wlp.width = WindowManager.LayoutParams.WRAP_CONTENT;
wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(wlp);
}
}

View File

@@ -0,0 +1,125 @@
package com.miraclegarden.smsmessage.Activity;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.ActionConfirmDialog;
import com.miraclegarden.smsmessage.App;
import com.miraclegarden.smsmessage.AppInfo;
import com.miraclegarden.smsmessage.AppListUtil;
import com.miraclegarden.smsmessage.R;
import com.miraclegarden.smsmessage.comm.CommonAdapter;
import com.miraclegarden.smsmessage.comm.ViewHolder;
import com.miraclegarden.smsmessage.databinding.AppListSettingBinding;
import java.util.ArrayList;
public class AppListActivity extends MiracleGardenActivity<AppListSettingBinding> {
public static SharedPreferences sp;
private ArrayList<AppInfo> appList = new ArrayList<>();
CommonAdapter userAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sp = getSharedPreferences("server", MODE_PRIVATE);
initData();
}
@Override
protected void onResume() {
super.onResume();
new LoadAppTask().execute();
}
// 异步加载
private class LoadAppTask extends AsyncTask<Void, Void, ArrayList<AppInfo>> {
@Override
protected ArrayList<AppInfo> doInBackground(Void... voids) {
// 获取全部APP
return AppListUtil.getAllInstalledApps(AppListActivity.this);
}
@Override
protected void onPostExecute(ArrayList<AppInfo> result) {
super.onPostExecute(result);
appList.clear();
appList.addAll(result);
userAdapter.notifyDataSetChanged();
if(appList.size()<=1){
binding.oneLy.setVisibility(View.VISIBLE);
}else{
binding.oneLy.setVisibility(View.GONE);
}
binding.loadingLy.setVisibility(View.GONE);
}
}
private void initData() {
binding.backIv.setOnClickListener(view -> finish());
binding.recyclerview.setLayoutManager(new LinearLayoutManager(this));
userAdapter = new CommonAdapter<>(AppListActivity.this, R.layout.item_user, appList) {
@Override
public void convert(ViewHolder holder, AppInfo info, int index) {
info = App.getAppInfoByNotiList(AppListActivity.this,info);
((ImageView) holder.getView(R.id.iv_icon)).setImageDrawable(info.getIcon());
holder.setText(R.id.tv_appname, info.getAppName());
holder.setText(R.id.tv_package, info.getPackageName());
if (!TextUtils.isEmpty(info.getName())) {
holder.setText(R.id.tv_name, info.getName()+"/");
}else{
holder.setText(R.id.tv_name, "--/");
}
if (!TextUtils.isEmpty(info.getCode())) {
holder.setText(R.id.tv_code, info.getCode()+"/");
}else{
holder.setText(R.id.tv_code, "--/");
}
if (!TextUtils.isEmpty(info.getRemark())) {
holder.setText(R.id.tv_remark, info.getRemark());
}else{
holder.setText(R.id.tv_remark, "--");
}
AppInfo finalInfo = info;
holder.getView(R.id.layout_big).setOnClickListener(view -> {
ActionConfirmDialog actionConfirmDialog = new ActionConfirmDialog(AppListActivity.this, finalInfo);
actionConfirmDialog.setOnToActionListener(new ActionConfirmDialog.OnToActionListener() {
@Override
public void toSumbit(AppInfo appInfo) {
appList.set(index,appInfo);
userAdapter.notifyItemChanged(index,appInfo);
App.saveNotiBean(AppListActivity.this,appInfo);
}
@Override
public void toCancel() {
}
});
actionConfirmDialog.show();
});
}
};
binding.recyclerview.setAdapter(userAdapter);
}
}

View File

@@ -0,0 +1,73 @@
package com.miraclegarden.smsmessage.Activity;
import android.Manifest;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.databinding.ActivityMainBinding;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends MiracleGardenActivity<ActivityMainBinding> {
public static SharedPreferences sp;
private static final String TAG = "MainActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sp = getSharedPreferences("server", MODE_PRIVATE);
initData();
initView();
}
private void initData() {
if (sp == null) {
return;
}
binding.host.setText(sp.getString("host", ""));
}
private void initView() {
binding.yes.setOnClickListener(v -> {
if (sp == null) {
return;
}
binding.host.setText(sp.getString("host", "https://www.judy88.xin/api/bills/app-upload"));
if (binding.host.getText().toString().length() == 0) {
Toast.makeText(this, "服务器和添加参数不能为空", Toast.LENGTH_SHORT).show();
return;
}
if (!binding.host.getText().toString().startsWith("http")) {
Toast.makeText(this, "服务器地址错误", Toast.LENGTH_SHORT).show();
return;
}
SharedPreferences.Editor edit = sp.edit();
edit.putString("host", binding.host.getText().toString());
edit.apply();
startActivity(new Intent(MainActivity.this, NotificationActivity.class));
finish();
});
new Handler().postDelayed(() -> binding.yes.performClick(),2000);
}
}

View File

@@ -0,0 +1,207 @@
package com.miraclegarden.smsmessage.Activity;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.ComponentName;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
import android.view.View;
import android.view.WindowManager;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.core.content.ContextCompat;
import androidx.core.widget.NestedScrollView;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.AppInfo;
import com.miraclegarden.smsmessage.AppListUtil;
import com.miraclegarden.smsmessage.MessageInfo;
import com.miraclegarden.smsmessage.databinding.ActivityNotificationBinding;
import com.miraclegarden.smsmessage.service.NotificationService;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class NotificationActivity extends MiracleGardenActivity<ActivityNotificationBinding> {
private static final Handler handler = new Handler(Looper.myLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
String str = (String) msg.obj;
if (str != null) {
@SuppressLint("SimpleDateFormat") SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH:mm:ss");
String t = format.format(new Date());
if (textView != null) {
textView.append(t + "" + str + "\n\r");
scrollView.post(() -> scrollView.fullScroll(ScrollView.FOCUS_DOWN));
}
}
}
};
public static SharedPreferences sharedPreferences;
public static void sendMessage(String str) {
Message message = new Message();
message.obj = str;
NotificationActivity.handler.sendMessage(message);
}
@SuppressLint("StaticFieldLeak")
private static TextView textView;
@SuppressLint("StaticFieldLeak")
private static NestedScrollView scrollView;
private final String[] permissions = new String[]{
Manifest.permission.RECEIVE_BOOT_COMPLETED
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sharedPreferences = getSharedPreferences("server", MODE_PRIVATE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
NotificationActivity.textView = binding.text;
NotificationActivity.scrollView = binding.scrollable;
initPermission();
initView();
}
private void initView() {
binding.button2.setOnClickListener(v -> {
//打开监听引用消息Notification access
Intent intent_s = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
startActivity(intent_s);
});
binding.button.setOnClickListener(v -> {
AppInfo appInfo = AppListUtil.getAppByPackageName(NotificationActivity.this,"com.miraclegarden.smsmessage");
appInfo.setCode("111");
appInfo.setName("测试");
appInfo.setRemark("测试备注");
MessageInfo messageInfo = MessageInfo.AppInfoToMessageInfo(appInfo);
Submit(messageInfo,"我是标题","我是正文");
});
binding.button1.setOnClickListener(view -> {
if (textView != null) {
textView.setText("");
}
});
binding.toolbar.setOnLongClickListener(view -> {
// Toast.makeText(NotificationActivity.this,"点我干嘛",Toast.LENGTH_SHORT).show();
startActivity(new Intent(NotificationActivity.this,SettingActivity.class));
return false;
});
}
private void initPermission() {
checkPermission();
for (String permission : permissions) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
sendMessage("没有" + permission + "权限");
}
}
}
//打开设置界面
private void checkPermission() {
if (!isEnabled()) {
startActivity(new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"));
}
}
// 判断是否打开了通知监听权限
private boolean isEnabled() {
String pkgName = getPackageName();
final String flat = Settings.Secure.getString(getContentResolver(), "enabled_notification_listeners");
if (!TextUtils.isEmpty(flat)) {
final String[] names = flat.split(":");
for (String name : names) {
final ComponentName cn = ComponentName.unflattenFromString(name);
if (cn != null) {
if (TextUtils.equals(pkgName, cn.getPackageName())) {
return true;
}
}
}
}
return false;
}
public MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public void Submit(MessageInfo messageInfo, String title, String context) {
OkHttpClient client = new OkHttpClient();
SharedPreferences sharedPreferences = getSharedPreferences("server", MODE_PRIVATE);
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", messageInfo.getName());
jsonObject.put("code", messageInfo.getCode());
jsonObject.put("remark", messageInfo.getRemark());
jsonObject.put("appName", messageInfo.getAppName());
jsonObject.put("packageName", messageInfo.getPackageName());
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("title", title);
jsonObject1.put("context", context);
jsonObject.put("data", jsonObject1);
String json = jsonObject.toString();
RequestBody body = RequestBody.create(json, JSON);
// 构建请求
Request request = new Request.Builder()
.url(sharedPreferences.getString("host", ""))
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
NotificationActivity.sendMessage("提交失败:" + e);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
String str = response.body().string();
NotificationActivity.sendMessage(title + "提交成功:" + str);
return;
}
NotificationActivity.sendMessage("提交失败:");
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -0,0 +1,151 @@
package com.miraclegarden.smsmessage.Activity;
import android.Manifest;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.PermissionInfo;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.ActionConfirmDialog;
import com.miraclegarden.smsmessage.App;
import com.miraclegarden.smsmessage.AppInfo;
import com.miraclegarden.smsmessage.AppListUtil;
import com.miraclegarden.smsmessage.DeleteConfirmDialog;
import com.miraclegarden.smsmessage.MessageInfo;
import com.miraclegarden.smsmessage.R;
import com.miraclegarden.smsmessage.comm.CommonAdapter;
import com.miraclegarden.smsmessage.comm.ViewHolder;
import com.miraclegarden.smsmessage.databinding.ActivityMainBinding;
import com.miraclegarden.smsmessage.databinding.ActivitySettingBinding;
import java.util.ArrayList;
import java.util.List;
public class SettingActivity extends MiracleGardenActivity<ActivitySettingBinding> {
public static SharedPreferences sp;
private static final String TAG = "SettingActivity";
private ArrayList<MessageInfo> appList = new ArrayList<>();
CommonAdapter userAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
sp = getSharedPreferences("server", MODE_PRIVATE);
initData();
initList();
}
@Override
protected void onResume() {
super.onResume();
initChangeList();
}
private void initChangeList() {
List<MessageInfo> appList1 = App.getNotiList(SettingActivity.this);
if(appList1 == null){
appList = new ArrayList<>();
}else{
appList = (ArrayList<MessageInfo>) appList1;
}
userAdapter.setDates(appList);
if(appList.size()>0){
binding.recyclerview.setVisibility(View.VISIBLE);
binding.nodataIv.setVisibility(View.GONE);
}else{
binding.recyclerview.setVisibility(View.GONE);
binding.nodataIv.setVisibility(View.VISIBLE);
}
}
private void initData() {
binding.host.setText(sp.getString("host", "https://www.judy88.xin/api/bills/app-upload"));
binding.addBt.setOnClickListener(view -> startActivity(new Intent(SettingActivity.this,AppListActivity.class)));
binding.backIv.setOnClickListener(view -> finish());
binding.yes.setOnClickListener(v -> {
if (sp == null) {
return;
}
if (binding.host.getText().toString().length() == 0) {
Toast.makeText(this, "服务器和添加参数不能为空", Toast.LENGTH_SHORT).show();
return;
}
if (!binding.host.getText().toString().startsWith("http")) {
Toast.makeText(this, "服务器地址错误", Toast.LENGTH_SHORT).show();
return;
}
SharedPreferences.Editor edit = sp.edit();
edit.putString("host", binding.host.getText().toString());
edit.apply();
Toast.makeText(this, "服务器地址修改成功", Toast.LENGTH_SHORT).show();
});
}
private void initList() {
binding.recyclerview.setLayoutManager(new LinearLayoutManager(this));
userAdapter = new CommonAdapter<>(SettingActivity.this, R.layout.item_user, appList) {
@Override
public void convert(ViewHolder holder, MessageInfo info, int index) {
AppInfo appInfo = AppListUtil.getAppByPackageName(SettingActivity.this,info.getPackageName());
ImageView delete_img = holder.getView(R.id.delete_img);
delete_img.setVisibility(View.VISIBLE);
delete_img.setOnClickListener(view -> {
DeleteConfirmDialog deleteConfirmDialog = new DeleteConfirmDialog(SettingActivity.this,info);
deleteConfirmDialog.setOnToActionListener(() -> {
App.deleteNotiBean(SettingActivity.this,info);
initChangeList();
});
deleteConfirmDialog.show();
});
if(appInfo!=null){
((ImageView) holder.getView(R.id.iv_icon)).setImageDrawable(appInfo.getIcon());
}else{
((ImageView) holder.getView(R.id.iv_icon)).setImageResource(R.mipmap.app_logo);
}
holder.setText(R.id.tv_appname, info.getAppName());
holder.setText(R.id.tv_package, info.getPackageName());
if (!TextUtils.isEmpty(info.getName())) {
holder.setText(R.id.tv_name, info.getName()+"/");
}else{
holder.setText(R.id.tv_name, "--/");
}
if (!TextUtils.isEmpty(info.getCode())) {
holder.setText(R.id.tv_code, info.getCode()+"/");
}else{
holder.setText(R.id.tv_code, "--/");
}
if (!TextUtils.isEmpty(info.getRemark())) {
holder.setText(R.id.tv_remark, info.getRemark());
}else{
holder.setText(R.id.tv_remark, "--");
}
}
};
binding.recyclerview.setAdapter(userAdapter);
}
}

View File

@@ -0,0 +1,162 @@
package com.miraclegarden.smsmessage;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
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;
public class App extends Application {
private static final String TAG = "App";
public static String Hash_value;
@Override
public void onCreate() {
super.onCreate();
String packageName = getApplicationContext().getPackageName();
MessageDigest messageDigest = getMessageDigest();
String signature = getSignature(this, packageName);
Hash_value = getHashCode(packageName, messageDigest, signature);
}
public static MessageDigest getMessageDigest() {
MessageDigest messageDigest = null;
try {
messageDigest = MessageDigest.getInstance("SHA-256");
} catch (NoSuchAlgorithmException e) {
Log.e(TAG, "No Such Algorithm.", e);
}
return messageDigest;
}
public static String getSignature(Context context, String packageName) {
PackageManager packageManager = context.getPackageManager();
Signature[] signatureArrs;
try {
signatureArrs = packageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES).signatures;
} catch (PackageManager.NameNotFoundException e) {
Log.e(TAG, "Package name inexistent.");
return "";
}
if (null == signatureArrs || 0 == signatureArrs.length) {
Log.e(TAG, "signature is null.");
return "";
}
return signatureArrs[0].toCharsString();
}
private String getHashCode(String packageName, MessageDigest messageDigest, String signature) {
String appInfo = packageName + " " + signature;
messageDigest.update(appInfo.getBytes(StandardCharsets.UTF_8));
byte[] hashSignature = messageDigest.digest();
hashSignature = Arrays.copyOfRange(hashSignature, 0, 9);
String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP);
base64Hash = base64Hash.substring(0, 11);
return base64Hash;
}
public static void saveNotiList(Context context, String value) {
SharedPreferences sp = context.getSharedPreferences("server", Activity.MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("saveNotiList", value);
editor.apply();
}
public static List<MessageInfo> getNotiList(Context context) {
SharedPreferences sp = context.getSharedPreferences("server", Activity.MODE_PRIVATE);
String messages = sp.getString("saveNotiList", "");
if (TextUtils.isEmpty(messages)) {
return null;
}
List<MessageInfo> messageInfos = GsonUtils.getListFromJSON(messages, MessageInfo.class);
return messageInfos;
}
public static void deleteNotiBean(Context context, MessageInfo appInfo) {
List<MessageInfo> messageInfos = getNotiList(context);
if (messageInfos == null) {
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) {
saveNotiList(context, "");
} else {
saveNotiList(context, GsonUtils.beanToJSONString(messageInfos));
}
break;
}
}
}
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())) {
messageInfos.get(i).setName(appInfo.getName());
messageInfos.get(i).setCode(appInfo.getCode());
messageInfos.get(i).setRemark(appInfo.getRemark());
isAdd = false;
}
}
if(isAdd){
messageInfos.add(MessageInfo.AppInfoToMessageInfo(appInfo));
}
saveNotiList(context, GsonUtils.beanToJSONString(messageInfos));
}
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());
return appInfo;
}
}
return appInfo;
}
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);
}
}
return null;
}
}

View File

@@ -0,0 +1,53 @@
package com.miraclegarden.smsmessage;
/**
* **********************
*
* @Author bug machine
* 创建时间: 2026/3/20 15:30
* 用途
* **********************
*/
import android.graphics.drawable.Drawable;
public class AppInfo {
private String appName; // 应用名
private String packageName;// 包名
private Drawable icon; // 图标
private String name;
private String code;
private String remark;
public AppInfo(String appName, String packageName, Drawable icon) {
this.appName = appName;
this.packageName = packageName;
this.icon = icon;
}
public String getAppName() { return appName; }
public String getPackageName() { return packageName; }
public Drawable getIcon() { return icon; }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}

View File

@@ -0,0 +1,91 @@
package com.miraclegarden.smsmessage;
/**
* **********************
*
* @Author bug machine
* 创建时间: 2026/3/20 15:30
* 用途
* **********************
*/
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class AppListUtil {
// 获取 所有已安装APP
public static ArrayList<AppInfo> getAllInstalledApps(Context context) {
ArrayList<AppInfo> list = new ArrayList<>();
PackageManager pm = context.getPackageManager();
// 获取全部应用
List<ApplicationInfo> apps = pm.getInstalledApplications(0);
Log.i("多少条数据","多少条数据:"+apps.size());
for (ApplicationInfo info : apps) {
// 只获取有桌面图标的应用(可去掉,获取全部)
if (pm.getLaunchIntentForPackage(info.packageName) != null) {
String name = info.loadLabel(pm).toString();
String pkg = info.packageName;
Drawable icon = info.loadIcon(pm);
boolean isSystem = (info.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
list.add(new AppInfo(name, pkg, icon));
}
}
return list;
}
// 只获取 第三方APP排除系统APP
public static ArrayList<AppInfo> getThirdPartyApps(Context context) {
ArrayList<AppInfo> list = new ArrayList<>();
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> apps = pm.getInstalledApplications(0);
for (ApplicationInfo info : apps) {
// 非系统APP + 有启动图标
if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0
&& pm.getLaunchIntentForPackage(info.packageName) != null) {
list.add(new AppInfo(
info.loadLabel(pm).toString(),
info.packageName,
info.loadIcon(pm)
));
}
}
return list;
}
/**
* 判断某个包名是否已安装,并返回应用信息
* @param context 上下文
* @param packageName 指定包名
* @return null = 未安装 / 无权限 / 找不到
*/
public static AppInfo getAppByPackageName(Context context, String packageName) {
try {
PackageManager pm = context.getPackageManager();
// ✅ 关键:直接查询指定包名(不会被系统限制)
PackageInfo packageInfo = pm.getPackageInfo(packageName, 0);
ApplicationInfo appInfo = packageInfo.applicationInfo;
String appName = appInfo.loadLabel(pm).toString();
Drawable icon = appInfo.loadIcon(pm);
return new AppInfo(appName, packageName, icon);
} catch (PackageManager.NameNotFoundException e) {
// 没安装
return null;
}
}
}

View File

@@ -0,0 +1,65 @@
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();
}
public void setOnToActionListener(OnToActionListener onNextCallListener) {
this.onToActionListener = onNextCallListener;
}
public DeleteConfirmDialog(Context context, MessageInfo messageInfo) {
super(context, R.style.MaterialDesignDialog);
this.messageInfo = messageInfo;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
dialogActionConfirmBinding = DialogDeleteConfirmBinding.inflate(getLayoutInflater());
setContentView(dialogActionConfirmBinding.getRoot());
dialogActionConfirmBinding.contentTv.setText("删除"+messageInfo.getAppName()+"监听");
dialogActionConfirmBinding.sumbitTv.setOnClickListener(v -> {
dismiss();
if(onToActionListener!=null){
onToActionListener.toSumbit();
}
});
dialogActionConfirmBinding.cancelTv.setOnClickListener(v -> {
dismiss();
});
Window window = getWindow();
WindowManager.LayoutParams wlp = window.getAttributes();
wlp.gravity = Gravity.CENTER;
wlp.width = WindowManager.LayoutParams.WRAP_CONTENT;
wlp.height = WindowManager.LayoutParams.WRAP_CONTENT;
window.setAttributes(wlp);
}
}

View File

@@ -0,0 +1,91 @@
package com.miraclegarden.smsmessage;
import android.text.TextUtils;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
/**
* json解析工具类 其实对于数组解析有一些问题
* @author
*/
public class GsonUtils {
public static Gson gson = new Gson();
/**
* 返回List对象
* @param str
* @param type new TypeToken<ArrayList<T>>(){}.getType()
* @param <T>
* @return
*/
public static <T> T getListFromJSON(String str, Type type) {
if (!TextUtils.isEmpty(str)) {
return gson.fromJson(str, type);
}
return null;
}
/**
* 返回List对象
* @param str
* @param cls
* @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));
}
return arrayList;
}
/**
* 返回对象
* @param str
* @param cls
* @param <T>
* @return
*/
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) {
return null;
}
}
/**
* 返回JsonString
* @return
*/
public static String beanToJSONString(Object bean) {
return new Gson().toJson(bean);
}
public static String JSONTokener(String in) {
// consume an optional byte order mark (BOM) if it exists
if (in != null && in.startsWith("\ufeff")) {
in = in.substring(1);
}
return in;
}
}

View File

@@ -0,0 +1,79 @@
package com.miraclegarden.smsmessage;
/**
* **********************
*
* @Author bug machine
* 创建时间: 2026/3/20 15:30
* 用途
* **********************
*/
public class MessageInfo {
private String appName; // 应用名
private String packageName;// 包名
private String name;
private String code;
private String remark;
public MessageInfo() {
}
public MessageInfo(String appName, String packageName, String name, String code, String remark) {
this.appName = appName;
this.packageName = packageName;
this.name = name;
this.code = code;
this.remark = remark;
}
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();
return messageInfo;
}
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}

View File

@@ -0,0 +1,68 @@
package com.miraclegarden.smsmessage.comm;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
/**
* 通用列表适配器
* @param <T>
*/
public abstract class CommonAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
protected Context mContext;
protected int mLayoutId;
protected List<T> mDatas;
protected LayoutInflater mInflater;
ViewHolder viewHolder;
public CommonAdapter(Context context, int layoutId, List<T> datas) {
mContext = context;
mInflater = LayoutInflater.from(context);
mLayoutId = layoutId;
mDatas = datas;
}
public void setDates(List<T> dates){
this.mDatas=dates;
// notifyItemRangeChanged(0,mDatas.size());
notifyDataSetChanged();
}
public void addDates(List<T> dates){
this.mDatas.addAll(dates);
notifyDataSetChanged();
}
public void addDates(int localSize){
int size=mDatas.size();
notifyItemRangeChanged(size,localSize);
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
viewHolder = ViewHolder.get(mContext, parent, mLayoutId);
return viewHolder;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
convert(holder, mDatas.get(position),position);
}
public abstract void convert(ViewHolder holder, T t,int index);
@Override
public int getItemCount() {
return mDatas.size();
}
}

View File

@@ -0,0 +1,60 @@
package com.miraclegarden.smsmessage.comm;
import android.content.Context;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
/**
* 通用列表ViewHolder
*/
public class ViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> mViews;
private View mConvertView;
private Context mContext;
public ViewHolder(Context context, View itemView, ViewGroup parent) {
super(itemView);
mContext = context;
mConvertView = itemView;
mViews = new SparseArray<View>();
}
public static ViewHolder get(Context context, ViewGroup parent, int layoutId) {
View itemView = LayoutInflater.from(context).inflate(layoutId, parent,
false);
ViewHolder holder = new ViewHolder(context, itemView, parent);
return holder;
}
public <T extends View> T getView(int viewId) {
View view = mViews.get(viewId);
if (view == null) {
view = mConvertView.findViewById(viewId);
mViews.put(viewId, view);
}
return (T) view;
}
public ViewHolder setText(int viewId, String text)
{
TextView tv = getView(viewId);
tv.setText(text);
return this;
}
public ViewHolder setOnClickListener(int viewId, View.OnClickListener listener) {
View view = getView(viewId);
view.setOnClickListener(listener);
return this;
}
}

View File

@@ -0,0 +1,157 @@
package com.miraclegarden.smsmessage.service;
import android.annotation.SuppressLint;
import android.app.Notification;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import com.miraclegarden.smsmessage.Activity.NotificationActivity;
import com.miraclegarden.smsmessage.App;
import com.miraclegarden.smsmessage.MessageInfo;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class NotificationService extends NotificationListenerService {
private static final String TAG = "NotificationService";
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationActivity.sendMessage("监听服务成功!");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
getSbnByNotificatinList(sbn);
}
public void getSbnByNotificatinList(StatusBarNotification sbn) {
MessageInfo messageInfo = App.getMessageByNotiList(this, sbn.getPackageName());
if (messageInfo != null) {
initData(messageInfo, sbn);
}
}
private String text = "";
private void initData(MessageInfo messageInfo, StatusBarNotification sbn) {
Bundle bundle = sbn.getNotification().extras;
String title = bundle.getString(Notification.EXTRA_TITLE, "获取标题失败!");
String context = bundle.getString(Notification.EXTRA_TEXT, "获取内容失败!");
if (context.equals("获取内容失败!")) {
if (sbn.getNotification().tickerText != null) {
context = sbn.getNotification().tickerText.toString();
}
}
NotificationActivity.sendMessage(title + " " + context);
if (!text.equals(context)) {
if (!title.equals("获取标题失败!") && !context.equals("获取内容失败!") && !title.contains("正在运行")) {
NotificationActivity.sendMessage("准备发送服务器:成功");
Submit(messageInfo,title, context);
}
}
}
public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
public void Submit(MessageInfo messageInfo,String title, String context) {
OkHttpClient client = new OkHttpClient();
SharedPreferences sharedPreferences = getSharedPreferences("server", MODE_PRIVATE);
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("name", messageInfo.getName());
jsonObject.put("code", messageInfo.getCode());
jsonObject.put("remark", messageInfo.getRemark());
jsonObject.put("appName", messageInfo.getAppName());
jsonObject.put("packageName", messageInfo.getPackageName());
JSONObject jsonObject1 = new JSONObject();
jsonObject1.put("title", title+System.currentTimeMillis());
jsonObject1.put("context", context+System.currentTimeMillis());
jsonObject.put("data", jsonObject1);
String json = jsonObject.toString();
RequestBody body = RequestBody.create(json, JSON);
// 构建请求
Request request = new Request.Builder()
.url(sharedPreferences.getString("host", ""))
.post(body)
.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
NotificationActivity.sendMessage("提交失败:" + e);
}
@Override
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
if (response.isSuccessful()) {
String str = response.body().string();
NotificationActivity.sendMessage(title + "提交成功:" + str);
text = context;
return;
}
NotificationActivity.sendMessage("提交失败:");
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
/**
* 监听断开
*/
@Override
public void onListenerDisconnected() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
// 通知侦听器断开连接 - 请求重新绑定
requestRebind(new ComponentName(this, NotificationListenerService.class));
}
}
/**
* @param context 反正第二次启动失败
*/
public static void toggleNotificationListenerService(Context context) {
PackageManager pm = context.getPackageManager();
pm.setComponentEnabledSetting(new ComponentName(context, NotificationService.class),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(new ComponentName(context, NotificationService.class),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}
}