feat: Telegram 双通道抓取、PC 调试台与 Hook 后台转发修复

v2.2.1 更新说明:

- 新增 xposed-module(Telegram/微信/SQLite Hook),双 APK + LSPosed 作用域

- HookMessageReceiver 后台直接 DebugForwarder + goAsync,修复 notiMessage 退后台丢消息

- MessageLogStore 日志持久化;AppConfig 调试/上传开关;PC 调试台 debug-server

- 健康检查去掉联网限制;通知/Hook 通道增加诊断日志

- 安装脚本 install-full/configure-lsposed/start-debug-server;文档 CHANGELOG + HOOK_GUIDE
This commit is contained in:
2026-07-02 16:50:12 +08:00
parent 7c80b7073a
commit 125dfe583b
33 changed files with 2164 additions and 88 deletions

View File

@@ -1,5 +1,6 @@
package com.miraclegarden.smsmessage.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
@@ -17,6 +18,8 @@ import androidx.annotation.Nullable;
import com.miraclegarden.library.app.MiracleGardenActivity;
import com.miraclegarden.smsmessage.App;
import com.miraclegarden.smsmessage.AppConfig;
import com.miraclegarden.smsmessage.MessageLogStore;
import com.miraclegarden.smsmessage.databinding.ActivityNotificationBinding;
import com.miraclegarden.smsmessage.network.TokenManager;
import com.miraclegarden.smsmessage.service.NotificationService;
@@ -36,19 +39,25 @@ public class NotificationActivity extends MiracleGardenActivity<ActivityNotifica
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
String str = (String) msg.obj;
if (str != null && binding != null && binding.tvLog != null) {
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
String t = format.format(new Date());
binding.tvLog.append(t + " " + str + "\n");
trimLogIfNeeded();
binding.scrollView.post(() ->
binding.scrollView.fullScroll(ScrollView.FOCUS_DOWN));
}
restoreLogFromStore();
}
};
private void restoreLogFromStore() {
if (binding == null || binding.tvLog == null) {
return;
}
binding.tvLog.setText(MessageLogStore.getDisplayText(this));
binding.scrollView.post(() ->
binding.scrollView.fullScroll(ScrollView.FOCUS_DOWN));
}
public static void sendMessage(String str) {
Context appContext = App.getAppContext();
if (appContext != null) {
MessageLogStore.append(appContext, str);
}
NotificationActivity activity = instanceRef != null ? instanceRef.get() : null;
if (activity != null && activity.handler != null) {
Message message = Message.obtain();
@@ -74,11 +83,13 @@ public class NotificationActivity extends MiracleGardenActivity<ActivityNotifica
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
initView();
restoreLogFromStore();
}
@Override
protected void onResume() {
super.onResume();
restoreLogFromStore();
updateUI();
handler.post(statsUpdateRunnable);
}
@@ -99,9 +110,18 @@ public class NotificationActivity extends MiracleGardenActivity<ActivityNotifica
}
private void initView() {
String username = tokenManager.getUsername();
if (username != null) {
binding.tvUsername.setText(username);
if (AppConfig.ENABLE_SERVER_UPLOAD) {
String username = tokenManager.getUsername();
if (username != null) {
binding.tvUsername.setText(username);
}
} else {
binding.tvUsername.setVisibility(android.view.View.GONE);
}
if (!AppConfig.ENABLE_SERVER_UPLOAD) {
binding.tvUploadedCount.setVisibility(android.view.View.GONE);
binding.tvSuccessRate.setVisibility(android.view.View.GONE);
}
binding.ivBack.setOnClickListener(v -> finish());
@@ -111,6 +131,7 @@ public class NotificationActivity extends MiracleGardenActivity<ActivityNotifica
});
binding.btnClearLog.setOnClickListener(v -> {
MessageLogStore.clear(this);
binding.tvLog.setText("");
});
@@ -173,14 +194,16 @@ public class NotificationActivity extends MiracleGardenActivity<ActivityNotifica
int configuredCount = App.getNotiList(this).size();
binding.tvConfiguredApps.setText("已配置: " + configuredCount + "");
binding.tvMonitoredCount.setText("监听: " + totalCount);
binding.tvUploadedCount.setText("上传: " + uploadedCount);
binding.tvMonitoredCount.setText("已抓取: " + totalCount);
if (totalCount > 0) {
int successRate = (uploadedCount * 100) / totalCount;
binding.tvSuccessRate.setText("成功率: " + successRate + "%");
} else {
binding.tvSuccessRate.setText("成功率: --");
if (AppConfig.ENABLE_SERVER_UPLOAD) {
binding.tvUploadedCount.setText("上传: " + uploadedCount);
if (totalCount > 0) {
int successRate = (uploadedCount * 100) / totalCount;
binding.tvSuccessRate.setText("成功率: " + successRate + "%");
} else {
binding.tvSuccessRate.setText("成功率: --");
}
}
}

View File

@@ -10,9 +10,7 @@ import android.text.TextUtils;
import android.util.Base64;
import android.util.Log;
import androidx.work.Constraints;
import androidx.work.ExistingPeriodicWorkPolicy;
import androidx.work.NetworkType;
import androidx.work.PeriodicWorkRequest;
import androidx.work.WorkManager;
@@ -29,10 +27,12 @@ import java.util.concurrent.TimeUnit;
public class App extends Application {
private static final String TAG = "App";
public static String Hash_value;
private static App instance;
@Override
public void onCreate() {
super.onCreate();
instance = this;
String packageName = getApplicationContext().getPackageName();
MessageDigest messageDigest = getMessageDigest();
String signature = getSignature(this, packageName);
@@ -40,14 +40,13 @@ public class App extends Application {
scheduleHealthCheck();
}
private void scheduleHealthCheck() {
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.build();
public static Context getAppContext() {
return instance;
}
private void scheduleHealthCheck() {
PeriodicWorkRequest healthCheck = new PeriodicWorkRequest.Builder(
NotificationHealthCheckWorker.class, 15, TimeUnit.MINUTES)
.setConstraints(constraints)
.build();
WorkManager.getInstance(this).enqueueUniquePeriodicWork(
"notification_health_check",

View File

@@ -0,0 +1,23 @@
package com.miraclegarden.smsmessage;
/**
* 应用运行配置。
*/
public final class AppConfig {
/** 是否上传到正式后端judy88.xin */
public static final boolean ENABLE_SERVER_UPLOAD = false;
/** 是否转发到 PC 本地调试服务 */
public static final boolean ENABLE_DEBUG_FORWARD = true;
/**
* 调试服务地址。
* USB + adb reverse: http://127.0.0.1:8765
* Wi-Fi: http://&lt;电脑局域网IP&gt;:8765
*/
public static final String DEBUG_SERVER_URL = "http://127.0.0.1:8765";
private AppConfig() {
}
}

View File

@@ -0,0 +1,101 @@
package com.miraclegarden.smsmessage;
import android.content.Context;
import android.content.SharedPreferences;
import android.text.TextUtils;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* 监听日志持久化App 在后台时也能保留历史,回到监听页可恢复显示。
*/
public final class MessageLogStore {
private static final String SP_NAME = "message_log";
private static final String KEY_LINES = "lines";
private static final int MAX_LINES = 500;
private static final String SEP = "\u001E";
private MessageLogStore() {
}
public static void append(Context context, String message) {
if (context == null || TextUtils.isEmpty(message)) {
return;
}
Context appContext = context.getApplicationContext();
SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss", Locale.getDefault());
String line = format.format(new Date()) + " " + message;
SharedPreferences sp = appContext.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
String raw = sp.getString(KEY_LINES, "");
List<String> lines = decode(raw);
lines.add(line);
while (lines.size() > MAX_LINES) {
lines.remove(0);
}
sp.edit().putString(KEY_LINES, encode(lines)).apply();
}
public static String getDisplayText(Context context) {
if (context == null) {
return "";
}
StringBuilder sb = new StringBuilder();
for (String line : getLines(context)) {
sb.append(line).append('\n');
}
return sb.toString();
}
public static List<String> getLines(Context context) {
if (context == null) {
return new ArrayList<>();
}
SharedPreferences sp = context.getApplicationContext()
.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
return decode(sp.getString(KEY_LINES, ""));
}
public static void clear(Context context) {
if (context == null) {
return;
}
context.getApplicationContext()
.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE)
.edit()
.remove(KEY_LINES)
.apply();
}
private static List<String> decode(String raw) {
List<String> lines = new ArrayList<>();
if (TextUtils.isEmpty(raw)) {
return lines;
}
for (String part : raw.split(SEP, -1)) {
if (!TextUtils.isEmpty(part)) {
lines.add(part);
}
}
return lines;
}
private static String encode(List<String> lines) {
if (lines == null || lines.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < lines.size(); i++) {
if (i > 0) {
sb.append(SEP);
}
sb.append(lines.get(i));
}
return sb.toString();
}
}

View File

@@ -0,0 +1,113 @@
package com.miraclegarden.smsmessage.network;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.miraclegarden.smsmessage.AppConfig;
import com.miraclegarden.smsmessage.MessageInfo;
import org.json.JSONObject;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* 将抓取到的消息转发到 PC 本地调试服务,便于浏览器查看。
*/
public final class DebugForwarder {
private static final String TAG = "DebugForwarder";
private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
private static final OkHttpClient CLIENT = new OkHttpClient.Builder()
.connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(5, TimeUnit.SECONDS)
.build();
private DebugForwarder() {
}
public static void forward(Context context, MessageInfo messageInfo,
String title, String content, long timestamp, String source) {
forward(context, messageInfo, title, content, timestamp, source, null);
}
public static void forward(Context context, MessageInfo messageInfo,
String title, String content, long timestamp, String source,
Runnable onComplete) {
if (!AppConfig.ENABLE_DEBUG_FORWARD || messageInfo == null) {
if (onComplete != null) {
onComplete.run();
}
return;
}
try {
JSONObject json = new JSONObject();
json.put("source", source != null ? source : "unknown");
json.put("packageName", messageInfo.getPackageName());
json.put("appName", messageInfo.getAppName());
json.put("title", title != null ? title : "");
json.put("group", resolveGroup(title, messageInfo));
json.put("content", content != null ? content : "");
json.put("timestamp", timestamp);
Request request = new Request.Builder()
.url(AppConfig.DEBUG_SERVER_URL + "/api/debug/push")
.header("Content-Type", "application/json")
.post(RequestBody.create(json.toString(), JSON))
.build();
Log.d(TAG, "forwarding [" + source + "] " + messageInfo.getPackageName()
+ " -> " + AppConfig.DEBUG_SERVER_URL);
CLIENT.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, java.io.IOException e) {
Log.w(TAG, "forward failed [" + source + "]: " + e.getMessage());
if (onComplete != null) {
onComplete.run();
}
}
@Override
public void onResponse(Call call, Response response) {
int code = response.code();
response.close();
if (code >= 200 && code < 300) {
Log.d(TAG, "forward ok [" + source + "] " + title);
} else {
Log.w(TAG, "forward http " + code + " [" + source + "] " + title);
}
if (onComplete != null) {
onComplete.run();
}
}
});
} catch (Exception e) {
Log.w(TAG, "forward build failed: " + e.getMessage());
if (onComplete != null) {
onComplete.run();
}
}
}
private static String resolveGroup(String title, MessageInfo messageInfo) {
if (!TextUtils.isEmpty(title)) {
return title.trim();
}
if (messageInfo != null && !TextUtils.isEmpty(messageInfo.getAppName())) {
return messageInfo.getAppName();
}
return "未分类";
}
}

View File

@@ -4,16 +4,22 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
import android.util.Log;
import com.miraclegarden.smsmessage.Activity.NotificationActivity;
import com.miraclegarden.smsmessage.App;
import com.miraclegarden.smsmessage.AppConfig;
import com.miraclegarden.smsmessage.MessageInfo;
import com.miraclegarden.smsmessage.network.DebugForwarder;
/**
* 接收 Xposed 模块转发的消息(前台 Hook 通道)。
* 接收 Xposed 模块转发的消息Hook 通道)。
* 在 Receiver 内直接转发到 PC避免依赖 startService后台易被系统拦截
*/
public class HookMessageReceiver extends BroadcastReceiver {
private static final String TAG = "HookMessageReceiver";
public static final String ACTION_HOOK_MESSAGE = "com.miraclegarden.smsmessage.action.HOOK_MESSAGE";
public static final String EXTRA_PACKAGE_NAME = "packageName";
public static final String EXTRA_TITLE = "title";
@@ -34,11 +40,13 @@ public class HookMessageReceiver extends BroadcastReceiver {
String source = intent.getStringExtra(EXTRA_SOURCE);
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(content)) {
Log.w(TAG, "ignore hook: empty package or content");
return;
}
MessageInfo messageInfo = App.getMessageByNotiList(context, packageName);
if (messageInfo == null) {
Log.w(TAG, "ignore hook: not in monitor list, pkg=" + packageName);
NotificationActivity.sendMessage("[Hook] 未配置监听: " + packageName);
return;
}
@@ -47,7 +55,26 @@ public class HookMessageReceiver extends BroadcastReceiver {
title = messageInfo.getAppName();
}
NotificationActivity.sendMessage("[Hook/" + source + "] " + title + " " + content);
NotificationService.submitFromHook(context, messageInfo, title, content, timestamp);
Log.i(TAG, "hook received: pkg=" + packageName + " source=" + source + " title=" + title);
final String finalTitle = title;
NotificationActivity.sendMessage("[Hook/" + source + "] " + finalTitle + " " + content);
Context appContext = context.getApplicationContext();
PendingResult pendingResult = goAsync();
Runnable finish = pendingResult::finish;
Runnable afterForward = () -> {
if (AppConfig.ENABLE_SERVER_UPLOAD) {
NotificationService.submitFromHook(appContext, messageInfo, finalTitle, content, timestamp, source);
}
finish.run();
};
if (AppConfig.ENABLE_DEBUG_FORWARD) {
DebugForwarder.forward(appContext, messageInfo, finalTitle, content, timestamp, source, afterForward);
} else {
afterForward.run();
}
}
}

