react-native-otp-auto-verify 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +452 -300
- package/android/src/main/java/com/otpautoverify/AppSignatureHelper.kt +80 -80
- package/android/src/main/java/com/otpautoverify/OtpAutoVerifyModule.kt +146 -146
- package/android/src/main/java/com/otpautoverify/SmsRetrieverBroadcastReceiver.kt +61 -61
- package/ios/OtpAutoVerify.h +5 -5
- package/ios/OtpAutoVerify.mm +36 -36
- package/lib/commonjs/NativeOtpAutoVerify.ts +11 -0
- package/lib/commonjs/core/extractOtp.js +24 -0
- package/lib/commonjs/core/extractOtp.js.map +1 -0
- package/lib/commonjs/hooks/useOtpVerification.js +70 -0
- package/lib/commonjs/hooks/useOtpVerification.js.map +1 -0
- package/lib/commonjs/index.js +57 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/native/bridge.js +71 -0
- package/lib/commonjs/native/bridge.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/module/NativeOtpAutoVerify.ts +13 -11
- package/lib/module/index.js +8 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/NativeOtpAutoVerify.d.ts +2 -2
- package/lib/typescript/src/NativeOtpAutoVerify.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +8 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +175 -183
- package/src/NativeOtpAutoVerify.ts +13 -11
- package/src/index.tsx +8 -2
|
@@ -1,80 +1,80 @@
|
|
|
1
|
-
package com.otpautoverify
|
|
2
|
-
|
|
3
|
-
import android.content.Context
|
|
4
|
-
import android.content.ContextWrapper
|
|
5
|
-
import android.content.pm.PackageManager
|
|
6
|
-
import android.os.Build
|
|
7
|
-
import android.util.Base64
|
|
8
|
-
import android.util.Log
|
|
9
|
-
import java.nio.charset.StandardCharsets
|
|
10
|
-
import java.security.MessageDigest
|
|
11
|
-
import java.security.NoSuchAlgorithmException
|
|
12
|
-
import java.util.ArrayList
|
|
13
|
-
import java.util.Arrays
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Computes the 11-character app hash required by the SMS Retriever API.
|
|
17
|
-
* Uses GET_SIGNING_CERTIFICATES on API 28+ and GET_SIGNATURES on older versions.
|
|
18
|
-
*/
|
|
19
|
-
class AppSignatureHelper(context: Context) : ContextWrapper(context) {
|
|
20
|
-
|
|
21
|
-
companion object {
|
|
22
|
-
private const val TAG = "AppSignatureHelper"
|
|
23
|
-
private const val HASH_TYPE = "SHA-256"
|
|
24
|
-
private const val NUM_HASHED_BYTES = 9
|
|
25
|
-
private const val NUM_BASE64_CHAR = 11
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Returns app hash strings for the current package.
|
|
30
|
-
* Include one of these in your SMS message (e.g. at the end) for the Retriever to match.
|
|
31
|
-
*/
|
|
32
|
-
fun getAppSignatures(): ArrayList<String> {
|
|
33
|
-
val appCodes = ArrayList<String>()
|
|
34
|
-
try {
|
|
35
|
-
val packageName = packageName
|
|
36
|
-
val packageManager = packageManager
|
|
37
|
-
val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
38
|
-
val packageInfo = packageManager.getPackageInfo(
|
|
39
|
-
packageName,
|
|
40
|
-
PackageManager.GET_SIGNING_CERTIFICATES
|
|
41
|
-
)
|
|
42
|
-
packageInfo.signingInfo?.apkContentsSigners
|
|
43
|
-
} else {
|
|
44
|
-
@Suppress("DEPRECATION")
|
|
45
|
-
val packageInfo = packageManager.getPackageInfo(
|
|
46
|
-
packageName,
|
|
47
|
-
PackageManager.GET_SIGNATURES
|
|
48
|
-
)
|
|
49
|
-
packageInfo.signatures
|
|
50
|
-
} ?: return appCodes
|
|
51
|
-
|
|
52
|
-
for (signature in signatures) {
|
|
53
|
-
val hash = hash(packageName, signature.toCharsString())
|
|
54
|
-
if (hash != null) {
|
|
55
|
-
appCodes.add(hash)
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
} catch (e: PackageManager.NameNotFoundException) {
|
|
59
|
-
Log.e(TAG, "Unable to find package to obtain hash.", e)
|
|
60
|
-
}
|
|
61
|
-
return appCodes
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
private fun hash(packageName: String, signature: String): String? {
|
|
65
|
-
return try {
|
|
66
|
-
val appInfo = "$packageName $signature"
|
|
67
|
-
val messageDigest = MessageDigest.getInstance(HASH_TYPE)
|
|
68
|
-
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8))
|
|
69
|
-
var hashSignature = messageDigest.digest()
|
|
70
|
-
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES)
|
|
71
|
-
var base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP)
|
|
72
|
-
base64Hash = base64Hash.substring(0, minOf(NUM_BASE64_CHAR, base64Hash.length))
|
|
73
|
-
Log.d(TAG, "pkg: $packageName -- hash: $base64Hash")
|
|
74
|
-
base64Hash
|
|
75
|
-
} catch (e: NoSuchAlgorithmException) {
|
|
76
|
-
Log.e(TAG, "hash: NoSuchAlgorithm", e)
|
|
77
|
-
null
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
}
|
|
1
|
+
package com.otpautoverify
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.content.ContextWrapper
|
|
5
|
+
import android.content.pm.PackageManager
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import android.util.Base64
|
|
8
|
+
import android.util.Log
|
|
9
|
+
import java.nio.charset.StandardCharsets
|
|
10
|
+
import java.security.MessageDigest
|
|
11
|
+
import java.security.NoSuchAlgorithmException
|
|
12
|
+
import java.util.ArrayList
|
|
13
|
+
import java.util.Arrays
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Computes the 11-character app hash required by the SMS Retriever API.
|
|
17
|
+
* Uses GET_SIGNING_CERTIFICATES on API 28+ and GET_SIGNATURES on older versions.
|
|
18
|
+
*/
|
|
19
|
+
class AppSignatureHelper(context: Context) : ContextWrapper(context) {
|
|
20
|
+
|
|
21
|
+
companion object {
|
|
22
|
+
private const val TAG = "AppSignatureHelper"
|
|
23
|
+
private const val HASH_TYPE = "SHA-256"
|
|
24
|
+
private const val NUM_HASHED_BYTES = 9
|
|
25
|
+
private const val NUM_BASE64_CHAR = 11
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Returns app hash strings for the current package.
|
|
30
|
+
* Include one of these in your SMS message (e.g. at the end) for the Retriever to match.
|
|
31
|
+
*/
|
|
32
|
+
fun getAppSignatures(): ArrayList<String> {
|
|
33
|
+
val appCodes = ArrayList<String>()
|
|
34
|
+
try {
|
|
35
|
+
val packageName = packageName
|
|
36
|
+
val packageManager = packageManager
|
|
37
|
+
val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
38
|
+
val packageInfo = packageManager.getPackageInfo(
|
|
39
|
+
packageName,
|
|
40
|
+
PackageManager.GET_SIGNING_CERTIFICATES
|
|
41
|
+
)
|
|
42
|
+
packageInfo.signingInfo?.apkContentsSigners
|
|
43
|
+
} else {
|
|
44
|
+
@Suppress("DEPRECATION")
|
|
45
|
+
val packageInfo = packageManager.getPackageInfo(
|
|
46
|
+
packageName,
|
|
47
|
+
PackageManager.GET_SIGNATURES
|
|
48
|
+
)
|
|
49
|
+
packageInfo.signatures
|
|
50
|
+
} ?: return appCodes
|
|
51
|
+
|
|
52
|
+
for (signature in signatures) {
|
|
53
|
+
val hash = hash(packageName, signature.toCharsString())
|
|
54
|
+
if (hash != null) {
|
|
55
|
+
appCodes.add(hash)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (e: PackageManager.NameNotFoundException) {
|
|
59
|
+
Log.e(TAG, "Unable to find package to obtain hash.", e)
|
|
60
|
+
}
|
|
61
|
+
return appCodes
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private fun hash(packageName: String, signature: String): String? {
|
|
65
|
+
return try {
|
|
66
|
+
val appInfo = "$packageName $signature"
|
|
67
|
+
val messageDigest = MessageDigest.getInstance(HASH_TYPE)
|
|
68
|
+
messageDigest.update(appInfo.toByteArray(StandardCharsets.UTF_8))
|
|
69
|
+
var hashSignature = messageDigest.digest()
|
|
70
|
+
hashSignature = Arrays.copyOfRange(hashSignature, 0, NUM_HASHED_BYTES)
|
|
71
|
+
var base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING or Base64.NO_WRAP)
|
|
72
|
+
base64Hash = base64Hash.substring(0, minOf(NUM_BASE64_CHAR, base64Hash.length))
|
|
73
|
+
Log.d(TAG, "pkg: $packageName -- hash: $base64Hash")
|
|
74
|
+
base64Hash
|
|
75
|
+
} catch (e: NoSuchAlgorithmException) {
|
|
76
|
+
Log.e(TAG, "hash: NoSuchAlgorithm", e)
|
|
77
|
+
null
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -1,146 +1,146 @@
|
|
|
1
|
-
package com.otpautoverify
|
|
2
|
-
|
|
3
|
-
import android.annotation.SuppressLint
|
|
4
|
-
import android.content.IntentFilter
|
|
5
|
-
import android.os.Build
|
|
6
|
-
import android.util.Log
|
|
7
|
-
import com.facebook.react.bridge.Arguments
|
|
8
|
-
import com.facebook.react.bridge.LifecycleEventListener
|
|
9
|
-
import com.facebook.react.bridge.Promise
|
|
10
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
11
|
-
import com.google.android.gms.auth.api.phone.SmsRetriever
|
|
12
|
-
import com.google.android.gms.tasks.OnFailureListener
|
|
13
|
-
import com.google.android.gms.tasks.OnSuccessListener
|
|
14
|
-
class OtpAutoVerifyModule(reactContext: ReactApplicationContext) :
|
|
15
|
-
NativeOtpAutoVerifySpec(reactContext), LifecycleEventListener {
|
|
16
|
-
|
|
17
|
-
companion object {
|
|
18
|
-
const val NAME = NativeOtpAutoVerifySpec.NAME
|
|
19
|
-
private const val TAG = "OtpAutoVerifyModule"
|
|
20
|
-
const val OTP_RECEIVED_EVENT = "otpReceived"
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
private var smsReceiver: SmsRetrieverBroadcastReceiver? = null
|
|
24
|
-
private var isReceiverRegistered = false
|
|
25
|
-
private var isListening = false
|
|
26
|
-
|
|
27
|
-
init {
|
|
28
|
-
reactContext.addLifecycleEventListener(this)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
override fun getTypedExportedConstants(): MutableMap<String, Any> {
|
|
32
|
-
return mutableMapOf("OTP_RECEIVED_EVENT" to OTP_RECEIVED_EVENT)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
override fun getHash(promise: Promise) {
|
|
36
|
-
try {
|
|
37
|
-
val helper = AppSignatureHelper(reactApplicationContext)
|
|
38
|
-
val signatures = helper.getAppSignatures()
|
|
39
|
-
val arr = Arguments.createArray()
|
|
40
|
-
for (s in signatures) {
|
|
41
|
-
arr.pushString(s)
|
|
42
|
-
}
|
|
43
|
-
promise.resolve(arr)
|
|
44
|
-
} catch (e: Exception) {
|
|
45
|
-
Log.e(TAG, "getHash failed", e)
|
|
46
|
-
promise.reject("GET_HASH_ERROR", e.message, e)
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
override fun startSmsRetriever(promise: Promise) {
|
|
51
|
-
val activity = currentActivity
|
|
52
|
-
if (activity == null) {
|
|
53
|
-
promise.reject("NO_ACTIVITY", "No current activity. Ensure the app is in the foreground.")
|
|
54
|
-
return
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
registerReceiverIfNecessary(activity)
|
|
58
|
-
|
|
59
|
-
val client = SmsRetriever.getClient(reactApplicationContext)
|
|
60
|
-
client.startSmsRetriever()
|
|
61
|
-
.addOnSuccessListener(OnSuccessListener {
|
|
62
|
-
Log.d(TAG, "SMS retriever started")
|
|
63
|
-
isListening = true
|
|
64
|
-
promise.resolve(true)
|
|
65
|
-
})
|
|
66
|
-
.addOnFailureListener(OnFailureListener { e ->
|
|
67
|
-
Log.e(TAG, "Failed to start SMS retriever", e)
|
|
68
|
-
promise.reject("START_SMS_RETRIEVER_ERROR", e.message, e)
|
|
69
|
-
})
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
|
73
|
-
private fun registerReceiverIfNecessary(activity: android.app.Activity) {
|
|
74
|
-
if (isReceiverRegistered) return
|
|
75
|
-
try {
|
|
76
|
-
smsReceiver = SmsRetrieverBroadcastReceiver(reactApplicationContext, OTP_RECEIVED_EVENT)
|
|
77
|
-
val filter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
|
|
78
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
79
|
-
activity.registerReceiver(
|
|
80
|
-
smsReceiver,
|
|
81
|
-
filter,
|
|
82
|
-
SmsRetriever.SEND_PERMISSION,
|
|
83
|
-
null,
|
|
84
|
-
android.content.Context.RECEIVER_EXPORTED
|
|
85
|
-
)
|
|
86
|
-
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
87
|
-
activity.registerReceiver(
|
|
88
|
-
smsReceiver,
|
|
89
|
-
filter,
|
|
90
|
-
android.content.Context.RECEIVER_EXPORTED
|
|
91
|
-
)
|
|
92
|
-
} else {
|
|
93
|
-
activity.registerReceiver(smsReceiver, filter)
|
|
94
|
-
}
|
|
95
|
-
isReceiverRegistered = true
|
|
96
|
-
Log.d(TAG, "SMS receiver registered")
|
|
97
|
-
} catch (e: Exception) {
|
|
98
|
-
Log.e(TAG, "Failed to register SMS receiver", e)
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private fun unregisterReceiver() {
|
|
103
|
-
val activity = currentActivity
|
|
104
|
-
if (isReceiverRegistered && activity != null && smsReceiver != null) {
|
|
105
|
-
try {
|
|
106
|
-
activity.unregisterReceiver(smsReceiver)
|
|
107
|
-
Log.d(TAG, "SMS receiver unregistered")
|
|
108
|
-
} catch (e: Exception) {
|
|
109
|
-
Log.e(TAG, "Failed to unregister SMS receiver", e)
|
|
110
|
-
}
|
|
111
|
-
isReceiverRegistered = false
|
|
112
|
-
smsReceiver = null
|
|
113
|
-
}
|
|
114
|
-
isListening = false
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
override fun addListener(eventName: String) {
|
|
118
|
-
// Required for NativeEventEmitter; no-op.
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
override fun removeListeners(count: Double) {
|
|
122
|
-
unregisterReceiver()
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
override fun onHostResume() {
|
|
126
|
-
// Optionally re-register if we were listening and activity was recreated.
|
|
127
|
-
if (isListening && currentActivity != null && !isReceiverRegistered) {
|
|
128
|
-
currentActivity?.let { registerReceiverIfNecessary(it) }
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
override fun onHostPause() {
|
|
133
|
-
unregisterReceiver()
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
override fun onHostDestroy() {
|
|
137
|
-
unregisterReceiver()
|
|
138
|
-
reactApplicationContext.removeLifecycleEventListener(this)
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
override fun invalidate() {
|
|
142
|
-
unregisterReceiver()
|
|
143
|
-
reactApplicationContext.removeLifecycleEventListener(this)
|
|
144
|
-
super.invalidate()
|
|
145
|
-
}
|
|
146
|
-
}
|
|
1
|
+
package com.otpautoverify
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.content.IntentFilter
|
|
5
|
+
import android.os.Build
|
|
6
|
+
import android.util.Log
|
|
7
|
+
import com.facebook.react.bridge.Arguments
|
|
8
|
+
import com.facebook.react.bridge.LifecycleEventListener
|
|
9
|
+
import com.facebook.react.bridge.Promise
|
|
10
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
11
|
+
import com.google.android.gms.auth.api.phone.SmsRetriever
|
|
12
|
+
import com.google.android.gms.tasks.OnFailureListener
|
|
13
|
+
import com.google.android.gms.tasks.OnSuccessListener
|
|
14
|
+
class OtpAutoVerifyModule(reactContext: ReactApplicationContext) :
|
|
15
|
+
NativeOtpAutoVerifySpec(reactContext), LifecycleEventListener {
|
|
16
|
+
|
|
17
|
+
companion object {
|
|
18
|
+
const val NAME = NativeOtpAutoVerifySpec.NAME
|
|
19
|
+
private const val TAG = "OtpAutoVerifyModule"
|
|
20
|
+
const val OTP_RECEIVED_EVENT = "otpReceived"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private var smsReceiver: SmsRetrieverBroadcastReceiver? = null
|
|
24
|
+
private var isReceiverRegistered = false
|
|
25
|
+
private var isListening = false
|
|
26
|
+
|
|
27
|
+
init {
|
|
28
|
+
reactContext.addLifecycleEventListener(this)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
override fun getTypedExportedConstants(): MutableMap<String, Any> {
|
|
32
|
+
return mutableMapOf("OTP_RECEIVED_EVENT" to OTP_RECEIVED_EVENT)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
override fun getHash(promise: Promise) {
|
|
36
|
+
try {
|
|
37
|
+
val helper = AppSignatureHelper(reactApplicationContext)
|
|
38
|
+
val signatures = helper.getAppSignatures()
|
|
39
|
+
val arr = Arguments.createArray()
|
|
40
|
+
for (s in signatures) {
|
|
41
|
+
arr.pushString(s)
|
|
42
|
+
}
|
|
43
|
+
promise.resolve(arr)
|
|
44
|
+
} catch (e: Exception) {
|
|
45
|
+
Log.e(TAG, "getHash failed", e)
|
|
46
|
+
promise.reject("GET_HASH_ERROR", e.message, e)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
override fun startSmsRetriever(promise: Promise) {
|
|
51
|
+
val activity = currentActivity
|
|
52
|
+
if (activity == null) {
|
|
53
|
+
promise.reject("NO_ACTIVITY", "No current activity. Ensure the app is in the foreground.")
|
|
54
|
+
return
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
registerReceiverIfNecessary(activity)
|
|
58
|
+
|
|
59
|
+
val client = SmsRetriever.getClient(reactApplicationContext)
|
|
60
|
+
client.startSmsRetriever()
|
|
61
|
+
.addOnSuccessListener(OnSuccessListener {
|
|
62
|
+
Log.d(TAG, "SMS retriever started")
|
|
63
|
+
isListening = true
|
|
64
|
+
promise.resolve(true)
|
|
65
|
+
})
|
|
66
|
+
.addOnFailureListener(OnFailureListener { e ->
|
|
67
|
+
Log.e(TAG, "Failed to start SMS retriever", e)
|
|
68
|
+
promise.reject("START_SMS_RETRIEVER_ERROR", e.message, e)
|
|
69
|
+
})
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
@SuppressLint("UnspecifiedRegisterReceiverFlag")
|
|
73
|
+
private fun registerReceiverIfNecessary(activity: android.app.Activity) {
|
|
74
|
+
if (isReceiverRegistered) return
|
|
75
|
+
try {
|
|
76
|
+
smsReceiver = SmsRetrieverBroadcastReceiver(reactApplicationContext, OTP_RECEIVED_EVENT)
|
|
77
|
+
val filter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
|
|
78
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
79
|
+
activity.registerReceiver(
|
|
80
|
+
smsReceiver,
|
|
81
|
+
filter,
|
|
82
|
+
SmsRetriever.SEND_PERMISSION,
|
|
83
|
+
null,
|
|
84
|
+
android.content.Context.RECEIVER_EXPORTED
|
|
85
|
+
)
|
|
86
|
+
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
|
|
87
|
+
activity.registerReceiver(
|
|
88
|
+
smsReceiver,
|
|
89
|
+
filter,
|
|
90
|
+
android.content.Context.RECEIVER_EXPORTED
|
|
91
|
+
)
|
|
92
|
+
} else {
|
|
93
|
+
activity.registerReceiver(smsReceiver, filter)
|
|
94
|
+
}
|
|
95
|
+
isReceiverRegistered = true
|
|
96
|
+
Log.d(TAG, "SMS receiver registered")
|
|
97
|
+
} catch (e: Exception) {
|
|
98
|
+
Log.e(TAG, "Failed to register SMS receiver", e)
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private fun unregisterReceiver() {
|
|
103
|
+
val activity = currentActivity
|
|
104
|
+
if (isReceiverRegistered && activity != null && smsReceiver != null) {
|
|
105
|
+
try {
|
|
106
|
+
activity.unregisterReceiver(smsReceiver)
|
|
107
|
+
Log.d(TAG, "SMS receiver unregistered")
|
|
108
|
+
} catch (e: Exception) {
|
|
109
|
+
Log.e(TAG, "Failed to unregister SMS receiver", e)
|
|
110
|
+
}
|
|
111
|
+
isReceiverRegistered = false
|
|
112
|
+
smsReceiver = null
|
|
113
|
+
}
|
|
114
|
+
isListening = false
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
override fun addListener(eventName: String) {
|
|
118
|
+
// Required for NativeEventEmitter; no-op.
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
override fun removeListeners(count: Double) {
|
|
122
|
+
unregisterReceiver()
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
override fun onHostResume() {
|
|
126
|
+
// Optionally re-register if we were listening and activity was recreated.
|
|
127
|
+
if (isListening && currentActivity != null && !isReceiverRegistered) {
|
|
128
|
+
currentActivity?.let { registerReceiverIfNecessary(it) }
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
override fun onHostPause() {
|
|
133
|
+
unregisterReceiver()
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
override fun onHostDestroy() {
|
|
137
|
+
unregisterReceiver()
|
|
138
|
+
reactApplicationContext.removeLifecycleEventListener(this)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
override fun invalidate() {
|
|
142
|
+
unregisterReceiver()
|
|
143
|
+
reactApplicationContext.removeLifecycleEventListener(this)
|
|
144
|
+
super.invalidate()
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -1,61 +1,61 @@
|
|
|
1
|
-
package com.otpautoverify
|
|
2
|
-
|
|
3
|
-
import android.content.BroadcastReceiver
|
|
4
|
-
import android.content.Context
|
|
5
|
-
import android.content.Intent
|
|
6
|
-
import android.os.Bundle
|
|
7
|
-
import android.util.Log
|
|
8
|
-
import com.facebook.react.bridge.ReactApplicationContext
|
|
9
|
-
import com.google.android.gms.auth.api.phone.SmsRetriever
|
|
10
|
-
import com.google.android.gms.common.api.CommonStatusCodes
|
|
11
|
-
import com.google.android.gms.common.api.Status
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Receives SMS Retriever API results and forwards the message or timeout to JS via RCTDeviceEventEmitter.
|
|
15
|
-
* No READ_SMS permission required; fully Play Store compliant.
|
|
16
|
-
*/
|
|
17
|
-
class SmsRetrieverBroadcastReceiver(
|
|
18
|
-
private val reactContext: ReactApplicationContext,
|
|
19
|
-
private val eventName: String
|
|
20
|
-
) : BroadcastReceiver() {
|
|
21
|
-
|
|
22
|
-
companion object {
|
|
23
|
-
private const val TAG = "SmsRetrieverReceiver"
|
|
24
|
-
const val TIMEOUT_MESSAGE = "Timeout Error."
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
override fun onReceive(context: Context?, intent: Intent?) {
|
|
28
|
-
if (intent?.action != SmsRetriever.SMS_RETRIEVED_ACTION) return
|
|
29
|
-
val extras: Bundle = intent.extras ?: return
|
|
30
|
-
val status = extras.get(SmsRetriever.EXTRA_STATUS) as? Status ?: return
|
|
31
|
-
|
|
32
|
-
when (status.statusCode) {
|
|
33
|
-
CommonStatusCodes.SUCCESS -> {
|
|
34
|
-
val message = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE)
|
|
35
|
-
if (message != null) {
|
|
36
|
-
Log.d(TAG, "SMS received")
|
|
37
|
-
emitMessage(message)
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
CommonStatusCodes.TIMEOUT -> {
|
|
41
|
-
Log.d(TAG, "SMS retriever timeout")
|
|
42
|
-
emitMessage(TIMEOUT_MESSAGE)
|
|
43
|
-
}
|
|
44
|
-
else -> {
|
|
45
|
-
Log.w(TAG, "SMS retriever status: ${status.statusCode}")
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
private fun emitMessage(message: String) {
|
|
51
|
-
if (!reactContext.hasActiveReactInstance()) return
|
|
52
|
-
reactContext.runOnJSQueueThread {
|
|
53
|
-
try {
|
|
54
|
-
reactContext.getJSModule(com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
55
|
-
?.emit(eventName, message)
|
|
56
|
-
} catch (e: Exception) {
|
|
57
|
-
Log.e(TAG, "Failed to emit OTP event", e)
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
1
|
+
package com.otpautoverify
|
|
2
|
+
|
|
3
|
+
import android.content.BroadcastReceiver
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
6
|
+
import android.os.Bundle
|
|
7
|
+
import android.util.Log
|
|
8
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
9
|
+
import com.google.android.gms.auth.api.phone.SmsRetriever
|
|
10
|
+
import com.google.android.gms.common.api.CommonStatusCodes
|
|
11
|
+
import com.google.android.gms.common.api.Status
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Receives SMS Retriever API results and forwards the message or timeout to JS via RCTDeviceEventEmitter.
|
|
15
|
+
* No READ_SMS permission required; fully Play Store compliant.
|
|
16
|
+
*/
|
|
17
|
+
class SmsRetrieverBroadcastReceiver(
|
|
18
|
+
private val reactContext: ReactApplicationContext,
|
|
19
|
+
private val eventName: String
|
|
20
|
+
) : BroadcastReceiver() {
|
|
21
|
+
|
|
22
|
+
companion object {
|
|
23
|
+
private const val TAG = "SmsRetrieverReceiver"
|
|
24
|
+
const val TIMEOUT_MESSAGE = "Timeout Error."
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
override fun onReceive(context: Context?, intent: Intent?) {
|
|
28
|
+
if (intent?.action != SmsRetriever.SMS_RETRIEVED_ACTION) return
|
|
29
|
+
val extras: Bundle = intent.extras ?: return
|
|
30
|
+
val status = extras.get(SmsRetriever.EXTRA_STATUS) as? Status ?: return
|
|
31
|
+
|
|
32
|
+
when (status.statusCode) {
|
|
33
|
+
CommonStatusCodes.SUCCESS -> {
|
|
34
|
+
val message = extras.getString(SmsRetriever.EXTRA_SMS_MESSAGE)
|
|
35
|
+
if (message != null) {
|
|
36
|
+
Log.d(TAG, "SMS received")
|
|
37
|
+
emitMessage(message)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
CommonStatusCodes.TIMEOUT -> {
|
|
41
|
+
Log.d(TAG, "SMS retriever timeout")
|
|
42
|
+
emitMessage(TIMEOUT_MESSAGE)
|
|
43
|
+
}
|
|
44
|
+
else -> {
|
|
45
|
+
Log.w(TAG, "SMS retriever status: ${status.statusCode}")
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private fun emitMessage(message: String) {
|
|
51
|
+
if (!reactContext.hasActiveReactInstance()) return
|
|
52
|
+
reactContext.runOnJSQueueThread {
|
|
53
|
+
try {
|
|
54
|
+
reactContext.getJSModule(com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
55
|
+
?.emit(eventName, message)
|
|
56
|
+
} catch (e: Exception) {
|
|
57
|
+
Log.e(TAG, "Failed to emit OTP event", e)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
package/ios/OtpAutoVerify.h
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
#import <OtpAutoVerifySpec/OtpAutoVerifySpec.h>
|
|
2
|
-
|
|
3
|
-
@interface OtpAutoVerify : NSObject <NativeOtpAutoVerifySpec>
|
|
4
|
-
|
|
5
|
-
@end
|
|
1
|
+
#import <OtpAutoVerifySpec/OtpAutoVerifySpec.h>
|
|
2
|
+
|
|
3
|
+
@interface OtpAutoVerify : NSObject <NativeOtpAutoVerifySpec>
|
|
4
|
+
|
|
5
|
+
@end
|
package/ios/OtpAutoVerify.mm
CHANGED
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
#import "OtpAutoVerify.h"
|
|
2
|
-
|
|
3
|
-
@implementation OtpAutoVerify
|
|
4
|
-
|
|
5
|
-
- (NSDictionary *)getConstants {
|
|
6
|
-
return @{ @"OTP_RECEIVED_EVENT": @"otpReceived" };
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
- (void)getHash:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
10
|
-
resolve(@[]);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
- (void)startSmsRetriever:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
14
|
-
resolve(@NO);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
- (void)addListener:(NSString *)eventName {
|
|
18
|
-
// No-op on iOS; SMS Retriever is Android-only.
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
- (void)removeListeners:(double)count {
|
|
22
|
-
// No-op on iOS.
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
26
|
-
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
27
|
-
{
|
|
28
|
-
return std::make_shared<facebook::react::NativeOtpAutoVerifySpecJSI>(params);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
+ (NSString *)moduleName
|
|
32
|
-
{
|
|
33
|
-
return @"OtpAutoVerify";
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
@end
|
|
1
|
+
#import "OtpAutoVerify.h"
|
|
2
|
+
|
|
3
|
+
@implementation OtpAutoVerify
|
|
4
|
+
|
|
5
|
+
- (NSDictionary *)getConstants {
|
|
6
|
+
return @{ @"OTP_RECEIVED_EVENT": @"otpReceived" };
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
- (void)getHash:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
10
|
+
resolve(@[]);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
- (void)startSmsRetriever:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject {
|
|
14
|
+
resolve(@NO);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
- (void)addListener:(NSString *)eventName {
|
|
18
|
+
// No-op on iOS; SMS Retriever is Android-only.
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
- (void)removeListeners:(double)count {
|
|
22
|
+
// No-op on iOS.
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
26
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
27
|
+
{
|
|
28
|
+
return std::make_shared<facebook::react::NativeOtpAutoVerifySpecJSI>(params);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
+ (NSString *)moduleName
|
|
32
|
+
{
|
|
33
|
+
return @"OtpAutoVerify";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
@end
|