commit b68b389cd42e6c143fe1f158f900e292b2b8a7af Author: xuhuixiang Date: Sat Mar 21 10:50:34 2026 +0800 第一次提交 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..aa724b7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..359bb53 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# 默认忽略的文件 +/shelf/ +/workspace.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..b589d56 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..a2b259d --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,22 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..8978d23 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,9 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..639c88b --- /dev/null +++ b/README.md @@ -0,0 +1,8 @@ +# SmsMessage + +```https://github.com/yingliangwei/SmsMessage/blob/master/app/release/app-release.apk``` +Android监听通知栏短信号码和信息内容,适配华为,MIUI等手机 +防止锁屏,支持后台运行,Activity控制面板显示日志 +支持获取收款信息,通知栏信息(QQ,微信等)区分 +信息内容上传到服务器,可以用来做码支付,收款服务等 +![微信图片_20221104000122](https://user-images.githubusercontent.com/39827069/199772161-a01ab624-3f74-427c-b137-fe19c91d040b.jpg) diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle new file mode 100644 index 0000000..5becaf9 --- /dev/null +++ b/app/build.gradle @@ -0,0 +1,71 @@ +plugins { + id 'com.android.application' +} + +ext { + miraclegardenlibVersion = '1.0' +} + +android { + namespace 'com.miraclegarden.smsmessage' + compileSdk 32 + + defaultConfig { + applicationId "com.miraclegarden.smsmessage" + minSdk 24 + targetSdk 32 + versionCode 11 + versionName "2.1" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + signingConfigs { + debug { + storeFile file('justlet.jks') + storePassword "123456" + keyAlias 'key0' + keyPassword "123456" + } + release { + storeFile file('justlet.jks') + storePassword "123456" + keyAlias 'key0' + keyPassword "123456" + } + } + + buildTypes { + release { + signingConfig signingConfigs.release + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + lintOptions { + abortOnError false + } + + buildFeatures { + viewBinding true + } + +} + +dependencies { + implementation "com.github.yingliangwei:MiracleGardenLib:1.0" + implementation 'androidx.databinding:viewbinding:7.3.0' + implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.10' + implementation 'androidx.appcompat:appcompat:1.5.0' + implementation 'com.google.android.material:material:1.6.1' + implementation 'com.github.yingliangwei:MiracleGardenLib:1.0' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + api 'com.google.code.gson:gson:2.9.0' +} \ No newline at end of file diff --git a/app/justlet.jks b/app/justlet.jks new file mode 100644 index 0000000..a33b839 Binary files /dev/null and b/app/justlet.jks differ diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/release/app-release.apk b/app/release/app-release.apk new file mode 100644 index 0000000..946f558 Binary files /dev/null and b/app/release/app-release.apk differ diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..315e395 --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.miraclegarden.smsmessage", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 11, + "versionName": "2.1", + "outputFile": "app-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/miraclegarden/smsmessage/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/miraclegarden/smsmessage/ExampleInstrumentedTest.java new file mode 100644 index 0000000..882494c --- /dev/null +++ b/app/src/androidTest/java/com/miraclegarden/smsmessage/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.miraclegarden.smsmessage; + +import android.content.Context; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + @Test + public void useAppContext() { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + assertEquals("com.miraclegarden.smsmessage", appContext.getPackageName()); + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..fce362d --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/AppInfo.java b/app/src/main/AppInfo.java new file mode 100644 index 0000000..63451b6 --- /dev/null +++ b/app/src/main/AppInfo.java @@ -0,0 +1,25 @@ +/** + * ********************** + * + * @Author bug machine + * 创建时间: 2026/3/20 15:29 + * 用途 + * ********************** + */ +import android.graphics.drawable.Drawable; + +public class AppInfo { + private String appName; // 应用名 + private String packageName;// 包名 + private Drawable icon; // 图标 + + 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; } +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/ActionConfirmDialog.java b/app/src/main/java/com/miraclegarden/smsmessage/ActionConfirmDialog.java new file mode 100644 index 0000000..6c1aa00 --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/ActionConfirmDialog.java @@ -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); + } + +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/Activity/AppListActivity.java b/app/src/main/java/com/miraclegarden/smsmessage/Activity/AppListActivity.java new file mode 100644 index 0000000..f71a8ca --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/Activity/AppListActivity.java @@ -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 { + public static SharedPreferences sp; + private ArrayList 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> { + @Override + protected ArrayList doInBackground(Void... voids) { + // 获取全部APP + return AppListUtil.getAllInstalledApps(AppListActivity.this); + + } + + @Override + protected void onPostExecute(ArrayList 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); + } + + +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/Activity/MainActivity.java b/app/src/main/java/com/miraclegarden/smsmessage/Activity/MainActivity.java new file mode 100644 index 0000000..cc68a89 --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/Activity/MainActivity.java @@ -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 { + 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); + } + +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/Activity/NotificationActivity.java b/app/src/main/java/com/miraclegarden/smsmessage/Activity/NotificationActivity.java new file mode 100644 index 0000000..a0a2247 --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/Activity/NotificationActivity.java @@ -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 { + + 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); + } + } +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/Activity/SettingActivity.java b/app/src/main/java/com/miraclegarden/smsmessage/Activity/SettingActivity.java new file mode 100644 index 0000000..ca07961 --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/Activity/SettingActivity.java @@ -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 { + public static SharedPreferences sp; + private static final String TAG = "SettingActivity"; + private ArrayList 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 appList1 = App.getNotiList(SettingActivity.this); + if(appList1 == null){ + appList = new ArrayList<>(); + }else{ + appList = (ArrayList) 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); + } + + + +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/App.java b/app/src/main/java/com/miraclegarden/smsmessage/App.java new file mode 100644 index 0000000..9faf4bc --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/App.java @@ -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 getNotiList(Context context) { + SharedPreferences sp = context.getSharedPreferences("server", Activity.MODE_PRIVATE); + String messages = sp.getString("saveNotiList", ""); + if (TextUtils.isEmpty(messages)) { + return null; + } + List messageInfos = GsonUtils.getListFromJSON(messages, MessageInfo.class); + return messageInfos; + } + + + public static void deleteNotiBean(Context context, MessageInfo appInfo) { + List 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 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 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 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; + } + +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/AppInfo.java b/app/src/main/java/com/miraclegarden/smsmessage/AppInfo.java new file mode 100644 index 0000000..b6bb4a2 --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/AppInfo.java @@ -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; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/miraclegarden/smsmessage/AppListUtil.java b/app/src/main/java/com/miraclegarden/smsmessage/AppListUtil.java new file mode 100644 index 0000000..7aa15cd --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/AppListUtil.java @@ -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 getAllInstalledApps(Context context) { + ArrayList list = new ArrayList<>(); + PackageManager pm = context.getPackageManager(); + + // 获取全部应用 + List 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 getThirdPartyApps(Context context) { + ArrayList list = new ArrayList<>(); + PackageManager pm = context.getPackageManager(); + List 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; + } + } +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/DeleteConfirmDialog.java b/app/src/main/java/com/miraclegarden/smsmessage/DeleteConfirmDialog.java new file mode 100644 index 0000000..f1858a7 --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/DeleteConfirmDialog.java @@ -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); + } + +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/GsonUtils.java b/app/src/main/java/com/miraclegarden/smsmessage/GsonUtils.java new file mode 100644 index 0000000..6419c7f --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/GsonUtils.java @@ -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>(){}.getType() + * @param + * @return + */ + public static T getListFromJSON(String str, Type type) { + if (!TextUtils.isEmpty(str)) { + return gson.fromJson(str, type); + } + return null; + } + + /** + * 返回List对象 + * @param str + * @param cls + * @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)); + } + return arrayList; + } + + /** + * 返回对象 + * @param str + * @param cls + * @param + * @return + */ + 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) { + 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; + } + +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/MessageInfo.java b/app/src/main/java/com/miraclegarden/smsmessage/MessageInfo.java new file mode 100644 index 0000000..170c0dd --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/MessageInfo.java @@ -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; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/miraclegarden/smsmessage/comm/CommonAdapter.java b/app/src/main/java/com/miraclegarden/smsmessage/comm/CommonAdapter.java new file mode 100644 index 0000000..72713d5 --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/comm/CommonAdapter.java @@ -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 + */ +public abstract class CommonAdapter extends RecyclerView.Adapter { + + protected Context mContext; + protected int mLayoutId; + protected List mDatas; + protected LayoutInflater mInflater; + + ViewHolder viewHolder; + + + public CommonAdapter(Context context, int layoutId, List datas) { + mContext = context; + mInflater = LayoutInflater.from(context); + mLayoutId = layoutId; + mDatas = datas; + } + public void setDates(List dates){ + this.mDatas=dates; +// notifyItemRangeChanged(0,mDatas.size()); + notifyDataSetChanged(); + } + public void addDates(List 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(); + } +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/comm/ViewHolder.java b/app/src/main/java/com/miraclegarden/smsmessage/comm/ViewHolder.java new file mode 100644 index 0000000..4c24b0d --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/comm/ViewHolder.java @@ -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 mViews; + private View mConvertView; + private Context mContext; + + public ViewHolder(Context context, View itemView, ViewGroup parent) { + super(itemView); + mContext = context; + mConvertView = itemView; + mViews = new SparseArray(); + } + + 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 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; + } + +} diff --git a/app/src/main/java/com/miraclegarden/smsmessage/service/NotificationService.java b/app/src/main/java/com/miraclegarden/smsmessage/service/NotificationService.java new file mode 100644 index 0000000..9ee580e --- /dev/null +++ b/app/src/main/java/com/miraclegarden/smsmessage/service/NotificationService.java @@ -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); + } + +} diff --git a/app/src/main/res/drawable-anydpi/ic_action_back.xml b/app/src/main/res/drawable-anydpi/ic_action_back.xml new file mode 100644 index 0000000..d5422d4 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_action_back.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_action_back.png b/app/src/main/res/drawable-hdpi/ic_action_back.png new file mode 100644 index 0000000..a35dd56 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_action_back.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_action_back.png b/app/src/main/res/drawable-mdpi/ic_action_back.png new file mode 100644 index 0000000..644f4c4 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_action_back.png differ diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable-xhdpi/ic_action_back.png b/app/src/main/res/drawable-xhdpi/ic_action_back.png new file mode 100644 index 0000000..6fd77f9 Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_action_back.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_action_back.png b/app/src/main/res/drawable-xxhdpi/ic_action_back.png new file mode 100644 index 0000000..194a318 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_action_back.png differ diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/pass_word_bg.xml b/app/src/main/res/drawable/pass_word_bg.xml new file mode 100644 index 0000000..677d956 --- /dev/null +++ b/app/src/main/res/drawable/pass_word_bg.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/pass_word_bg1.xml b/app/src/main/res/drawable/pass_word_bg1.xml new file mode 100644 index 0000000..51f46d4 --- /dev/null +++ b/app/src/main/res/drawable/pass_word_bg1.xml @@ -0,0 +1,23 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_dialog_bg3.xml b/app/src/main/res/drawable/shape_dialog_bg3.xml new file mode 100644 index 0000000..bf38fd8 --- /dev/null +++ b/app/src/main/res/drawable/shape_dialog_bg3.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..be64066 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + +