first blood

This commit is contained in:
2026-01-23 16:48:55 +08:00
parent 608af0f289
commit 5e0e7e0069
122 changed files with 8930 additions and 2 deletions

View File

@@ -0,0 +1,40 @@
buildscript {
ext {
buildToolsVersion = "33.0.0"
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.4.2")
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion rootProject.ext.compileSdkVersion
defaultConfig {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
}
lintOptions {
abortOnError false
}
}
repositories {
google()
mavenCentral()
}
dependencies {
implementation 'com.facebook.react:react-native:+'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
}

View File

@@ -0,0 +1,30 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rnwalletman">
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE" />
<application>
<!-- SMS 广播接收器 -->
<receiver
android:name=".SmsReceiver"
android:exported="true"
android:enabled="true">
<intent-filter android:priority="999">
<action android:name="android.provider.Telephony.SMS_RECEIVED" />
</intent-filter>
</receiver>
<!-- 通知监听服务 -->
<service
android:name=".RNWalletNotificationListener"
android:label="RNWalletMan Notification Listener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:exported="true">
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
</application>
</manifest>

View File

@@ -0,0 +1,130 @@
package com.rnwalletman;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Base64;
import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import net.one97.paytm.upi.transaction.common.models.o;
import org.json.JSONObject;
public class PaytmPersonalModule extends ReactContextBaseJavaModule implements ActivityEventListener {
private static final String TAG = "PaytmPersonalModule";
private Promise tokenPromise;
public PaytmPersonalModule(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addActivityEventListener(this);
}
@Override
public String getName() {
return "PaytmPersonalModule";
}
@ReactMethod
public void getToken(Promise promise) {
try {
tokenPromise = promise;
Activity activity = getCurrentActivity();
if (activity == null) {
promise.reject("ERROR", "Activity is null");
return;
}
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("paytmgtk://getToken"));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "拉起 Paytm 失败", e);
promise.reject("ERROR", e.getMessage());
tokenPromise = null;
}
}
@ReactMethod
@SuppressLint("WrongConstant")
public void pay(String amount, String payeeName, String accountNo, String ifscCode, String comments, Promise promise) {
try {
Activity activity = getCurrentActivity();
if (activity == null) {
promise.reject("ERROR", "Activity is null");
return;
}
Intent intent = new Intent();
intent.setComponent(new ComponentName(
"net.one97.paytm",
"net.one97.paytm.moneytransfer.eas.view.activity.MTEnterAmountActivity"
));
Bundle bundle = new Bundle();
bundle.putString("payee_name", payeeName);
bundle.putString("account_no", accountNo);
bundle.putString("bankname", ifscCode.substring(0, 4));
bundle.putString("ifsc", ifscCode);
bundle.putString("amount", amount);
bundle.putString("comments", comments);
bundle.putBoolean("post_txn_scan_flow", false);
bundle.putBoolean("post_txn_collect_flow", false);
intent.putExtra("post_txn_data", bundle);
intent.putExtra("t", o.INSTANCE);
intent.addFlags(268468224);
activity.startActivity(intent);
promise.resolve(true);
} catch (Exception e) {
Log.e(TAG, "Paytm 支付失败", e);
promise.reject("ERROR", e.getMessage());
}
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
}
@Override
public void onNewIntent(Intent intent) {
if (intent == null || intent.getData() == null || tokenPromise == null) {
return;
}
Uri uri = intent.getData();
if ("ipay".equals(uri.getScheme()) && "native".equals(uri.getHost())) {
String base64Data = uri.getQueryParameter("data");
if (base64Data != null) {
try {
byte[] decodedBytes = Base64.decode(base64Data, Base64.DEFAULT);
String jsonStr = new String(decodedBytes);
JSONObject tokenData = new JSONObject(jsonStr);
String mobile = tokenData.optString("mobile", "");
String token = tokenData.optString("token", "");
String userId = tokenData.optString("userId", "");
JSONObject result = new JSONObject();
result.put("mobile", mobile);
result.put("token", token);
result.put("userId", userId);
tokenPromise.resolve(result.toString());
tokenPromise = null;
} catch (Exception e) {
tokenPromise.reject("ERROR", e.getMessage());
tokenPromise = null;
}
}
}
}
}

View File

@@ -0,0 +1,96 @@
package com.rnwalletman;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import com.facebook.react.bridge.ActivityEventListener;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import org.json.JSONObject;
public class PhonepePersonalModule extends ReactContextBaseJavaModule implements ActivityEventListener {
private static final String TAG = "PhonepePersonalModule";
private Promise tokenPromise;
public PhonepePersonalModule(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addActivityEventListener(this);
}
@Override
public String getName() {
return "PhonepePersonalModule";
}
@ReactMethod
public void getToken(Promise promise) {
Activity activity = getCurrentActivity();
if (activity == null) {
promise.reject("ERROR", "Activity is null");
return;
}
try {
tokenPromise = promise;
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("phonepegtk://getToken?callback=" + activity.getPackageName()));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
Log.d(TAG, "已拉起 PhonePe等待回调...");
} catch (Exception e) {
Log.e(TAG, "拉起 PhonePe 失败", e);
tokenPromise = null;
promise.reject("ERROR", e.getMessage());
}
}
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) {
}
@Override
public void onNewIntent(Intent intent) {
if (intent == null || intent.getData() == null || tokenPromise == null) {
return;
}
Uri uri = intent.getData();
String scheme = uri.getScheme();
String host = uri.getHost();
Log.d(TAG, "onNewIntent - Scheme: " + scheme + ", Host: " + host);
// PhonePe 也使用 ipay://native 回调
if ("ipay".equals(scheme) && "native".equals(host)) {
String base64Data = uri.getQueryParameter("data");
if (base64Data != null) {
try {
byte[] decodedBytes = android.util.Base64.decode(base64Data, android.util.Base64.DEFAULT);
String jsonStr = new String(decodedBytes);
JSONObject tokenData = new JSONObject(jsonStr);
String mobile = tokenData.optString("mobile", "");
String token = tokenData.optString("token", "");
String userId = tokenData.optString("userId", "");
JSONObject result = new JSONObject();
result.put("mobile", mobile);
result.put("token", token);
result.put("userId", userId);
tokenPromise.resolve(result.toString());
tokenPromise = null;
} catch (Exception e) {
Log.e(TAG, "解析 Token 失败", e);
tokenPromise.reject("ERROR", e.getMessage());
tokenPromise = null;
}
}
}
}
}

View File

@@ -0,0 +1,112 @@
package com.rnwalletman;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
/**
* 通知监听服务
*/
public class RNWalletNotificationListener extends NotificationListenerService {
private static final String TAG = "RNWalletNotification";
private static RNWalletNotificationListener instance;
@Override
public void onCreate() {
super.onCreate();
instance = this;
Log.i(TAG, "RNWalletNotificationListener 服务已启动");
// 检查并打印当前通知数量
try {
StatusBarNotification[] notifications = getActiveNotifications();
Log.i(TAG, "服务启动时当前有 " + notifications.length + " 条活跃通知");
} catch (Exception e) {
Log.e(TAG, "无法获取活跃通知: " + e.getMessage(), e);
}
}
@Override
public void onDestroy() {
super.onDestroy();
instance = null;
Log.i(TAG, "RNWalletNotificationListener stopped");
}
/**
* 获取服务实例
*/
public static RNWalletNotificationListener getInstance() {
return instance;
}
/**
* 读取当前所有活跃的通知
*/
public StatusBarNotification[] readActiveNotifications() {
try {
StatusBarNotification[] notifications = super.getActiveNotifications();
Log.i(TAG, "读取到 " + notifications.length + " 条活跃通知");
for (StatusBarNotification sbn : notifications) {
Log.d(TAG, "通知: " + sbn.getPackageName() + " - " + sbn.getId());
}
return notifications;
} catch (Exception e) {
Log.e(TAG, "读取通知失败: " + e.getMessage(), e);
return new StatusBarNotification[0];
}
}
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
// 检查是否启用监听
android.content.SharedPreferences prefs = getSharedPreferences("rnwalletman", MODE_PRIVATE);
boolean isEnabled = prefs.getBoolean("notification_enabled", false);
if (!isEnabled) {
return;
}
try {
String packageName = sbn.getPackageName();
String title = "";
String text = "";
String bigText = "";
if (sbn.getNotification() != null && sbn.getNotification().extras != null) {
android.os.Bundle extras = sbn.getNotification().extras;
title = getStringFromExtras(extras, "android.title");
text = getStringFromExtras(extras, "android.text");
bigText = getStringFromExtras(extras, "android.bigText");
}
Log.i(TAG, "收到通知: " + packageName + " - " + title);
// 发送事件到 React Native
SmsNotificationModule.sendNotificationEvent(
String.valueOf(sbn.getId()),
packageName,
sbn.getTag(),
sbn.getPostTime(),
title,
text,
bigText
);
} catch (Exception e) {
Log.e(TAG, "处理通知失败", e);
}
}
private String getStringFromExtras(android.os.Bundle extras, String key) {
if (extras == null || key == null) {
return "";
}
Object obj = extras.get(key);
return obj != null ? obj.toString() : "";
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
// 不需要处理
}
}

