添加 Hook 消息接收与本地调试模式,便于 LSPosed/Telegram 联调。
新增 HookMessageReceiver 与通知服务 Hook 上报链路,支持未登录本地监听;补充 build/install 脚本并忽略 platform-tools 与 xposed 构建产物。
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -13,3 +13,6 @@
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
gradle-local.properties
|
||||
platform-tools/
|
||||
xposed-module/build/
|
||||
|
||||
@@ -101,6 +101,16 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Xposed Hook 消息接收 -->
|
||||
<receiver
|
||||
android:name=".service.HookMessageReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="com.miraclegarden.smsmessage.action.HOOK_MESSAGE" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.miraclegarden.smsmessage.databinding.DialogActionConfirmBinding;
|
||||
import com.miraclegarden.smsmessage.model.ApiError;
|
||||
import com.miraclegarden.smsmessage.model.BankInfo;
|
||||
import com.miraclegarden.smsmessage.network.ApiService;
|
||||
import com.miraclegarden.smsmessage.network.TokenManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -30,8 +31,10 @@ public class ActionConfirmDialog extends Dialog {
|
||||
int index;
|
||||
DialogActionConfirmBinding actionConfirmBinding;
|
||||
ApiService apiService;
|
||||
TokenManager tokenManager;
|
||||
List<BankInfo> bankList = new ArrayList<>();
|
||||
BankInfo selectedBank = null;
|
||||
private boolean localMode = false;
|
||||
|
||||
public interface OnToActionListener {
|
||||
void toSumbit(AppInfo appInfo);
|
||||
@@ -47,6 +50,7 @@ public class ActionConfirmDialog extends Dialog {
|
||||
this.context = context;
|
||||
this.appInfo = appInfo;
|
||||
this.apiService = new ApiService(context);
|
||||
this.tokenManager = TokenManager.getInstance(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,10 +63,19 @@ public class ActionConfirmDialog extends Dialog {
|
||||
actionConfirmBinding.tvAppname.setText(appInfo.getAppName());
|
||||
actionConfirmBinding.tvPackage.setText(appInfo.getPackageName());
|
||||
|
||||
// 加载银行账户列表
|
||||
loadBankList();
|
||||
// 未登录时走本地模式,不请求服务器银行账户
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
setupLocalMode();
|
||||
} else {
|
||||
loadBankList();
|
||||
}
|
||||
|
||||
actionConfirmBinding.sumbitTv.setOnClickListener(view -> {
|
||||
if (localMode) {
|
||||
submitLocalMode();
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedBank == null) {
|
||||
Toast.makeText(context, "请选择关联的银行账户", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
@@ -124,14 +137,43 @@ public class ActionConfirmDialog extends Dialog {
|
||||
public void onError(ApiError error) {
|
||||
if (context instanceof android.app.Activity) {
|
||||
((android.app.Activity) context).runOnUiThread(() -> {
|
||||
Toast.makeText(context, "加载银行账户失败: " + error.getMessage(), Toast.LENGTH_LONG).show();
|
||||
dismiss();
|
||||
Toast.makeText(context, "无法加载银行账户,已切换本地模式", Toast.LENGTH_SHORT).show();
|
||||
setupLocalMode();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupLocalMode() {
|
||||
localMode = true;
|
||||
selectedBank = null;
|
||||
actionConfirmBinding.spinnerBank.setVisibility(View.GONE);
|
||||
View bankSection = (View) actionConfirmBinding.spinnerBank.getParent();
|
||||
if (bankSection != null) {
|
||||
for (int i = 0; i < ((android.view.ViewGroup) bankSection).getChildCount(); i++) {
|
||||
((android.view.ViewGroup) bankSection).getChildAt(i).setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void submitLocalMode() {
|
||||
if (TextUtils.isEmpty(appInfo.getBankInfoId())) {
|
||||
appInfo.setBankInfoId("local:" + appInfo.getPackageName());
|
||||
}
|
||||
if (TextUtils.isEmpty(appInfo.getName())) {
|
||||
appInfo.setName(appInfo.getAppName());
|
||||
}
|
||||
if (TextUtils.isEmpty(appInfo.getCode())) {
|
||||
appInfo.setCode(appInfo.getPackageName());
|
||||
}
|
||||
|
||||
if (onToActionListener != null) {
|
||||
dismiss();
|
||||
onToActionListener.toSumbit(appInfo);
|
||||
}
|
||||
}
|
||||
|
||||
private void setupSpinner() {
|
||||
List<String> bankNames = new ArrayList<>();
|
||||
bankNames.add("请选择银行账户");
|
||||
|
||||
@@ -33,11 +33,12 @@ public class AppListActivity extends MiracleGardenActivity<AppListSettingBinding
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
tokenManager = TokenManager.getInstance(this);
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// 调试用:暂时跳过登录
|
||||
// if (!tokenManager.isLoggedIn()) {
|
||||
// startActivity(new Intent(this, LoginActivity.class));
|
||||
// finish();
|
||||
// return;
|
||||
// }
|
||||
|
||||
initView();
|
||||
}
|
||||
|
||||
@@ -27,11 +27,12 @@ public class BankEditActivity extends MiracleGardenActivity<ActivityBankEditBind
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
tokenManager = TokenManager.getInstance(this);
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// 调试用:暂时跳过登录
|
||||
// if (!tokenManager.isLoggedIn()) {
|
||||
// startActivity(new Intent(this, LoginActivity.class));
|
||||
// finish();
|
||||
// return;
|
||||
// }
|
||||
|
||||
apiService = new ApiService(this);
|
||||
|
||||
|
||||
@@ -37,11 +37,12 @@ public class BankListActivity extends MiracleGardenActivity<ActivityBankListBind
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
tokenManager = TokenManager.getInstance(this);
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// 调试用:暂时跳过登录
|
||||
// if (!tokenManager.isLoggedIn()) {
|
||||
// startActivity(new Intent(this, LoginActivity.class));
|
||||
// finish();
|
||||
// return;
|
||||
// }
|
||||
|
||||
apiService = new ApiService(this);
|
||||
|
||||
|
||||
@@ -32,11 +32,12 @@ public class MainActivity extends MiracleGardenActivity<ActivityMainBinding> {
|
||||
|
||||
tokenManager = TokenManager.getInstance(this);
|
||||
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// 调试用:暂时跳过登录
|
||||
// if (!tokenManager.isLoggedIn()) {
|
||||
// startActivity(new Intent(this, LoginActivity.class));
|
||||
// finish();
|
||||
// return;
|
||||
// }
|
||||
|
||||
initView();
|
||||
}
|
||||
@@ -44,11 +45,12 @@ public class MainActivity extends MiracleGardenActivity<ActivityMainBinding> {
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// 调试用:暂时跳过登录
|
||||
// if (!tokenManager.isLoggedIn()) {
|
||||
// startActivity(new Intent(this, LoginActivity.class));
|
||||
// finish();
|
||||
// return;
|
||||
// }
|
||||
updatePermissionStatus();
|
||||
}
|
||||
|
||||
|
||||
@@ -62,12 +62,13 @@ public class NotificationActivity extends MiracleGardenActivity<ActivityNotifica
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
tokenManager = TokenManager.getInstance(this);
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
Toast.makeText(this, "请先登录", Toast.LENGTH_SHORT).show();
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// 调试用:暂时跳过登录
|
||||
// if (!tokenManager.isLoggedIn()) {
|
||||
// Toast.makeText(this, "请先登录", Toast.LENGTH_SHORT).show();
|
||||
// startActivity(new Intent(this, LoginActivity.class));
|
||||
// finish();
|
||||
// return;
|
||||
// }
|
||||
|
||||
instanceRef = new WeakReference<>(this);
|
||||
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
|
||||
@@ -29,11 +29,12 @@ public class PermissionActivity extends MiracleGardenActivity<ActivityPermission
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
tokenManager = TokenManager.getInstance(this);
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// 调试用:暂时跳过登录
|
||||
// if (!tokenManager.isLoggedIn()) {
|
||||
// startActivity(new Intent(this, LoginActivity.class));
|
||||
// finish();
|
||||
// return;
|
||||
// }
|
||||
|
||||
initView();
|
||||
}
|
||||
|
||||
@@ -34,11 +34,12 @@ public class SettingActivity extends MiracleGardenActivity<ActivitySettingBindin
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
tokenManager = TokenManager.getInstance(this);
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
startActivity(new Intent(this, LoginActivity.class));
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
// 调试用:暂时跳过登录
|
||||
// if (!tokenManager.isLoggedIn()) {
|
||||
// startActivity(new Intent(this, LoginActivity.class));
|
||||
// finish();
|
||||
// return;
|
||||
// }
|
||||
|
||||
initView();
|
||||
initList();
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.miraclegarden.smsmessage.service;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.miraclegarden.smsmessage.Activity.NotificationActivity;
|
||||
import com.miraclegarden.smsmessage.App;
|
||||
import com.miraclegarden.smsmessage.MessageInfo;
|
||||
|
||||
/**
|
||||
* 接收 Xposed 模块转发的消息(前台 Hook 通道)。
|
||||
*/
|
||||
public class HookMessageReceiver extends BroadcastReceiver {
|
||||
|
||||
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";
|
||||
public static final String EXTRA_CONTENT = "content";
|
||||
public static final String EXTRA_TIMESTAMP = "timestamp";
|
||||
public static final String EXTRA_SOURCE = "source";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent == null || !ACTION_HOOK_MESSAGE.equals(intent.getAction())) {
|
||||
return;
|
||||
}
|
||||
|
||||
String packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME);
|
||||
String title = intent.getStringExtra(EXTRA_TITLE);
|
||||
String content = intent.getStringExtra(EXTRA_CONTENT);
|
||||
long timestamp = intent.getLongExtra(EXTRA_TIMESTAMP, System.currentTimeMillis());
|
||||
String source = intent.getStringExtra(EXTRA_SOURCE);
|
||||
|
||||
if (TextUtils.isEmpty(packageName) || TextUtils.isEmpty(content)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageInfo messageInfo = App.getMessageByNotiList(context, packageName);
|
||||
if (messageInfo == null) {
|
||||
NotificationActivity.sendMessage("[Hook] 未配置监听: " + packageName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(title)) {
|
||||
title = messageInfo.getAppName();
|
||||
}
|
||||
|
||||
NotificationActivity.sendMessage("[Hook/" + source + "] " + title + " " + content);
|
||||
NotificationService.submitFromHook(context, messageInfo, title, content, timestamp);
|
||||
}
|
||||
}
|
||||
@@ -86,15 +86,46 @@ public class NotificationService extends NotificationListenerService {
|
||||
retryManager.restoreFromPersistence();
|
||||
}
|
||||
|
||||
private static final String ACTION_HOOK_MESSAGE = "ACTION_HOOK_MESSAGE";
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
if (intent != null && "ACTION_START_MONITORING".equals(intent.getAction())) {
|
||||
startMonitoring();
|
||||
if (intent != null) {
|
||||
if ("ACTION_START_MONITORING".equals(intent.getAction())) {
|
||||
startMonitoring();
|
||||
} else if (ACTION_HOOK_MESSAGE.equals(intent.getAction())) {
|
||||
handleHookMessageIntent(intent);
|
||||
}
|
||||
}
|
||||
NotificationActivity.sendMessage("监听服务成功!");
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
public static void submitFromHook(Context context, MessageInfo messageInfo,
|
||||
String title, String content, long timestamp) {
|
||||
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);
|
||||
}
|
||||
|
||||
private void handleHookMessageIntent(Intent intent) {
|
||||
String packageName = intent.getStringExtra("hook_package");
|
||||
MessageInfo messageInfo = App.getMessageByNotiList(this, packageName);
|
||||
if (messageInfo == null) {
|
||||
return;
|
||||
}
|
||||
NotificationExtractor.Result result = new NotificationExtractor.Result(
|
||||
intent.getStringExtra("hook_title"),
|
||||
intent.getStringExtra("hook_content"),
|
||||
intent.getLongExtra("hook_timestamp", System.currentTimeMillis())
|
||||
);
|
||||
submitNotification(messageInfo, result);
|
||||
}
|
||||
|
||||
public static boolean isMonitoring() {
|
||||
return isMonitoring;
|
||||
}
|
||||
@@ -167,17 +198,17 @@ public class NotificationService extends NotificationListenerService {
|
||||
}
|
||||
|
||||
private void submitNotification(MessageInfo messageInfo, NotificationExtractor.Result result) {
|
||||
// 检查是否已登录
|
||||
if (!tokenManager.isLoggedIn()) {
|
||||
NotificationActivity.sendMessage("未登录,请先登录");
|
||||
return;
|
||||
}
|
||||
// 调试用:暂时跳过登录检查
|
||||
// if (!tokenManager.isLoggedIn()) {
|
||||
// NotificationActivity.sendMessage("未登录,请先登录");
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 检查是否关联了银行账户
|
||||
if (TextUtils.isEmpty(messageInfo.getBankInfoId())) {
|
||||
NotificationActivity.sendMessage("该应用未关联银行账户,请先配置");
|
||||
return;
|
||||
}
|
||||
// 调试用:本地模式可不绑定服务器银行账户
|
||||
// if (TextUtils.isEmpty(messageInfo.getBankInfoId())) {
|
||||
// NotificationActivity.sendMessage("该应用未关联银行账户,请先配置");
|
||||
// return;
|
||||
// }
|
||||
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
|
||||
19
scripts/build-debug.ps1
Normal file
19
scripts/build-debug.ps1
Normal file
@@ -0,0 +1,19 @@
|
||||
# Build debug APK for notiMessage
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProjectRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location $ProjectRoot
|
||||
|
||||
$env:JAVA_HOME = "C:\Program Files\Java\jdk-17"
|
||||
$javaHomeArg = "-Dorg.gradle.java.home=C:\Program Files\Java\jdk-17"
|
||||
Write-Host "JAVA_HOME=$env:JAVA_HOME"
|
||||
Write-Host "Building debug APK..."
|
||||
& "$ProjectRoot\gradlew.bat" $javaHomeArg assembleDebug
|
||||
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
||||
|
||||
$apk = Join-Path $ProjectRoot "app\build\outputs\apk\debug\app-debug.apk"
|
||||
if (Test-Path $apk) {
|
||||
Write-Host ""
|
||||
Write-Host "Build OK: $apk"
|
||||
} else {
|
||||
Write-Host "Build finished but APK not found at expected path." -ForegroundColor Yellow
|
||||
}
|
||||
23
scripts/install-debug.ps1
Normal file
23
scripts/install-debug.ps1
Normal file
@@ -0,0 +1,23 @@
|
||||
# Install debug APK to connected Android device via adb
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProjectRoot = Split-Path -Parent $PSScriptRoot
|
||||
$apk = Join-Path $ProjectRoot "app\build\outputs\apk\debug\app-debug.apk"
|
||||
|
||||
if (-not (Test-Path $apk)) {
|
||||
Write-Host "APK not found. Run scripts\build-debug.ps1 first." -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
$sdk = "C:\Users\Administrator\AppData\Local\Android\Sdk"
|
||||
$adb = Join-Path $sdk "platform-tools\adb.exe"
|
||||
if (-not (Test-Path $adb)) {
|
||||
Write-Host "adb not found at $adb" -ForegroundColor Red
|
||||
exit 1
|
||||
}
|
||||
|
||||
& $adb devices
|
||||
Write-Host "Installing $apk ..."
|
||||
& $adb install -r $apk
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host "Install OK." -ForegroundColor Green
|
||||
}
|
||||
Reference in New Issue
Block a user