View File

@@ -3,6 +3,7 @@ package com.miraclegarden.smsmessage.service;
import android.content.Context;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.work.Worker;
@@ -10,6 +11,8 @@ import androidx.work.WorkerParameters;
public class NotificationHealthCheckWorker extends Worker {
private static final String TAG = "NotiHealthCheck";
public NotificationHealthCheckWorker(@NonNull Context context, @NonNull WorkerParameters params) {
super(context, params);
}
@@ -18,7 +21,10 @@ public class NotificationHealthCheckWorker extends Worker {
@Override
public Result doWork() {
if (!isNotificationListenerEnabled()) {
Log.w(TAG, "notification listener disabled, attempting recovery");
NotificationService.toggleNotificationListenerService(getApplicationContext());
} else {
Log.d(TAG, "notification listener ok");
}
return Result.success();
}

View File

@@ -26,11 +26,13 @@ import androidx.core.app.NotificationCompat;
import com.miraclegarden.smsmessage.Activity.NotificationActivity;
import com.miraclegarden.smsmessage.App;
import com.miraclegarden.smsmessage.AppConfig;
import com.miraclegarden.smsmessage.MessageInfo;
import com.miraclegarden.smsmessage.R;
import com.miraclegarden.smsmessage.model.ApiError;
import com.miraclegarden.smsmessage.model.UploadNotificationResponse;
import com.miraclegarden.smsmessage.network.ApiService;
import com.miraclegarden.smsmessage.network.DebugForwarder;
import com.miraclegarden.smsmessage.network.TokenManager;
import org.json.JSONException;
@@ -82,8 +84,10 @@ public class NotificationService extends NotificationListenerService {
tokenManager = TokenManager.getInstance(this);
retryManager = new RetryManager(this);
retryManager.setUploadCallback(this::performUpload);
retryManager.restoreFromPersistence();
if (AppConfig.ENABLE_SERVER_UPLOAD) {
retryManager.setUploadCallback(this::performUpload);
retryManager.restoreFromPersistence();
}
}
private static final String ACTION_HOOK_MESSAGE = "ACTION_HOOK_MESSAGE";
@@ -102,14 +106,25 @@ public class NotificationService extends NotificationListenerService {
}
public static void submitFromHook(Context context, MessageInfo messageInfo,
String title, String content, long timestamp) {
String title, String content, long timestamp, String source) {
Intent intent = new Intent(context, NotificationService.class);
intent.setAction(ACTION_HOOK_MESSAGE);
intent.putExtra("hook_package", messageInfo.getPackageName());
intent.putExtra("hook_title", title);
intent.putExtra("hook_content", content);
intent.putExtra("hook_timestamp", timestamp);
context.startService(intent);
intent.putExtra("hook_source", source);
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
Log.d(TAG, "submitFromHook: service started for " + messageInfo.getPackageName());
} catch (Exception e) {
Log.e(TAG, "submitFromHook failed: " + e.getMessage(), e);
NotificationActivity.sendMessage("[Hook] 服务启动失败: " + e.getMessage());
}
}
private void handleHookMessageIntent(Intent intent) {
@@ -118,12 +133,13 @@ public class NotificationService extends NotificationListenerService {
if (messageInfo == null) {
return;
}
String source = intent.getStringExtra("hook_source");
NotificationExtractor.Result result = new NotificationExtractor.Result(
intent.getStringExtra("hook_title"),
intent.getStringExtra("hook_content"),
intent.getLongExtra("hook_timestamp", System.currentTimeMillis())
);
submitNotification(messageInfo, result);
submitNotification(messageInfo, result, source != null ? source : "hook");
}
public static boolean isMonitoring() {
@@ -135,7 +151,9 @@ public class NotificationService extends NotificationListenerService {
isMonitoring = true;
toggleNotificationListenerService(this);
updateForegroundNotification(retryManager != null ? retryManager.getUploadedCount() : 0);
updateForegroundNotification(retryManager != null
? (AppConfig.ENABLE_SERVER_UPLOAD ? retryManager.getUploadedCount() : retryManager.getTotalCount())
: 0);
NotificationActivity.sendMessage("已开启持续监听模式");
NotificationActivity.updateUI();
}
@@ -167,7 +185,11 @@ public class NotificationService extends NotificationListenerService {
try {
Intent intent = new Intent(context, NotificationService.class);
intent.setAction("ACTION_START_MONITORING");
context.startService(intent);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
} catch (Exception e) {
Log.e(TAG, "启动监听失败", e);
}
@@ -183,8 +205,11 @@ public class NotificationService extends NotificationListenerService {
if (messageInfo == null) return;
NotificationExtractor.Result result = NotificationExtractor.extract(sbn);
Log.d(TAG, "onNotificationPosted: pkg=" + sbn.getPackageName()
+ " title=" + result.title + " content=" + result.content);
if (TextUtils.isEmpty(result.title) && TextUtils.isEmpty(result.content)) {
Log.d(TAG, "skip empty notification: " + sbn.getPackageName());
NotificationActivity.sendMessage("[" + messageInfo.getAppName() + "] 通知内容为空,跳过");
return;
}
@@ -194,50 +219,60 @@ public class NotificationService extends NotificationListenerService {
}
NotificationActivity.sendMessage(result.title + " " + result.content);
submitNotification(messageInfo, result);
submitNotification(messageInfo, result, "notification");
}
private void submitNotification(MessageInfo messageInfo, NotificationExtractor.Result result) {
// 调试用:暂时跳过登录检查
// if (!tokenManager.isLoggedIn()) {
// NotificationActivity.sendMessage("未登录,请先登录");
// return;
// }
private void submitNotification(MessageInfo messageInfo, NotificationExtractor.Result result,
String debugSource) {
DebugForwarder.forward(this, messageInfo, result.title, result.content,
result.timestamp, debugSource);
// 调试用:本地模式可不绑定服务器银行账户
// if (TextUtils.isEmpty(messageInfo.getBankInfoId())) {
// NotificationActivity.sendMessage("该应用未关联银行账户,请先配置");
// return;
// }
if (!AppConfig.ENABLE_SERVER_UPLOAD) {
recordLocalCapture();
return;
}
if (!tokenManager.isLoggedIn()) {
NotificationActivity.sendMessage("未登录,请先登录");
return;
}
if (TextUtils.isEmpty(messageInfo.getBankInfoId())) {
NotificationActivity.sendMessage("该应用未关联银行账户,请先配置");
return;
}
try {
JSONObject jsonObject = new JSONObject();
// 根据文档bankInfoId 是建议字段
jsonObject.put("bankInfoId", messageInfo.getBankInfoId());
// data 字段包含通知内容
JSONObject dataObject = new JSONObject();
dataObject.put("title", result.title);
dataObject.put("context", result.content);
dataObject.put("timestamp", result.timestamp);
jsonObject.put("data", dataObject);
// 保留原有字段用于兼容性
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());
String payload = jsonObject.toString();
NotificationActivity.sendMessage("准备发送服务器:成功");
retryManager.enqueue(payload);
retryManager.enqueue(jsonObject.toString());
} catch (JSONException e) {
Log.e(TAG, "JSON构建失败", e);
NotificationActivity.sendMessage("JSON构建失败: " + e.getMessage());
}
}
private void recordLocalCapture() {
if (retryManager != null) {
retryManager.recordLocalCapture();
updateForegroundNotification(retryManager.getTotalCount());
}
NotificationActivity.updateUI();
}
private void performUpload(String jsonPayload, RetryManager.UploadResultListener listener) {
acquireWakeLockForUpload();
@@ -345,11 +380,12 @@ public class NotificationService extends NotificationListenerService {
}
}
private Notification buildForegroundNotification(int uploadedCount, boolean monitoring) {
private Notification buildForegroundNotification(int count, boolean monitoring) {
String title = monitoring ? "持续监听中" : "通知监听服务";
String countLabel = AppConfig.ENABLE_SERVER_UPLOAD ? "已上传" : "已抓取";
String text = monitoring
? "已上传 " + uploadedCount + " 条 · 持续运行中"
: "已上传 " + uploadedCount + "";
? countLabel + " " + count + " 条 · 持续运行中"
: countLabel + " " + count + "";
return new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(title)
@@ -361,10 +397,10 @@ public class NotificationService extends NotificationListenerService {
.build();
}
private void updateForegroundNotification(int uploadedCount) {
private void updateForegroundNotification(int count) {
NotificationManager nm = getSystemService(NotificationManager.class);
if (nm != null) {
nm.notify(FOREGROUND_NOTIFICATION_ID, buildForegroundNotification(uploadedCount, isMonitoring));
nm.notify(FOREGROUND_NOTIFICATION_ID, buildForegroundNotification(count, isMonitoring));
}
}
}

View File

@@ -178,6 +178,11 @@ public class RetryManager {
return failedCount;
}
/** 本地监听模式下仅累计抓取条数,不上传服务器 */
public void recordLocalCapture() {
incrementTotalCount();
}
private void incrementTotalCount() {
totalCount++;
sharedPreferences.edit()