From 143b8f3b24c73cffe7c9e02168a1e6dd47d5aa51 Mon Sep 17 00:00:00 2001 From: TQCasey <494294315@qq.com> Date: Sun, 7 Jun 2026 13:43:53 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=90=8E=E5=8F=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + .../dependencies-accessors.lock | Bin 17 -> 0 bytes android/app/build.gradle | 1 + android/app/src/main/AndroidManifest.xml | 10 +- .../src/main/java/com/rnpay/AdIdModule.java | 37 ++++ .../src/main/java/com/rnpay/AdIdPackage.java | 21 ++ .../main/java/com/rnpay/MainApplication.java | 1 + android/build.gradle | 2 +- libs/rnwalletman | 2 +- package.json | 2 +- ...eact-native-background-actions+4.0.1.patch | 15 -- ...eact-native-background-actions+4.1.0.patch | 202 ++++++++++++++++++ screens/HomeScreen.tsx | 48 ++++- screens/TestScreen.tsx | 10 + 14 files changed, 324 insertions(+), 28 deletions(-) delete mode 100644 android/.gradle/8.0.1/dependencies-accessors/dependencies-accessors.lock create mode 100644 android/app/src/main/java/com/rnpay/AdIdModule.java create mode 100644 android/app/src/main/java/com/rnpay/AdIdPackage.java delete mode 100644 patches/react-native-background-actions+4.0.1.patch create mode 100644 patches/react-native-background-actions+4.1.0.patch diff --git a/.gitignore b/.gitignore index 61dcc57..df6fd13 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ ios/Pods android/app/release __pycache__ others +*.lock diff --git a/android/.gradle/8.0.1/dependencies-accessors/dependencies-accessors.lock b/android/.gradle/8.0.1/dependencies-accessors/dependencies-accessors.lock deleted file mode 100644 index a58772f32636b2e64d8511616521a422391d37b4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17 TcmZR+|8?(L*6DZuF+c$TNB0G} diff --git a/android/app/build.gradle b/android/app/build.gradle index 3eac50c..d93a063 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -115,6 +115,7 @@ android { dependencies { // The version of react-native is set by the React Native Gradle Plugin implementation("com.facebook.react:react-android") + implementation("com.google.android.gms:play-services-ads-identifier:18.1.0") debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 41a541d..4833351 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,9 +1,13 @@ - + + + + + diff --git a/android/app/src/main/java/com/rnpay/AdIdModule.java b/android/app/src/main/java/com/rnpay/AdIdModule.java new file mode 100644 index 0000000..48db732 --- /dev/null +++ b/android/app/src/main/java/com/rnpay/AdIdModule.java @@ -0,0 +1,37 @@ +package com.rnpay; + +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.google.android.gms.ads.identifier.AdvertisingIdClient; + +public class AdIdModule extends ReactContextBaseJavaModule { + + public AdIdModule(ReactApplicationContext reactContext) { + super(reactContext); + } + + @Override + public String getName() { + return "AdId"; + } + + @ReactMethod + public void getAdvertisingId(Promise promise) { + ReactApplicationContext ctx = getReactApplicationContext(); + new Thread(() -> { + try { + AdvertisingIdClient.Info info = AdvertisingIdClient.getAdvertisingIdInfo(ctx); + if (info.isLimitAdTrackingEnabled()) { + promise.reject("LIMITED", "Ad tracking limited"); + return; + } + String id = info.getId(); + promise.resolve(id != null ? id : ""); + } catch (Exception e) { + promise.reject("ERROR", e.getMessage(), e); + } + }).start(); + } +} diff --git a/android/app/src/main/java/com/rnpay/AdIdPackage.java b/android/app/src/main/java/com/rnpay/AdIdPackage.java new file mode 100644 index 0000000..cfb25bf --- /dev/null +++ b/android/app/src/main/java/com/rnpay/AdIdPackage.java @@ -0,0 +1,21 @@ +package com.rnpay; + +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.Arrays; +import java.util.Collections; +import java.util.List; + +public class AdIdPackage implements ReactPackage { + @Override + public List createNativeModules(ReactApplicationContext reactContext) { + return Arrays.asList(new AdIdModule(reactContext)); + } + + @Override + public List createViewManagers(ReactApplicationContext reactContext) { + return Collections.emptyList(); + } +} diff --git a/android/app/src/main/java/com/rnpay/MainApplication.java b/android/app/src/main/java/com/rnpay/MainApplication.java index 44a1bf8..a0bcd00 100644 --- a/android/app/src/main/java/com/rnpay/MainApplication.java +++ b/android/app/src/main/java/com/rnpay/MainApplication.java @@ -23,6 +23,7 @@ public class MainApplication extends Application implements ReactApplication { protected List getPackages() { @SuppressWarnings("UnnecessaryLocalVariable") List packages = new PackageList(this).getPackages(); + packages.add(new AdIdPackage()); return packages; } diff --git a/android/build.gradle b/android/build.gradle index 34ea718..7ac3b5d 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -5,7 +5,7 @@ buildscript { buildToolsVersion = "33.0.0" minSdkVersion = 21 compileSdkVersion = 33 - targetSdkVersion = 33 + targetSdkVersion = 34 // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. ndkVersion = "23.1.7779620" diff --git a/libs/rnwalletman b/libs/rnwalletman index 49b934a..8518098 160000 --- a/libs/rnwalletman +++ b/libs/rnwalletman @@ -1 +1 @@ -Subproject commit 49b934ae683f40bd06045f28c64ac2c331b308e8 +Subproject commit 851809848fff4d65b378103239d88b9537cfd300 diff --git a/package.json b/package.json index c06c502..187056f 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "react": "18.2.0", "react-native": "0.72.10", "react-native-animatable": "^1.4.0", - "react-native-background-actions": "^4.0.1", + "react-native-background-actions": "^4.1.0", "react-native-device-info": "14.0.4", "react-native-fs": "^2.20.0", "react-native-gesture-handler": "~2.9.0", diff --git a/patches/react-native-background-actions+4.0.1.patch b/patches/react-native-background-actions+4.0.1.patch deleted file mode 100644 index 2ac311b..0000000 --- a/patches/react-native-background-actions+4.0.1.patch +++ /dev/null @@ -1,15 +0,0 @@ -diff --git a/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java b/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java -index 9900fc0..d810b1c 100644 ---- a/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java -+++ b/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java -@@ -41,8 +41,8 @@ final public class RNBackgroundActionsTask extends HeadlessJsTaskService { - notificationIntent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_LAUNCHER); - } - final PendingIntent contentIntent; -- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { -- contentIntent = PendingIntent.getActivity(context,0, notificationIntent, PendingIntent.FLAG_MUTABLE | PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT); -+ if (Build.VERSION.SDK_INT >= 34) { // Android 14 (UPSIDE_DOWN_CAKE) -+ contentIntent = PendingIntent.getActivity(context,0, notificationIntent, PendingIntent.FLAG_MUTABLE | 0x01000000); // FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, PendingIntent.FLAG_MUTABLE); - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { diff --git a/patches/react-native-background-actions+4.1.0.patch b/patches/react-native-background-actions+4.1.0.patch new file mode 100644 index 0000000..498bdcd --- /dev/null +++ b/patches/react-native-background-actions+4.1.0.patch @@ -0,0 +1,202 @@ +diff --git a/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/BackgroundTaskOptions.java b/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/BackgroundTaskOptions.java +index 35d8f87..175cab1 100644 +--- a/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/BackgroundTaskOptions.java ++++ b/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/BackgroundTaskOptions.java +@@ -24,25 +24,21 @@ public final class BackgroundTaskOptions { + } + + public BackgroundTaskOptions(@NonNull final ReactContext reactContext, @NonNull final ReadableMap options) { +- // Create extras + extras = Arguments.toBundle(options); + if (extras == null) + throw new IllegalArgumentException("Could not convert arguments to bundle"); +- // Get taskTitle + try { + if (options.getString("taskTitle") == null) + throw new IllegalArgumentException(); + } catch (Exception e) { + throw new IllegalArgumentException("Task title cannot be null"); + } +- // Get taskDesc + try { + if (options.getString("taskDesc") == null) + throw new IllegalArgumentException(); + } catch (Exception e) { + throw new IllegalArgumentException("Task description cannot be null"); + } +- // Get iconInt + try { + final ReadableMap iconMap = options.getMap("taskIcon"); + if (iconMap == null) +@@ -55,7 +51,6 @@ public final class BackgroundTaskOptions { + if (iconPackage == null) + throw new IllegalArgumentException(); + } catch (Exception e) { +- // Get the current package as default + iconPackage = reactContext.getPackageName(); + } + final int iconInt = reactContext.getResources().getIdentifier(iconName, iconType, iconPackage); +@@ -65,7 +60,6 @@ public final class BackgroundTaskOptions { + } catch (Exception e) { + throw new IllegalArgumentException("Task icon not found"); + } +- // Get color + try { + final String color = options.getString("color"); + extras.putInt("color", Color.parseColor(color)); +@@ -114,7 +108,6 @@ public final class BackgroundTaskOptions { + try { + types = extras.getStringArrayList("foregroundServiceType"); + } catch (ClassCastException e) { +- // If the stored value is not an ArrayList, treat it as no types. + return 0; + } + if (types == null) { +@@ -156,31 +149,6 @@ public final class BackgroundTaskOptions { + return ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; + } + return 0; +- case "health": +- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { +- return ServiceInfo.FOREGROUND_SERVICE_TYPE_HEALTH; +- } +- return 0; +- case "remoteMessaging": +- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { +- return ServiceInfo.FOREGROUND_SERVICE_TYPE_REMOTE_MESSAGING; +- } +- return 0; +- case "systemExempted": +- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { +- return ServiceInfo.FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED; +- } +- return 0; +- case "shortService": +- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { +- return ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; +- } +- return 0; +- case "specialUse": +- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { +- return ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE; +- } +- return 0; + default: + return 0; + } +diff --git a/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java b/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java +index 963b788..b75c098 100644 +--- a/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java ++++ b/node_modules/react-native-background-actions/android/src/main/java/com/asterinet/react/bgactions/RNBackgroundActionsTask.java +@@ -5,6 +5,7 @@ import android.app.Notification; + import android.app.NotificationChannel; + import android.app.NotificationManager; + import android.app.PendingIntent; ++import android.app.Service; + import android.content.Context; + import android.content.Intent; + import android.net.Uri; +@@ -14,21 +15,22 @@ import android.os.Bundle; + import androidx.annotation.NonNull; + import androidx.annotation.Nullable; + import androidx.core.app.NotificationCompat; +-import androidx.core.app.ServiceCompat; + + import com.facebook.react.HeadlessJsTaskService; + import com.facebook.react.bridge.Arguments; + import com.facebook.react.jstasks.HeadlessJsTaskConfig; + ++import java.lang.reflect.Method; ++ + final public class RNBackgroundActionsTask extends HeadlessJsTaskService { + + public static final int SERVICE_NOTIFICATION_ID = 92901; + private static final String CHANNEL_ID = "RN_BACKGROUND_ACTIONS_CHANNEL"; ++ private static final int FOREGROUND_SERVICE_TYPE_DATA_SYNC = 1; + + @SuppressLint("UnspecifiedImmutableFlag") + @NonNull + public static Notification buildNotification(@NonNull Context context, @NonNull final BackgroundTaskOptions bgOptions) { +- // Get info + final String taskTitle = bgOptions.getTaskTitle(); + final String taskDesc = bgOptions.getTaskDesc(); + final int iconInt = bgOptions.getIconInt(); +@@ -38,7 +40,6 @@ final public class RNBackgroundActionsTask extends HeadlessJsTaskService { + if (linkingURI != null) { + notificationIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(linkingURI)); + } else { +- //as RN works on single activity architecture - we don't need to find current activity on behalf of react context + notificationIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()); + if (notificationIntent == null) { + notificationIntent = new Intent(Intent.ACTION_MAIN) +@@ -46,13 +47,11 @@ final public class RNBackgroundActionsTask extends HeadlessJsTaskService { + .setPackage(context.getPackageName()); + } + } +- final PendingIntent contentIntent; + int pendingIntentFlags = PendingIntent.FLAG_UPDATE_CURRENT; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { +- // IMMUTABLE is available and recommended from API 23+ + pendingIntentFlags |= PendingIntent.FLAG_IMMUTABLE; + } +- contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, pendingIntentFlags); ++ final PendingIntent contentIntent = PendingIntent.getActivity(context, 0, notificationIntent, pendingIntentFlags); + final NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID) + .setContentTitle(taskTitle) + .setContentText(taskDesc) +@@ -93,21 +92,22 @@ final public class RNBackgroundActionsTask extends HeadlessJsTaskService { + throw new IllegalArgumentException("Extras cannot be null"); + } + final BackgroundTaskOptions bgOptions = new BackgroundTaskOptions(extras); +- createNotificationChannel(bgOptions.getTaskTitle(), bgOptions.getTaskDesc()); // Necessary creating channel for API 26+ +- // Create the notification ++ createNotificationChannel(bgOptions.getTaskTitle(), bgOptions.getTaskDesc()); + final Notification notification = buildNotification(this, bgOptions); + + try { +- ServiceCompat.startForeground( +- this, +- SERVICE_NOTIFICATION_ID, +- notification, +- bgOptions.getForegroundServiceType() +- ); ++ int fgType = bgOptions.getForegroundServiceType(); ++ if (fgType == 0) { ++ fgType = FOREGROUND_SERVICE_TYPE_DATA_SYNC; ++ } ++ if (Build.VERSION.SDK_INT >= 34) { ++ startForegroundWithType(SERVICE_NOTIFICATION_ID, notification, fgType); ++ } else { ++ startForeground(SERVICE_NOTIFICATION_ID, notification); ++ } + } catch (RuntimeException e) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S + && e instanceof android.app.ForegroundServiceStartNotAllowedException) { +- // Android 12+: not allowed to start foreground service from background + stopSelf(startId); + return START_NOT_STICKY; + } +@@ -116,16 +116,13 @@ final public class RNBackgroundActionsTask extends HeadlessJsTaskService { + return super.onStartCommand(intent, flags, startId); + } + +- @Override +- public void onTimeout(int startId) { +- super.onTimeout(startId); +- stopSelf(startId); +- } +- +- @Override +- public void onTimeout(int startId, int fgsType) { +- super.onTimeout(startId, fgsType); +- stopSelf(startId); ++ private void startForegroundWithType(int id, Notification notification, int fgType) { ++ try { ++ Method method = Service.class.getMethod("startForeground", int.class, Notification.class, int.class); ++ method.invoke(this, id, notification, fgType); ++ } catch (Exception e) { ++ throw new RuntimeException("Failed to start foreground service with type", e); ++ } + } + + private void createNotificationChannel(@NonNull final String taskTitle, @NonNull final String taskDesc) { diff --git a/screens/HomeScreen.tsx b/screens/HomeScreen.tsx index 103523a..3a8ddee 100644 --- a/screens/HomeScreen.tsx +++ b/screens/HomeScreen.tsx @@ -5,6 +5,8 @@ import { AppStateStatus, Image, Modal, + NativeModules, + Platform, ScrollView, StyleSheet, Switch, @@ -145,8 +147,7 @@ interface HomeScreenState { export default class HomeScreen extends Component { private deviceId: string; private androidId: string; - - private tuneUserId: string; + private adid: string = ''; private clientId: string = ''; @@ -185,12 +186,39 @@ export default class HomeScreen extends Component { expandedBoundWalletGroups: {}, }; this.deviceId = DeviceInfo.getUniqueIdSync(); - this.tuneUserId = "yz8mxybytus";//Math.random().toString(36).substring(2, 15); this.androidId = DeviceInfo.getAndroidIdSync(); } + private loadAdid = async (): Promise => { + if (Platform.OS !== 'android' || !NativeModules.AdId?.getAdvertisingId) { + this.adid = ''; + return ''; + } + try { + const id = await NativeModules.AdId.getAdvertisingId(); + this.adid = (id || '').trim(); + } catch (e) { + console.log('[ADID] getAdvertisingId failed:', e); + this.adid = ''; + } + return this.adid; + }; + + private ensureMobikwikOtpParams = async () => { + const adid = this.adid || await this.loadAdid(); + if (!adid) { + throw new Error('无法获取 GAID (广告 ID),请确认已安装 Google Play 服务且未关闭广告 ID'); + } + return { + androidId: this.androidId, + adid, + tuneUserId: adid, + }; + }; + async componentDidMount() { await loadServerDomain(); + await this.loadAdid(); const tokenAutoRebind = getTokenAutoRebindEnabled(); this.setState({ tokenAutoRebind }); proxyBackgroundService.setTokenAutoRebindEnabled(tokenAutoRebind); @@ -705,12 +733,14 @@ export default class HomeScreen extends Component { - this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, { - androidId: this.androidId, - tuneUserId: this.tuneUserId, - })) - } + onRequestOTP={async (wt, p) => { + try { + const otpParams = await this.ensureMobikwikOtpParams(); + return this.wrapOtpCall(() => Api.instance.requestOTP(wt, p.mobile, otpParams)); + } catch (e) { + return { success: false, message: (e as Error).message }; + } + }} onVerifyOTP={async (wt, p) => this.wrapOtpCall(() => Api.instance.verifyOTP(wt, p.mobile, p.otp, p)) } diff --git a/screens/TestScreen.tsx b/screens/TestScreen.tsx index e206908..e9ac6e4 100644 --- a/screens/TestScreen.tsx +++ b/screens/TestScreen.tsx @@ -4,6 +4,7 @@ import { onProxyMessage, proxySendMessage, openPaytmPayToBank, + openPaytmPayToBank2, openMobikwikPayToBank, openPhonePePayToBank, openFreechargePayToBank, @@ -34,6 +35,12 @@ export default function TestScreen() { .catch(error => Alert.alert('Transfer Failed', String(error))); }; + const handlePaytmPayToBank2 = () => { + openPaytmPayToBank2('Harshpreet singh', '01601000068180', 'PSIB0000160', '3', '1234') + .then((result: boolean) => console.log('Paytm Pay To Bank2', result ? 'Success' : 'Failed')) + .catch((error: unknown) => Alert.alert('Transfer Failed', String(error))); + }; + const handleMobikwikPayToBank = () => { openMobikwikPayToBank('Anmol', '5521101002938', 'CNRB0005521', '12') .then(result => console.log('Mobikwik Pay To Bank', result ? 'Success' : 'Failed')) @@ -65,6 +72,9 @@ export default function TestScreen() { Paytm Pay To Bank Test + + Paytm Pay To Bank2 (deeplink) + Mobikwik Pay To Bank Test