Compare commits

...

2 Commits

Author SHA1 Message Date
d8523a3f3c Update MonitorService.java 2024-04-22 11:06:19 +08:00
7681db0596 Update MainActivity.java 2024-04-22 11:06:14 +08:00
2 changed files with 136 additions and 36 deletions

View File

@@ -29,34 +29,51 @@ import java.io.PrintWriter;
import timber.log.Timber; import timber.log.Timber;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity"; private final String TAG = "MainActivity";
private PermissionUtils permissionUtils; private PermissionUtils permissionUtils;
private SharedPreferencesManager spManager; private SharedPreferencesManager spManager;
private TextView logTextView; // 将类型从 View 更改为 TextView private TextView logTextView; // 将类型从 View 更改为 TextView
/**
* 当活动被创建时调用。
* 主要负责初始化界面、检查权限、设置初始数据、启动服务等操作。
*
* @param savedInstanceState 如果活动之前被销毁这参数包含之前的状态。如果活动没被销毁之前这参数是null。
*/
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
// 设置对应的布局文件
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
// 初始化“清除日志”按钮
Button clearAllLogs = findViewById(R.id.clearLogsButton); Button clearAllLogs = findViewById(R.id.clearLogsButton);
// 初始化日志文本视图并显示初始化信息
logTextView = findViewById(R.id.logTextView); logTextView = findViewById(R.id.logTextView);
updateLog("初始化日志..."); updateLog("初始化日志...");
// 初始化SharedPreferences管理器
spManager = new SharedPreferencesManager(this); spManager = new SharedPreferencesManager(this);
// 检查是否是首次运行,是则进行初始数据设置
if (isFirstRun()) { if (isFirstRun()) {
setupInitialDataAsync(); setupInitialDataAsync(); // 异步设置初始数据
spManager.putBoolean("isFirstRun", false); spManager.putBoolean("isFirstRun", false); // 标记不再首次运行
} }
// 请求忽略电池优化设置
requestIgnoreBatteryAsync(); requestIgnoreBatteryAsync();
// 初始化权限工具类
permissionUtils = new PermissionUtils(this); permissionUtils = new PermissionUtils(this);
permissionUtils.checkPermissions(); permissionUtils.checkPermissions();
// 检查通知权限是否开启
if (permissionUtils.isNotificationServiceEnabled(this)) { if (permissionUtils.isNotificationServiceEnabled(this)) {
// 拥有权限,可以执行相关操作 // 权限已开启,可以执行相关操作
} else { } else {
// 未获得权限,可能需要提示用户 // 权限未开启,引导用户去设置页面开启
Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS"); Intent intent = new Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
startActivity(intent); startActivity(intent);
} }
@@ -65,10 +82,11 @@ public class MainActivity extends AppCompatActivity {
Intent serviceIntent = new Intent(this, MonitorService.class); Intent serviceIntent = new Intent(this, MonitorService.class);
startService(serviceIntent); startService(serviceIntent);
//启动通知监听服务 // 启动通知监听服务
Intent serviceIntent1 = new Intent(this, NotificationListener.class); Intent serviceIntent1 = new Intent(this, NotificationListener.class);
startService(serviceIntent1); startService(serviceIntent1);
// 设置点击“清除日志”按钮的监听器
clearAllLogs.setOnClickListener(new View.OnClickListener() { clearAllLogs.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public void onClick(View v) {
@@ -77,9 +95,19 @@ public class MainActivity extends AppCompatActivity {
}); });
} }
/**
* 处理权限请求的结果。
* 当用户对应用的权限请求做出响应时,系统会调用此方法。
*
* @param requestCode 请求码,用于标识哪个权限请求被响应。
* @param permissions 请求的权限数组。
* @param grantResults 请求结果的整型数组,每个元素对应一个权限的请求结果。
*/
@Override @Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults); super.onRequestPermissionsResult(requestCode, permissions, grantResults);
// 向PermissionUtils传递权限请求的结果进行进一步处理。
permissionUtils.onRequestPermissionsResult(requestCode, permissions, grantResults); permissionUtils.onRequestPermissionsResult(requestCode, permissions, grantResults);
} }
@@ -89,43 +117,87 @@ public class MainActivity extends AppCompatActivity {
readLogFileAsync(); readLogFileAsync();
} }
/**
* 检查应用是否是首次运行。
* <p>此方法通过读取SharedPreferences中的“isFirstRun”键值对来判断。如果该键不存在或者值为true表示是首次运行。</p>
*
* @return boolean 如果是首次运行返回true否则返回false。
*/
private boolean isFirstRun() { private boolean isFirstRun() {
// 默认值为 true如果找不到该键值对 // 从SharedPreferences中获取“isFirstRun”键对应的值如果不存在则默认返回true
return spManager.getBoolean("isFirstRun", true); return spManager.getBoolean("isFirstRun", true);
} }
//设置通知监控的包名
/**
* 异步设置初始数据,主要用于配置通知监控的包名。
* 该方法会开启一个新的线程在其中进行SharedPreferences的配置
* 包括设置特定包名及其对应的应用名称。
* 完成后可根据需要在该线程中执行其他操作如发送广播或更新UI等
* 但若涉及UI操作需确保在主线程中进行。
*/
private void setupInitialDataAsync() { private void setupInitialDataAsync() {
new Thread(() -> { new Thread(() -> {
// 在这里设置SharedPreferences中的包名和对应的应用名 // 设置SharedPreferences中的包名和应用名示例
// 例如:
spManager.putString("com.tencent.mm", "微信"); spManager.putString("com.tencent.mm", "微信");
// 日志输出表示SharedPreferences设置成功
Timber.d("写入SharedPreferences成功。"); Timber.d("写入SharedPreferences成功。");
// 完成后,您可以在这里执行其他操作,如发送广播、更新UI等(确保在主线程中执行UI操作 // 在这里可以执行其他操作,如需要更新UI,请确保在主线程中进行
}).start(); }).start();
} }
/**
* 异步读取日志文件的函数。
* 该函数内部使用了AsyncTask来在后台读取日志文件并且在读取完成后更新日志显示。
* 无参数。
* 无返回值。
*/
private void readLogFileAsync() { private void readLogFileAsync() {
new AsyncTask<Void, Void, String>() { new AsyncTask<Void, Void, String>() {
@Override @Override
protected String doInBackground(Void... voids) { protected String doInBackground(Void... voids) {
// 在后台执行任务,读取日志文件内容
return readLogFile(); return readLogFile();
} }
@Override @Override
protected void onPostExecute(String logContent) { protected void onPostExecute(String logContent) {
// 在UI线程上执行将读取到的日志内容更新到UI
updateLog(logContent); updateLog(logContent);
} }
}.execute(); }.execute();
} }
/**
* 异步请求忽略电池优化的函数。
* 这个函数不会接收任何参数,也不会返回任何结果。
* 它主要通过创建并执行一个名为BatteryOptimizationTask的异步任务
* 来请求系统忽略应用的电池优化设置。
*/
private void requestIgnoreBatteryAsync() { private void requestIgnoreBatteryAsync() {
// 创建并执行BatteryOptimizationTask异步任务
new BatteryOptimizationTask(this).execute(); new BatteryOptimizationTask(this).execute();
} }
/**
* 更新日志文本视图的内容。
*
* @param newLog 新的日志内容。
* 调用此方法会将日志文本视图的内容更新为传入的新日志内容。
*/
private void updateLog(String newLog) { private void updateLog(String newLog) {
logTextView.setText(newLog); logTextView.setText(newLog); // 更新日志文本视图的内容为新的日志字符串
} }
/**
* 清除日志文件的内容。
* 该方法首先检查是否存在名为"echoLink.log"的日志文件。
* 如果存在,则重写该文件以清空其内容,并更新日志状态。
* 如果文件不存在,则直接更新日志状态表示无需清空。
* 该方法不接受任何参数且无返回值。
*/
private void clearLogs() { private void clearLogs() {
File logFile = new File(getFilesDir(), "logs/echoLink.log"); File logFile = new File(getFilesDir(), "logs/echoLink.log");
if (logFile.exists()) { if (logFile.exists()) {
@@ -134,30 +206,42 @@ public class MainActivity extends AppCompatActivity {
PrintWriter writer = new PrintWriter(logFile); PrintWriter writer = new PrintWriter(logFile);
writer.print(""); writer.print("");
writer.close(); writer.close();
updateLog("日志已清空。"); // 更新 TextView 显示日志已清空 updateLog("日志已清空。"); // 更新日志状态,提示日志已清空
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
e.printStackTrace(); e.printStackTrace();
updateLog("清空日志文件时出错。"); updateLog("清空日志文件时出错。"); // 更新日志状态,提示清空日志文件时出现错误
} }
} else { } else {
updateLog("日志文件不存在,无需清空。"); updateLog("日志文件不存在,无需清空。"); // 更新日志状态,表示无需清空日志文件
} }
} }
/**
* 读取日志文件的内容。
* <p>此方法不接受任何参数,它会尝试读取名为"echoLink.log"的日志文件。</p>
* <p>返回值为日志文件的字符串内容,如果文件不存在,则返回"日志文件不存在"
* 如果在读取过程中发生IO异常则返回"读取日志文件时出错"。</p>
*
* @return 返回日志文件的内容,或者错误信息。
*/
private String readLogFile() { private String readLogFile() {
try { try {
File logFile = new File(getFilesDir(), "logs/echoLink.log"); // 日志文件的路径 // 创建日志文件的路径
File logFile = new File(getFilesDir(), "logs/echoLink.log");
if (!logFile.exists()) { if (!logFile.exists()) {
return "日志文件不存在"; return "日志文件不存在";
} }
// 用于存储日志文件内容的StringBuilder
StringBuilder logContent = new StringBuilder(); StringBuilder logContent = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) { try (BufferedReader reader = new BufferedReader(new FileReader(logFile))) {
String line; String line;
// 逐行读取日志文件并将其添加到logContent中
while ((line = reader.readLine()) != null) { while ((line = reader.readLine()) != null) {
logContent.append(line).append("\n"); logContent.append(line).append("\n");
} }
} }
// 将logContent转换为字符串并返回
return logContent.toString(); return logContent.toString();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();

View File

@@ -32,31 +32,47 @@ public class MonitorService extends Service {
private String lastPhoneNumber = null; private String lastPhoneNumber = null;
private long lastCallTime = 0; private long lastCallTime = 0;
private static final long HEARTBEAT_INTERVAL_MINUTES = 10; // 10分钟 private static final long HEARTBEAT_INTERVAL_MINUTES = 10; // 10分钟
/**
* 服务创建时调用的函数。
* 该函数在服务创建时被系统自动调用,用于初始化服务的相关操作。
* 无参数和返回值。
*/
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate(); // 调用父类的onCreate方法进行初始化
Timber.d("监控服务 onCreate"); Timber.d("监控服务 onCreate"); // 记录日志,表示监控服务创建
startForegroundService(); startForegroundService(); // 启动前台服务,保证服务不会被系统轻易杀死
networkUtils = new NetworkUtils(this); networkUtils = new NetworkUtils(this); // 初始化网络工具类
scheduleHeartbeat(this); // 启动心跳定时任务 scheduleHeartbeat(this); // 启动心跳定时任务,用于保持服务的活跃状态或与服务器保持连接
} }
/**
* 服务启动时的命令处理。
*
* @param intent 携带启动服务的意图,可能包含来电或短信的信息。
* @param flags 启动标志,提供额外数据。
* @param startId 一个唯一的整数,标识此启动请求。
* @return 返回START_STICKY如果系统在服务终止后杀死则重新创建服务并调用onStartCommand(),但不重新传递最后的意图。
*/
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) { if (intent != null) { // 检查传入的Intent是否非空
if (intent.hasExtra("incomingNumber")) { if (intent.hasExtra("incomingNumber")) { // 检查Intent是否包含来电号码
String incomingNumber = intent.getStringExtra("incomingNumber"); String incomingNumber = intent.getStringExtra("incomingNumber");
Timber.d("获取到来电信息,号码: "+ incomingNumber); Timber.d("获取到来电信息,号码: " + incomingNumber);
sendCallInfoToServer(incomingNumber); sendCallInfoToServer(incomingNumber); // 将来电信息发送到服务器
} else if (intent.hasExtra("sender") && intent.hasExtra("messageBody")) { } else if (intent.hasExtra("sender") && intent.hasExtra("messageBody")) { // 检查Intent是否包含短信信息
String sender = intent.getStringExtra("sender"); String sender = intent.getStringExtra("sender");
String messageBody = intent.getStringExtra("messageBody"); String messageBody = intent.getStringExtra("messageBody");
Timber.d("获取到短信信息,号码: "+ sender); Timber.d("获取到短信信息,号码: " + sender);
sendSmsInfoToServer(sender, messageBody); sendSmsInfoToServer(sender, messageBody); // 将短信信息发送到服务器
} }
} }
return START_STICKY; return START_STICKY; // 返回服务启动策略
} }
private void sendCallInfoToServer(String incomingNumber) { private void sendCallInfoToServer(String incomingNumber) {
Timber.d("sendCallInfoToServer: 处理来电信息"); Timber.d("sendCallInfoToServer: 处理来电信息");
if (incomingNumber.equals("null") || incomingNumber == null) { if (incomingNumber.equals("null") || incomingNumber == null) {
@@ -81,9 +97,9 @@ public class MonitorService extends Service {
networkUtils.postRequest(callInfo); networkUtils.postRequest(callInfo);
} }
private void sendSmsInfoToServer(String sender,String messageBody){ private void sendSmsInfoToServer(String sender, String messageBody) {
Timber.d("sendSmsInfoToServer: 处理短信信息"); Timber.d("sendSmsInfoToServer: 处理短信信息");
if (checkNullString(sender,messageBody)){ if (checkNullString(sender, messageBody)) {
return; return;
} }
String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); String currentTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
@@ -94,15 +110,15 @@ public class MonitorService extends Service {
networkUtils.postRequest(smsInfo); networkUtils.postRequest(smsInfo);
} }
public void sendWeChatMsg(WeChatMsg weChatMsg){ public void sendWeChatMsg(WeChatMsg weChatMsg) {
networkUtils.postRequest(weChatMsg); networkUtils.postRequest(weChatMsg);
} }
private boolean checkNullString(String a, String b){ private boolean checkNullString(String a, String b) {
if (a == null || b == null){ if (a == null || b == null) {
return true; return true;
} }
if (a.equals("null") || b.equals("null")){ if (a.equals("null") || b.equals("null")) {
return true; return true;
} }
return false; return false;