rampkit-expo-dev 0.0.18 → 0.0.22
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/android/build.gradle +87 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/expo/modules/rampkit/RampKitModule.kt +445 -0
- package/build/DeviceInfoCollector.d.ts +21 -0
- package/build/DeviceInfoCollector.js +200 -0
- package/build/EventManager.d.ts +155 -0
- package/build/EventManager.js +419 -0
- package/build/RampKit.d.ts +93 -9
- package/build/RampKit.js +224 -21
- package/build/RampKitNative.d.ts +151 -0
- package/build/RampKitNative.js +255 -0
- package/build/RampkitOverlay.d.ts +8 -0
- package/build/RampkitOverlay.js +72 -106
- package/build/constants.d.ts +18 -0
- package/build/constants.js +29 -0
- package/build/index.d.ts +12 -0
- package/build/index.js +30 -1
- package/build/types.d.ts +178 -0
- package/build/types.js +5 -0
- package/build/userId.d.ts +8 -0
- package/build/userId.js +28 -72
- package/expo-module.config.json +10 -0
- package/ios/RampKit.podspec +22 -0
- package/ios/RampKitModule.swift +407 -0
- package/package.json +9 -7
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
apply plugin: 'com.android.library'
|
|
2
|
+
apply plugin: 'kotlin-android'
|
|
3
|
+
apply plugin: 'maven-publish'
|
|
4
|
+
|
|
5
|
+
group = 'expo.modules.rampkit'
|
|
6
|
+
version = '0.0.20'
|
|
7
|
+
|
|
8
|
+
buildscript {
|
|
9
|
+
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
10
|
+
if (expoModulesCorePlugin.exists()) {
|
|
11
|
+
apply from: expoModulesCorePlugin
|
|
12
|
+
applyKotlinExpoModulesCorePlugin()
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
repositories {
|
|
16
|
+
mavenCentral()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
dependencies {
|
|
20
|
+
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.23")
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
afterEvaluate {
|
|
25
|
+
publishing {
|
|
26
|
+
publications {
|
|
27
|
+
release(MavenPublication) {
|
|
28
|
+
from components.release
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
repositories {
|
|
32
|
+
maven {
|
|
33
|
+
url = mavenLocal().url
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
android {
|
|
40
|
+
namespace "expo.modules.rampkit"
|
|
41
|
+
compileSdkVersion safeExtGet("compileSdkVersion", 34)
|
|
42
|
+
|
|
43
|
+
defaultConfig {
|
|
44
|
+
minSdkVersion safeExtGet("minSdkVersion", 23)
|
|
45
|
+
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
publishing {
|
|
49
|
+
singleVariant("release") {
|
|
50
|
+
withSourcesJar()
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
lintOptions {
|
|
55
|
+
abortOnError false
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
compileOptions {
|
|
59
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
60
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
kotlinOptions {
|
|
64
|
+
jvmTarget = JavaVersion.VERSION_17.majorVersion
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
repositories {
|
|
69
|
+
mavenCentral()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
dependencies {
|
|
73
|
+
implementation project(':expo-modules-core')
|
|
74
|
+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
|
|
75
|
+
implementation 'com.google.android.play:review:2.0.1'
|
|
76
|
+
implementation 'com.google.android.play:review-ktx:2.0.1'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
def getKotlinVersion() {
|
|
80
|
+
def kotlinVersion = rootProject.ext.has("kotlinVersion") ? rootProject.ext.get("kotlinVersion") : "1.9.23"
|
|
81
|
+
return kotlinVersion
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
def safeExtGet(prop, fallback) {
|
|
85
|
+
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
|
86
|
+
}
|
|
87
|
+
|
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
package expo.modules.rampkit
|
|
2
|
+
|
|
3
|
+
import android.Manifest
|
|
4
|
+
import android.app.Activity
|
|
5
|
+
import android.app.ActivityManager
|
|
6
|
+
import android.app.NotificationChannel
|
|
7
|
+
import android.app.NotificationManager
|
|
8
|
+
import android.content.Context
|
|
9
|
+
import android.content.SharedPreferences
|
|
10
|
+
import android.content.pm.PackageManager
|
|
11
|
+
import android.content.res.Configuration
|
|
12
|
+
import android.os.Build
|
|
13
|
+
import android.os.PowerManager
|
|
14
|
+
import android.os.VibrationEffect
|
|
15
|
+
import android.os.Vibrator
|
|
16
|
+
import android.os.VibratorManager
|
|
17
|
+
import android.provider.Settings
|
|
18
|
+
import android.util.DisplayMetrics
|
|
19
|
+
import android.view.WindowManager
|
|
20
|
+
import androidx.core.app.ActivityCompat
|
|
21
|
+
import androidx.core.content.ContextCompat
|
|
22
|
+
import com.google.android.play.core.review.ReviewManagerFactory
|
|
23
|
+
import expo.modules.kotlin.Promise
|
|
24
|
+
import expo.modules.kotlin.modules.Module
|
|
25
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
26
|
+
import java.text.SimpleDateFormat
|
|
27
|
+
import java.util.Date
|
|
28
|
+
import java.util.Locale
|
|
29
|
+
import java.util.TimeZone
|
|
30
|
+
import java.util.UUID
|
|
31
|
+
|
|
32
|
+
class RampKitModule : Module() {
|
|
33
|
+
private val PREFS_NAME = "rampkit_prefs"
|
|
34
|
+
private val USER_ID_KEY = "rk_user_id"
|
|
35
|
+
private val INSTALL_DATE_KEY = "rk_install_date"
|
|
36
|
+
private val LAUNCH_COUNT_KEY = "rk_launch_count"
|
|
37
|
+
private val LAST_LAUNCH_KEY = "rk_last_launch"
|
|
38
|
+
|
|
39
|
+
private val context: Context
|
|
40
|
+
get() = requireNotNull(appContext.reactContext)
|
|
41
|
+
|
|
42
|
+
private val prefs: SharedPreferences
|
|
43
|
+
get() = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
|
|
44
|
+
|
|
45
|
+
override fun definition() = ModuleDefinition {
|
|
46
|
+
Name("RampKit")
|
|
47
|
+
|
|
48
|
+
// ============================================================================
|
|
49
|
+
// Device Info
|
|
50
|
+
// ============================================================================
|
|
51
|
+
|
|
52
|
+
AsyncFunction("getDeviceInfo") {
|
|
53
|
+
collectDeviceInfo()
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
AsyncFunction("getUserId") {
|
|
57
|
+
getOrCreateUserId()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
AsyncFunction("getStoredValue") { key: String ->
|
|
61
|
+
prefs.getString(key, null)
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
AsyncFunction("setStoredValue") { key: String, value: String ->
|
|
65
|
+
prefs.edit().putString(key, value).apply()
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
AsyncFunction("getLaunchTrackingData") {
|
|
69
|
+
getLaunchTrackingData()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ============================================================================
|
|
73
|
+
// Haptics
|
|
74
|
+
// ============================================================================
|
|
75
|
+
|
|
76
|
+
AsyncFunction("impactAsync") { style: String ->
|
|
77
|
+
performImpactHaptic(style)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
AsyncFunction("notificationAsync") { type: String ->
|
|
81
|
+
performNotificationHaptic(type)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
AsyncFunction("selectionAsync") {
|
|
85
|
+
performSelectionHaptic()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================================
|
|
89
|
+
// Store Review
|
|
90
|
+
// ============================================================================
|
|
91
|
+
|
|
92
|
+
AsyncFunction("requestReview") { promise: Promise ->
|
|
93
|
+
requestStoreReview(promise)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
AsyncFunction("isReviewAvailable") {
|
|
97
|
+
true // Google Play In-App Review is generally available
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
AsyncFunction("getStoreUrl") {
|
|
101
|
+
"https://play.google.com/store/apps/details?id=${context.packageName}"
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ============================================================================
|
|
105
|
+
// Notifications
|
|
106
|
+
// ============================================================================
|
|
107
|
+
|
|
108
|
+
AsyncFunction("requestNotificationPermissions") { options: Map<String, Any>? ->
|
|
109
|
+
requestNotificationPermissions(options)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
AsyncFunction("getNotificationPermissions") {
|
|
113
|
+
getNotificationPermissions()
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Device Info Collection
|
|
118
|
+
private fun collectDeviceInfo(): Map<String, Any?> {
|
|
119
|
+
val userId = getOrCreateUserId()
|
|
120
|
+
val launchData = getLaunchTrackingData()
|
|
121
|
+
val displayMetrics = getDisplayMetrics()
|
|
122
|
+
val locale = Locale.getDefault()
|
|
123
|
+
val timezone = TimeZone.getDefault()
|
|
124
|
+
val packageInfo = try {
|
|
125
|
+
context.packageManager.getPackageInfo(context.packageName, 0)
|
|
126
|
+
} catch (e: Exception) {
|
|
127
|
+
null
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return mapOf(
|
|
131
|
+
"appUserId" to userId,
|
|
132
|
+
"vendorId" to getAndroidId(),
|
|
133
|
+
"appSessionId" to UUID.randomUUID().toString().lowercase(),
|
|
134
|
+
"installDate" to launchData["installDate"],
|
|
135
|
+
"isFirstLaunch" to launchData["isFirstLaunch"],
|
|
136
|
+
"launchCount" to launchData["launchCount"],
|
|
137
|
+
"lastLaunchAt" to launchData["lastLaunchAt"],
|
|
138
|
+
"bundleId" to context.packageName,
|
|
139
|
+
"appName" to getAppName(),
|
|
140
|
+
"appVersion" to packageInfo?.versionName,
|
|
141
|
+
"buildNumber" to getBuildNumber(packageInfo),
|
|
142
|
+
"platform" to "Android",
|
|
143
|
+
"platformVersion" to Build.VERSION.RELEASE,
|
|
144
|
+
"deviceModel" to "${Build.MANUFACTURER} ${Build.MODEL}",
|
|
145
|
+
"deviceName" to Build.MODEL,
|
|
146
|
+
"isSimulator" to isEmulator(),
|
|
147
|
+
"deviceLanguageCode" to locale.language,
|
|
148
|
+
"deviceLocale" to locale.toLanguageTag(),
|
|
149
|
+
"regionCode" to locale.country,
|
|
150
|
+
"preferredLanguage" to locale.toLanguageTag(),
|
|
151
|
+
"preferredLanguages" to listOf(locale.toLanguageTag()),
|
|
152
|
+
"deviceCurrencyCode" to try { java.util.Currency.getInstance(locale).currencyCode } catch (e: Exception) { null },
|
|
153
|
+
"deviceCurrencySymbol" to try { java.util.Currency.getInstance(locale).symbol } catch (e: Exception) { null },
|
|
154
|
+
"timezoneIdentifier" to timezone.id,
|
|
155
|
+
"timezoneOffsetSeconds" to timezone.rawOffset / 1000,
|
|
156
|
+
"interfaceStyle" to getInterfaceStyle(),
|
|
157
|
+
"screenWidth" to (displayMetrics.widthPixels / displayMetrics.density),
|
|
158
|
+
"screenHeight" to (displayMetrics.heightPixels / displayMetrics.density),
|
|
159
|
+
"screenScale" to displayMetrics.density,
|
|
160
|
+
"isLowPowerMode" to isLowPowerMode(),
|
|
161
|
+
"totalMemoryBytes" to getTotalMemory(),
|
|
162
|
+
"collectedAt" to getIso8601Timestamp()
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
private fun getOrCreateUserId(): String {
|
|
167
|
+
val existingId = prefs.getString(USER_ID_KEY, null)
|
|
168
|
+
if (!existingId.isNullOrEmpty()) {
|
|
169
|
+
return existingId
|
|
170
|
+
}
|
|
171
|
+
val newId = UUID.randomUUID().toString().lowercase()
|
|
172
|
+
prefs.edit().putString(USER_ID_KEY, newId).apply()
|
|
173
|
+
return newId
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
private fun getLaunchTrackingData(): Map<String, Any?> {
|
|
177
|
+
val now = getIso8601Timestamp()
|
|
178
|
+
val existingInstallDate = prefs.getString(INSTALL_DATE_KEY, null)
|
|
179
|
+
val isFirstLaunch = existingInstallDate == null
|
|
180
|
+
val installDate = existingInstallDate ?: now
|
|
181
|
+
val lastLaunchAt = prefs.getString(LAST_LAUNCH_KEY, null)
|
|
182
|
+
val launchCount = prefs.getInt(LAUNCH_COUNT_KEY, 0) + 1
|
|
183
|
+
|
|
184
|
+
prefs.edit().apply {
|
|
185
|
+
if (isFirstLaunch) {
|
|
186
|
+
putString(INSTALL_DATE_KEY, installDate)
|
|
187
|
+
}
|
|
188
|
+
putInt(LAUNCH_COUNT_KEY, launchCount)
|
|
189
|
+
putString(LAST_LAUNCH_KEY, now)
|
|
190
|
+
apply()
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return mapOf(
|
|
194
|
+
"installDate" to installDate,
|
|
195
|
+
"isFirstLaunch" to isFirstLaunch,
|
|
196
|
+
"launchCount" to launchCount,
|
|
197
|
+
"lastLaunchAt" to lastLaunchAt
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Haptics
|
|
202
|
+
private fun performImpactHaptic(style: String) {
|
|
203
|
+
val vibrator = getVibrator() ?: return
|
|
204
|
+
|
|
205
|
+
val duration = when (style.lowercase()) {
|
|
206
|
+
"light" -> 10L
|
|
207
|
+
"medium" -> 20L
|
|
208
|
+
"heavy" -> 30L
|
|
209
|
+
"rigid" -> 15L
|
|
210
|
+
"soft" -> 25L
|
|
211
|
+
else -> 20L
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
215
|
+
val amplitude = when (style.lowercase()) {
|
|
216
|
+
"light" -> 50
|
|
217
|
+
"medium" -> 128
|
|
218
|
+
"heavy" -> 255
|
|
219
|
+
"rigid" -> 200
|
|
220
|
+
"soft" -> 80
|
|
221
|
+
else -> 128
|
|
222
|
+
}
|
|
223
|
+
vibrator.vibrate(VibrationEffect.createOneShot(duration, amplitude))
|
|
224
|
+
} else {
|
|
225
|
+
@Suppress("DEPRECATION")
|
|
226
|
+
vibrator.vibrate(duration)
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
private fun performNotificationHaptic(type: String) {
|
|
231
|
+
val vibrator = getVibrator() ?: return
|
|
232
|
+
|
|
233
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
234
|
+
val effect = when (type.lowercase()) {
|
|
235
|
+
"success" -> VibrationEffect.createWaveform(longArrayOf(0, 50, 50, 50), -1)
|
|
236
|
+
"warning" -> VibrationEffect.createWaveform(longArrayOf(0, 100, 50, 100), -1)
|
|
237
|
+
"error" -> VibrationEffect.createWaveform(longArrayOf(0, 50, 30, 50, 30, 100), -1)
|
|
238
|
+
else -> VibrationEffect.createOneShot(50, VibrationEffect.DEFAULT_AMPLITUDE)
|
|
239
|
+
}
|
|
240
|
+
vibrator.vibrate(effect)
|
|
241
|
+
} else {
|
|
242
|
+
@Suppress("DEPRECATION")
|
|
243
|
+
vibrator.vibrate(50)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private fun performSelectionHaptic() {
|
|
248
|
+
val vibrator = getVibrator() ?: return
|
|
249
|
+
|
|
250
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
251
|
+
vibrator.vibrate(VibrationEffect.createOneShot(10, 50))
|
|
252
|
+
} else {
|
|
253
|
+
@Suppress("DEPRECATION")
|
|
254
|
+
vibrator.vibrate(10)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private fun getVibrator(): Vibrator? {
|
|
259
|
+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
260
|
+
val vibratorManager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as? VibratorManager
|
|
261
|
+
vibratorManager?.defaultVibrator
|
|
262
|
+
} else {
|
|
263
|
+
@Suppress("DEPRECATION")
|
|
264
|
+
context.getSystemService(Context.VIBRATOR_SERVICE) as? Vibrator
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Store Review
|
|
269
|
+
private fun requestStoreReview(promise: Promise) {
|
|
270
|
+
try {
|
|
271
|
+
val activity = appContext.currentActivity
|
|
272
|
+
if (activity == null) {
|
|
273
|
+
promise.resolve(false)
|
|
274
|
+
return
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
val reviewManager = ReviewManagerFactory.create(context)
|
|
278
|
+
val requestFlow = reviewManager.requestReviewFlow()
|
|
279
|
+
|
|
280
|
+
requestFlow.addOnCompleteListener { task ->
|
|
281
|
+
if (task.isSuccessful) {
|
|
282
|
+
val reviewInfo = task.result
|
|
283
|
+
val flow = reviewManager.launchReviewFlow(activity, reviewInfo)
|
|
284
|
+
flow.addOnCompleteListener {
|
|
285
|
+
promise.resolve(true)
|
|
286
|
+
}
|
|
287
|
+
} else {
|
|
288
|
+
promise.resolve(false)
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
} catch (e: Exception) {
|
|
292
|
+
promise.resolve(false)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Notifications
|
|
297
|
+
private fun requestNotificationPermissions(options: Map<String, Any>?): Map<String, Any> {
|
|
298
|
+
// Create notification channel for Android 8+
|
|
299
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
300
|
+
val androidOptions = options?.get("android") as? Map<*, *>
|
|
301
|
+
val channelId = androidOptions?.get("channelId") as? String ?: "default"
|
|
302
|
+
val channelName = androidOptions?.get("name") as? String ?: "Default"
|
|
303
|
+
val importance = when ((androidOptions?.get("importance") as? String)?.uppercase()) {
|
|
304
|
+
"MAX" -> NotificationManager.IMPORTANCE_HIGH
|
|
305
|
+
"HIGH" -> NotificationManager.IMPORTANCE_HIGH
|
|
306
|
+
"DEFAULT" -> NotificationManager.IMPORTANCE_DEFAULT
|
|
307
|
+
"LOW" -> NotificationManager.IMPORTANCE_LOW
|
|
308
|
+
"MIN" -> NotificationManager.IMPORTANCE_MIN
|
|
309
|
+
else -> NotificationManager.IMPORTANCE_HIGH
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
val channel = NotificationChannel(channelId, channelName, importance)
|
|
313
|
+
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
314
|
+
notificationManager.createNotificationChannel(channel)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// For Android 13+, check POST_NOTIFICATIONS permission
|
|
318
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
319
|
+
val granted = ContextCompat.checkSelfPermission(
|
|
320
|
+
context,
|
|
321
|
+
Manifest.permission.POST_NOTIFICATIONS
|
|
322
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
323
|
+
|
|
324
|
+
if (!granted) {
|
|
325
|
+
// Request permission - this will be handled by the app
|
|
326
|
+
appContext.currentActivity?.let { activity ->
|
|
327
|
+
ActivityCompat.requestPermissions(
|
|
328
|
+
activity,
|
|
329
|
+
arrayOf(Manifest.permission.POST_NOTIFICATIONS),
|
|
330
|
+
1001
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
return mapOf(
|
|
336
|
+
"granted" to granted,
|
|
337
|
+
"status" to if (granted) "granted" else "denied",
|
|
338
|
+
"canAskAgain" to true
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// For older Android versions, notifications are allowed by default
|
|
343
|
+
return mapOf(
|
|
344
|
+
"granted" to true,
|
|
345
|
+
"status" to "granted",
|
|
346
|
+
"canAskAgain" to true
|
|
347
|
+
)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private fun getNotificationPermissions(): Map<String, Any> {
|
|
351
|
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
|
352
|
+
val granted = ContextCompat.checkSelfPermission(
|
|
353
|
+
context,
|
|
354
|
+
Manifest.permission.POST_NOTIFICATIONS
|
|
355
|
+
) == PackageManager.PERMISSION_GRANTED
|
|
356
|
+
|
|
357
|
+
return mapOf(
|
|
358
|
+
"granted" to granted,
|
|
359
|
+
"status" to if (granted) "granted" else "denied",
|
|
360
|
+
"canAskAgain" to true
|
|
361
|
+
)
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
return mapOf(
|
|
365
|
+
"granted" to true,
|
|
366
|
+
"status" to "granted",
|
|
367
|
+
"canAskAgain" to true
|
|
368
|
+
)
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Helper Functions
|
|
372
|
+
private fun getAndroidId(): String? {
|
|
373
|
+
return try {
|
|
374
|
+
Settings.Secure.getString(context.contentResolver, Settings.Secure.ANDROID_ID)
|
|
375
|
+
} catch (e: Exception) {
|
|
376
|
+
null
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private fun getAppName(): String? {
|
|
381
|
+
return try {
|
|
382
|
+
val applicationInfo = context.applicationInfo
|
|
383
|
+
val stringId = applicationInfo.labelRes
|
|
384
|
+
if (stringId == 0) applicationInfo.nonLocalizedLabel?.toString()
|
|
385
|
+
else context.getString(stringId)
|
|
386
|
+
} catch (e: Exception) {
|
|
387
|
+
null
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
private fun getBuildNumber(packageInfo: android.content.pm.PackageInfo?): String? {
|
|
392
|
+
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
|
393
|
+
packageInfo?.longVersionCode?.toString()
|
|
394
|
+
} else {
|
|
395
|
+
@Suppress("DEPRECATION")
|
|
396
|
+
packageInfo?.versionCode?.toString()
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private fun isEmulator(): Boolean {
|
|
401
|
+
return (Build.FINGERPRINT.startsWith("generic")
|
|
402
|
+
|| Build.FINGERPRINT.startsWith("unknown")
|
|
403
|
+
|| Build.MODEL.contains("google_sdk")
|
|
404
|
+
|| Build.MODEL.contains("Emulator")
|
|
405
|
+
|| Build.MODEL.contains("Android SDK built for x86")
|
|
406
|
+
|| Build.MANUFACTURER.contains("Genymotion")
|
|
407
|
+
|| (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic"))
|
|
408
|
+
|| Build.PRODUCT == "google_sdk")
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
private fun getDisplayMetrics(): DisplayMetrics {
|
|
412
|
+
val windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
|
413
|
+
val displayMetrics = DisplayMetrics()
|
|
414
|
+
@Suppress("DEPRECATION")
|
|
415
|
+
windowManager.defaultDisplay.getMetrics(displayMetrics)
|
|
416
|
+
return displayMetrics
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
private fun getInterfaceStyle(): String {
|
|
420
|
+
val nightModeFlags = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
|
|
421
|
+
return when (nightModeFlags) {
|
|
422
|
+
Configuration.UI_MODE_NIGHT_YES -> "dark"
|
|
423
|
+
Configuration.UI_MODE_NIGHT_NO -> "light"
|
|
424
|
+
else -> "unspecified"
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
private fun isLowPowerMode(): Boolean {
|
|
429
|
+
val powerManager = context.getSystemService(Context.POWER_SERVICE) as? PowerManager
|
|
430
|
+
return powerManager?.isPowerSaveMode ?: false
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
private fun getTotalMemory(): Long {
|
|
434
|
+
val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
|
|
435
|
+
val memoryInfo = ActivityManager.MemoryInfo()
|
|
436
|
+
activityManager.getMemoryInfo(memoryInfo)
|
|
437
|
+
return memoryInfo.totalMem
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
private fun getIso8601Timestamp(): String {
|
|
441
|
+
val sdf = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US)
|
|
442
|
+
sdf.timeZone = TimeZone.getTimeZone("UTC")
|
|
443
|
+
return sdf.format(Date())
|
|
444
|
+
}
|
|
445
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RampKit Device Info Collector
|
|
3
|
+
* Collects device information using native modules for the /app-users endpoint
|
|
4
|
+
*/
|
|
5
|
+
import { DeviceInfo } from "./types";
|
|
6
|
+
/**
|
|
7
|
+
* Get session start time
|
|
8
|
+
*/
|
|
9
|
+
export declare function getSessionStartTime(): Date | null;
|
|
10
|
+
/**
|
|
11
|
+
* Get the current session duration in seconds
|
|
12
|
+
*/
|
|
13
|
+
export declare function getSessionDurationSeconds(): number;
|
|
14
|
+
/**
|
|
15
|
+
* Collect all device information using native module
|
|
16
|
+
*/
|
|
17
|
+
export declare function collectDeviceInfo(): Promise<DeviceInfo>;
|
|
18
|
+
/**
|
|
19
|
+
* Reset session (call when app is fully restarted)
|
|
20
|
+
*/
|
|
21
|
+
export declare function resetSession(): void;
|