View File

@@ -0,0 +1,27 @@
package com.rnwalletman;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class RnWalletmanPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new PaytmPersonalModule(reactContext));
modules.add(new PhonepePersonalModule(reactContext));
modules.add(new SmsNotificationModule(reactContext));
modules.add(new TcpProxyModule(reactContext));
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,379 @@
package com.rnwalletman;
import android.Manifest;
import android.content.ContentResolver;
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.provider.Settings;
import android.provider.Telephony;
import android.service.notification.StatusBarNotification;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.modules.core.PermissionAwareActivity;
import com.facebook.react.modules.core.PermissionListener;
/**
* SMS 和通知监听模块
*/
public class SmsNotificationModule extends ReactContextBaseJavaModule {
private static final String TAG = "SmsNotificationModule";
private static ReactApplicationContext reactContext;
private static final String EVENT_SMS_RECEIVED = "onSmsMessage";
private static final String EVENT_NOTIFICATION_RECEIVED = "onNotificationMessage";
public SmsNotificationModule(ReactApplicationContext context) {
super(context);
reactContext = context;
}
@NonNull
@Override
public String getName() {
return "SmsNotificationModule";
}
@ReactMethod
public void addListener(String eventName) {
// Set up any upstream listeners or background tasks as necessary
}
@ReactMethod
public void removeListeners(Integer count) {
// Remove upstream listeners, stop unnecessary background tasks
}
/**
* 发送短信事件到 JS
*/
public static void sendSmsEvent(String sender, String message, long timestamp) {
if (reactContext == null) return;
try {
WritableMap params = Arguments.createMap();
params.putString("address", sender);
params.putString("body", message);
params.putDouble("timestamp", timestamp);
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(EVENT_SMS_RECEIVED, params);
} catch (Exception e) {
Log.e(TAG, "发送SMS事件失败", e);
}
}
/**
* 发送通知事件到 JS
*/
public static void sendNotificationEvent(String id, String packageName, String tag,
long postTime, String title, String text, String bigText) {
if (reactContext == null) return;
try {
WritableMap params = Arguments.createMap();
params.putString("id", id);
params.putString("packageName", packageName);
params.putString("tag", tag);
params.putDouble("postTime", postTime);
params.putString("title", title);
params.putString("text", text);
params.putString("bigText", bigText);
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(EVENT_NOTIFICATION_RECEIVED, params);
} catch (Exception e) {
Log.e(TAG, "发送通知事件失败", e);
}
}
/**
* 检查 SMS 权限
*/
@ReactMethod
public void checkSmsPermission(Promise promise) {
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
boolean hasReadSms = ContextCompat.checkSelfPermission(reactContext, Manifest.permission.READ_SMS) == PackageManager.PERMISSION_GRANTED;
boolean hasReceiveSms = ContextCompat.checkSelfPermission(reactContext, Manifest.permission.RECEIVE_SMS) == PackageManager.PERMISSION_GRANTED;
promise.resolve(hasReadSms && hasReceiveSms);
} else {
promise.resolve(true);
}
} catch (Exception e) {
promise.reject("CHECK_PERMISSION_ERROR", e.getMessage());
}
}
/**
* 请求 SMS 权限
*/
@ReactMethod
public void requestSmsPermission(final Promise promise) {
try {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
promise.resolve(true);
return;
}
PermissionAwareActivity activity = (PermissionAwareActivity) reactContext.getCurrentActivity();
if (activity == null) {
promise.reject("NO_ACTIVITY", "Activity is null");
return;
}
String[] permissions = new String[]{
Manifest.permission.READ_SMS,
Manifest.permission.RECEIVE_SMS
};
activity.requestPermissions(permissions, 1, new PermissionListener() {
@Override
public boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == 1) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
promise.resolve(allGranted);
return true;
}
return false;
}
});
} catch (Exception e) {
promise.reject("REQUEST_PERMISSION_ERROR", e.getMessage());
}
}
/**
* 检查通知监听权限
*/
@ReactMethod
public void checkNotificationPermission(Promise promise) {
try {
String packageName = reactContext.getPackageName();
String flat = Settings.Secure.getString(reactContext.getContentResolver(), "enabled_notification_listeners");
if (flat != null && flat.contains(packageName)) {
promise.resolve(true);
} else {
promise.resolve(false);
}
} catch (Exception e) {
promise.reject("CHECK_NOTIFICATION_PERMISSION_ERROR", e.getMessage());
}
}
/**
* 打开通知监听设置页面
*/
@ReactMethod
public void openNotificationSettings(Promise promise) {
try {
Intent intent = new Intent(Settings.ACTION_NOTIFICATION_LISTENER_SETTINGS);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
reactContext.startActivity(intent);
promise.resolve(true);
} catch (Exception e) {
promise.reject("OPEN_SETTINGS_ERROR", e.getMessage());
}
}
/**
* 启动 SMS 监听
*/
@ReactMethod
public void startSmsListener(Promise promise) {
try {
SharedPreferences prefs = reactContext.getSharedPreferences("rnwalletman", ReactApplicationContext.MODE_PRIVATE);
prefs.edit().putBoolean("sms_enabled", true).apply();
Log.i(TAG, "SMS 监听已启用");
promise.resolve(true);
} catch (Exception e) {
promise.reject("START_SMS_LISTENER_ERROR", e.getMessage());
}
}
/**
* 停止 SMS 监听
*/
@ReactMethod
public void stopSmsListener(Promise promise) {
try {
SharedPreferences prefs = reactContext.getSharedPreferences("rnwalletman", ReactApplicationContext.MODE_PRIVATE);
prefs.edit().putBoolean("sms_enabled", false).apply();
Log.i(TAG, "SMS 监听已停止");
promise.resolve(true);
} catch (Exception e) {
promise.reject("STOP_SMS_LISTENER_ERROR", e.getMessage());
}
}
/**
* 启动通知监听
*/
@ReactMethod
public void startNotificationListener(Promise promise) {
try {
SharedPreferences prefs = reactContext.getSharedPreferences("rnwalletman", ReactApplicationContext.MODE_PRIVATE);
prefs.edit().putBoolean("notification_enabled", true).apply();
Log.i(TAG, "通知监听已启用");
promise.resolve(true);
} catch (Exception e) {
promise.reject("START_NOTIFICATION_LISTENER_ERROR", e.getMessage());
}
}
/**
* 停止通知监听
*/
@ReactMethod
public void stopNotificationListener(Promise promise) {
try {
SharedPreferences prefs = reactContext.getSharedPreferences("rnwalletman", ReactApplicationContext.MODE_PRIVATE);
prefs.edit().putBoolean("notification_enabled", false).apply();
Log.i(TAG, "通知监听已停止");
promise.resolve(true);
} catch (Exception e) {
promise.reject("STOP_NOTIFICATION_LISTENER_ERROR", e.getMessage());
}
}
/**
* 读取所有短信
* @param limit 限制数量0 表示全部
*/
@ReactMethod
public void getAllSms(int limit, Promise promise) {
try {
WritableArray result = Arguments.createArray();
ContentResolver cr = reactContext.getContentResolver();
Uri uri = Telephony.Sms.CONTENT_URI;
String[] projection = new String[] {
Telephony.Sms._ID,
Telephony.Sms.ADDRESS,
Telephony.Sms.BODY,
Telephony.Sms.DATE,
Telephony.Sms.TYPE,
Telephony.Sms.READ
};
String sortOrder = Telephony.Sms.DATE + " DESC";
if (limit > 0) {
sortOrder += " LIMIT " + limit;
}
Cursor cursor = cr.query(uri, projection, null, null, sortOrder);
if (cursor != null) {
int count = 0;
while (cursor.moveToNext() && (limit == 0 || count < limit)) {
WritableMap sms = Arguments.createMap();
sms.putString("id", cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms._ID)));
sms.putString("address", cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.ADDRESS)));
sms.putString("body", cursor.getString(cursor.getColumnIndexOrThrow(Telephony.Sms.BODY)));
sms.putDouble("timestamp", cursor.getLong(cursor.getColumnIndexOrThrow(Telephony.Sms.DATE)));
sms.putInt("type", cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.TYPE)));
sms.putBoolean("read", cursor.getInt(cursor.getColumnIndexOrThrow(Telephony.Sms.READ)) == 1);
result.pushMap(sms);
count++;
}
cursor.close();
}
Log.i(TAG, "读取短信: " + result.size() + "");
promise.resolve(result);
} catch (Exception e) {
Log.e(TAG, "读取短信失败", e);
promise.reject("SMS_READ_ERROR", e.getMessage());
}
}
/**
* 读取所有活跃通知
* 需要先在系统设置中授权通知监听
*/
@ReactMethod
public void getAllNotifications(Promise promise) {
try {
RNWalletNotificationListener service = RNWalletNotificationListener.getInstance();
Log.d(TAG, "获取通知监听服务实例: " + (service != null ? "成功" : "失败"));
if (service == null) {
// 服务未运行,返回空数组而非错误(可能系统还未启动服务)
Log.w(TAG, "通知监听服务未运行,返回空数组");
promise.resolve(Arguments.createArray());
return;
}
StatusBarNotification[] notifications = service.readActiveNotifications();
WritableArray result = Arguments.createArray();
Log.d(TAG, "从系统获取到 " + notifications.length + " 条通知");
for (StatusBarNotification sbn : notifications) {
WritableMap notification = Arguments.createMap();
notification.putString("id", String.valueOf(sbn.getId()));
notification.putString("packageName", sbn.getPackageName());
notification.putString("tag", sbn.getTag());
notification.putDouble("postTime", sbn.getPostTime());
if (sbn.getNotification() != null && sbn.getNotification().extras != null) {
notification.putString("title", getStringFromExtras(sbn.getNotification().extras, "android.title"));
notification.putString("text", getStringFromExtras(sbn.getNotification().extras, "android.text"));
notification.putString("bigText", getStringFromExtras(sbn.getNotification().extras, "android.bigText"));
}
result.pushMap(notification);
}
Log.i(TAG, "返回通知数据: " + result.size() + "");
promise.resolve(result);
} catch (Exception e) {
Log.e(TAG, "读取通知失败", e);
promise.reject("NOTIFICATION_READ_ERROR", e.getMessage());
}
}
/**
* 从 Bundle 中安全地获取字符串
*/
private String getStringFromExtras(android.os.Bundle extras, String key) {
if (extras == null || key == null) {
return "";
}
Object obj = extras.get(key);
if (obj == null) {
return "";
}
return obj.toString();
}
}

View File

@@ -0,0 +1,47 @@
package com.rnwalletman;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.telephony.SmsMessage;
import android.util.Log;
/**
* SMS 广播接收器
*/
public class SmsReceiver extends BroadcastReceiver {
private static final String TAG = "RNWalletSmsReceiver";
@Override
public void onReceive(Context context, Intent intent) {
// 检查是否启用监听
SharedPreferences prefs = context.getSharedPreferences("rnwalletman", Context.MODE_PRIVATE);
boolean isEnabled = prefs.getBoolean("sms_enabled", false);
if (!isEnabled) {
return;
}
if ("android.provider.Telephony.SMS_RECEIVED".equals(intent.getAction())) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");
if (pdus != null) {
for (Object pdu : pdus) {
SmsMessage sms = SmsMessage.createFromPdu((byte[]) pdu);
String sender = sms.getDisplayOriginatingAddress();
String message = sms.getMessageBody();
long timestamp = sms.getTimestampMillis();
Log.i(TAG, "收到短信: " + sender + " - " + message);
// 发送事件到 React Native
SmsNotificationModule.sendSmsEvent(sender, message, timestamp);
}
}
}
}
}
}

View File

@@ -0,0 +1,164 @@
package com.rnwalletman;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;
import org.json.JSONObject;
public class TcpOverWebSocketClient {
private static final String TAG = "TcpProxy";
private static final int BUFFER_SIZE = 8192;
private final String wsUrl;
private final String hostname;
private final int port;
private final String messageId;
private final ExecutorService ioPool = Executors.newCachedThreadPool();
private final AtomicBoolean closed = new AtomicBoolean(false);
private final OkHttpClient httpClient = new OkHttpClient.Builder().build();
private WebSocket ws;
private Socket tcpSocket;
public TcpOverWebSocketClient(String wsUrl, String hostname, int port, String messageId) {
this.wsUrl = wsUrl;
this.hostname = hostname;
this.port = port > 0 ? port : 443;
this.messageId = messageId;
}
public void start() {
Log.d(TAG, "连接WebSocket: " + wsUrl);
Request request = new Request.Builder().url(wsUrl).build();
ws = httpClient.newWebSocket(request, new ProxyWebSocketListener());
}
private void safeClose(String reason) {
if (closed.compareAndSet(false, true)) {
Log.d(TAG, "关闭连接: " + reason);
if (ws != null) {
try {
ws.close(1000, reason);
} catch (Exception e) {
Log.e(TAG, "关闭WebSocket失败", e);
}
ws = null;
}
if (tcpSocket != null) {
try {
tcpSocket.close();
} catch (IOException e) {
Log.e(TAG, "关闭TCP socket失败", e);
}
tcpSocket = null;
}
closed.set(false);
}
}
private void forwardTcpToWs(Socket socket, WebSocket webSocket) {
byte[] buffer = new byte[BUFFER_SIZE];
try {
InputStream input = socket.getInputStream();
while (true) {
int len = input.read(buffer);
if (len == -1) {
Log.d(TAG, "TCP连接关闭");
webSocket.close(1000, "tcp_end");
safeClose("tcp_end");
break;
}
if (len > 0) {
boolean sent = webSocket.send(ByteString.of(buffer, 0, len));
if (!sent) {
Log.w(TAG, "WebSocket发送失败");
safeClose("ws_send_failed");
break;
}
}
}
} catch (IOException e) {
Log.e(TAG, "TCP读取异常", e);
safeClose("tcp_read_error");
}
}
private class ProxyWebSocketListener extends WebSocketListener {
@Override
public void onOpen(final WebSocket webSocket, Response response) {
Log.d(TAG, "WebSocket已连接");
ioPool.execute(new Runnable() {
@Override
public void run() {
try {
// 连接目标TCP服务器
tcpSocket = new Socket(hostname, port);
Log.d(TAG, "TCP已连接: " + hostname + ":" + port);
// 发送open事件
JSONObject event = new JSONObject();
event.put("event", "open");
event.put("messageId", messageId);
webSocket.send(event.toString());
// 启动TCP->WebSocket转发
forwardTcpToWs(tcpSocket, webSocket);
} catch (Exception e) {
Log.e(TAG, "TCP连接失败", e);
safeClose("tcp_connect_failed");
}
}
});
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
// WebSocket -> TCP
try {
if (tcpSocket != null && !tcpSocket.isClosed()) {
OutputStream output = tcpSocket.getOutputStream();
output.write(bytes.toByteArray());
output.flush();
}
} catch (IOException e) {
Log.e(TAG, "写入TCP失败", e);
safeClose("tcp_write_error");
}
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
Log.d(TAG, "WebSocket正在关闭");
webSocket.close(1000, null);
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
Log.d(TAG, "WebSocket已关闭: " + code + " " + reason);
safeClose("ws_closed");
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, Response response) {
Log.e(TAG, "WebSocket连接失败", t);
safeClose("ws_failed");
}
}
}

View File

@@ -0,0 +1,41 @@
package com.rnwalletman;
import android.util.Log;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;
import org.json.JSONObject;
public class TcpProxyModule extends ReactContextBaseJavaModule {
private static final String TAG = "TcpProxyModule";
public TcpProxyModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "TcpProxyModule";
}
@ReactMethod
public void startProxy(String wsUrl, String host, int port, String messageId, Promise promise) {
try {
Log.d(TAG, "启动代理: wsUrl=" + wsUrl + " host=" + host + " port=" + port);
final TcpOverWebSocketClient client = new TcpOverWebSocketClient(wsUrl, host, port, messageId);
new Thread(new Runnable() {
@Override
public void run() {
client.start();
}
}).start();
promise.resolve(true);
} catch (Exception e) {
Log.e(TAG, "启动代理失败", e);
promise.reject("START_PROXY_ERROR", e.getMessage(), e);
}
}
}

View File

@@ -0,0 +1,8 @@
package net.one97.paytm.upi.transaction.common.models;
import java.io.Serializable;
/* loaded from: classes.dex */
public abstract class a0 implements Serializable {
private static final long serialVersionUID = 3744706392290925551L;
}

View File

@@ -0,0 +1,7 @@
package net.one97.paytm.upi.transaction.common.models;
/* loaded from: classes.dex */
public class o extends t {
public static o INSTANCE = new o();
private static final long serialVersionUID = 2038457052099164746L;
}

View File

@@ -0,0 +1,8 @@
package net.one97.paytm.upi.transaction.common.models;
import java.io.Serializable;
/* loaded from: classes.dex */
public abstract class t implements Serializable {
private static final long serialVersionUID = 1973796336737578214L;
}

View File

@@ -0,0 +1,7 @@
package net.one97.paytm.upi.transaction.common.models;
/* loaded from: classes.dex */
public class v extends a0 {
public static v INSTANCE = new v();
private static final long serialVersionUID = -5916830127381874761L;
}