@octopus-community/react-native 1.0.8 → 1.9.1

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.
Files changed (108) hide show
  1. package/OctopusReactNativeSdk.podspec +1 -1
  2. package/README.md +40 -35
  3. package/android/build.gradle +2 -0
  4. package/android/gradle.properties +2 -2
  5. package/android/src/main/AndroidManifest.xml +2 -1
  6. package/android/src/main/AndroidManifestNew.xml +2 -1
  7. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusActivity.kt +56 -0
  8. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusContent.kt +396 -0
  9. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusEventEmitter.kt +22 -0
  10. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusEventSerializer.kt +339 -0
  11. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactModule.kt +326 -0
  12. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/{OctopusReactNativeSdkPackage.kt → OctopusReactPackage.kt} +3 -3
  13. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSDKInitializer.kt +22 -9
  14. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusSSOAuthenticator.kt +5 -15
  15. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIController.kt +17 -2
  16. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIViewManager.kt +63 -0
  17. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/ProfileFieldMapper.kt +2 -2
  18. package/ios/OctopusEventManager.swift +27 -0
  19. package/ios/OctopusEventSerializer.swift +271 -0
  20. package/ios/OctopusReactNativeSdk.mm +26 -1
  21. package/ios/OctopusReactNativeSdk.swift +216 -2
  22. package/ios/OctopusSSOAuthenticator.swift +1 -5
  23. package/ios/OctopusUIManager.swift +83 -0
  24. package/ios/OctopusUIViewManager.m +7 -0
  25. package/ios/OctopusUIViewManager.swift +37 -0
  26. package/lib/module/OctopusUIView.js +39 -0
  27. package/lib/module/OctopusUIView.js.map +1 -0
  28. package/lib/module/addHasAccessToCommunityListener.js +33 -0
  29. package/lib/module/addHasAccessToCommunityListener.js.map +1 -0
  30. package/lib/module/addNavigateToUrlListener.js +41 -0
  31. package/lib/module/addNavigateToUrlListener.js.map +1 -0
  32. package/lib/module/addNotSeenNotificationsCountListener.js +30 -0
  33. package/lib/module/addNotSeenNotificationsCountListener.js.map +1 -0
  34. package/lib/module/addSDKEventListener.js +48 -0
  35. package/lib/module/addSDKEventListener.js.map +1 -0
  36. package/lib/module/connectUser.js +24 -3
  37. package/lib/module/connectUser.js.map +1 -1
  38. package/lib/module/index.js +12 -0
  39. package/lib/module/index.js.map +1 -1
  40. package/lib/module/initialize.js +9 -12
  41. package/lib/module/initialize.js.map +1 -1
  42. package/lib/module/openUI.js +23 -2
  43. package/lib/module/openUI.js.map +1 -1
  44. package/lib/module/overrideCommunityAccess.js +36 -0
  45. package/lib/module/overrideCommunityAccess.js.map +1 -0
  46. package/lib/module/overrideDefaultLocale.js +75 -0
  47. package/lib/module/overrideDefaultLocale.js.map +1 -0
  48. package/lib/module/trackCommunityAccess.js +33 -0
  49. package/lib/module/trackCommunityAccess.js.map +1 -0
  50. package/lib/module/trackCustomEvent.js +36 -0
  51. package/lib/module/trackCustomEvent.js.map +1 -0
  52. package/lib/module/types/sdkEvents.js +2 -0
  53. package/lib/module/types/sdkEvents.js.map +1 -0
  54. package/lib/module/types/urlOpeningStrategy.js +23 -0
  55. package/lib/module/types/urlOpeningStrategy.js.map +1 -0
  56. package/lib/module/updateNotSeenNotificationsCount.js +33 -0
  57. package/lib/module/updateNotSeenNotificationsCount.js.map +1 -0
  58. package/lib/typescript/src/OctopusUIView.d.ts +32 -0
  59. package/lib/typescript/src/OctopusUIView.d.ts.map +1 -0
  60. package/lib/typescript/src/addHasAccessToCommunityListener.d.ts +27 -0
  61. package/lib/typescript/src/addHasAccessToCommunityListener.d.ts.map +1 -0
  62. package/lib/typescript/src/addNavigateToUrlListener.d.ts +31 -0
  63. package/lib/typescript/src/addNavigateToUrlListener.d.ts.map +1 -0
  64. package/lib/typescript/src/addNotSeenNotificationsCountListener.d.ts +24 -0
  65. package/lib/typescript/src/addNotSeenNotificationsCountListener.d.ts.map +1 -0
  66. package/lib/typescript/src/addSDKEventListener.d.ts +43 -0
  67. package/lib/typescript/src/addSDKEventListener.d.ts.map +1 -0
  68. package/lib/typescript/src/connectUser.d.ts +24 -8
  69. package/lib/typescript/src/connectUser.d.ts.map +1 -1
  70. package/lib/typescript/src/index.d.ts +13 -0
  71. package/lib/typescript/src/index.d.ts.map +1 -1
  72. package/lib/typescript/src/initialize.d.ts +9 -12
  73. package/lib/typescript/src/initialize.d.ts.map +1 -1
  74. package/lib/typescript/src/openUI.d.ts +28 -1
  75. package/lib/typescript/src/openUI.d.ts.map +1 -1
  76. package/lib/typescript/src/overrideCommunityAccess.d.ts +30 -0
  77. package/lib/typescript/src/overrideCommunityAccess.d.ts.map +1 -0
  78. package/lib/typescript/src/overrideDefaultLocale.d.ts +37 -0
  79. package/lib/typescript/src/overrideDefaultLocale.d.ts.map +1 -0
  80. package/lib/typescript/src/trackCommunityAccess.d.ts +27 -0
  81. package/lib/typescript/src/trackCommunityAccess.d.ts.map +1 -0
  82. package/lib/typescript/src/trackCustomEvent.d.ts +30 -0
  83. package/lib/typescript/src/trackCustomEvent.d.ts.map +1 -0
  84. package/lib/typescript/src/types/sdkEvents.d.ts +222 -0
  85. package/lib/typescript/src/types/sdkEvents.d.ts.map +1 -0
  86. package/lib/typescript/src/types/urlOpeningStrategy.d.ts +20 -0
  87. package/lib/typescript/src/types/urlOpeningStrategy.d.ts.map +1 -0
  88. package/lib/typescript/src/updateNotSeenNotificationsCount.d.ts +27 -0
  89. package/lib/typescript/src/updateNotSeenNotificationsCount.d.ts.map +1 -0
  90. package/package.json +2 -1
  91. package/src/OctopusUIView.tsx +57 -0
  92. package/src/addHasAccessToCommunityListener.ts +38 -0
  93. package/src/addNavigateToUrlListener.ts +54 -0
  94. package/src/addNotSeenNotificationsCountListener.ts +35 -0
  95. package/src/addSDKEventListener.ts +49 -0
  96. package/src/connectUser.ts +24 -8
  97. package/src/index.ts +13 -0
  98. package/src/initialize.ts +9 -12
  99. package/src/openUI.ts +32 -2
  100. package/src/overrideCommunityAccess.ts +33 -0
  101. package/src/overrideDefaultLocale.ts +88 -0
  102. package/src/trackCommunityAccess.ts +30 -0
  103. package/src/trackCustomEvent.ts +36 -0
  104. package/src/types/sdkEvents.ts +315 -0
  105. package/src/types/urlOpeningStrategy.ts +20 -0
  106. package/src/updateNotSeenNotificationsCount.ts +30 -0
  107. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusReactNativeSdkModule.kt +0 -155
  108. package/android/src/main/java/com/octopuscommunity/octopusreactnativesdk/OctopusUIActivity.kt +0 -434
@@ -1,434 +0,0 @@
1
- package com.octopuscommunity.octopusreactnativesdk
2
-
3
- import android.content.BroadcastReceiver
4
- import android.content.Context
5
- import android.content.Intent
6
- import android.content.IntentFilter
7
- import android.content.res.Configuration
8
- import android.graphics.BitmapFactory
9
- import android.os.Build
10
- import android.os.Bundle
11
- import android.util.Log
12
- import androidx.activity.ComponentActivity
13
- import androidx.activity.compose.setContent
14
- import androidx.compose.foundation.layout.Box
15
- import androidx.compose.foundation.layout.PaddingValues
16
- import androidx.compose.foundation.layout.fillMaxSize
17
- import androidx.compose.foundation.layout.padding
18
- import androidx.compose.runtime.Composable
19
- import androidx.compose.runtime.LaunchedEffect
20
- import androidx.compose.runtime.getValue
21
- import androidx.compose.runtime.mutableStateOf
22
- import androidx.compose.runtime.remember
23
- import androidx.compose.runtime.setValue
24
- import androidx.compose.ui.Modifier
25
- import androidx.compose.ui.unit.Dp
26
- import androidx.compose.ui.graphics.Color
27
- import androidx.compose.ui.graphics.asImageBitmap
28
- import androidx.compose.ui.graphics.painter.BitmapPainter
29
- import androidx.compose.ui.graphics.painter.Painter
30
- import androidx.compose.ui.platform.LocalContext
31
- import androidx.navigation.compose.NavHost
32
- import androidx.navigation.compose.composable
33
- import androidx.navigation.compose.rememberNavController
34
- import com.octopuscommunity.sdk.ui.OctopusDrawablesDefaults
35
- import com.octopuscommunity.sdk.ui.OctopusTheme
36
- import com.octopuscommunity.sdk.ui.OctopusTypographyDefaults
37
- import com.octopuscommunity.sdk.ui.OctopusTypography
38
- import com.octopuscommunity.sdk.ui.home.OctopusHomeDefaults
39
- import com.octopuscommunity.sdk.ui.components.OctopusNavigationHandler
40
- import androidx.compose.ui.text.TextStyle
41
- import androidx.compose.ui.text.font.FontFamily
42
- import androidx.compose.ui.text.font.FontWeight
43
- import androidx.compose.ui.unit.sp
44
- import androidx.compose.ui.unit.dp
45
- import com.octopuscommunity.sdk.ui.home.OctopusHomeScreen
46
- import com.octopuscommunity.sdk.ui.octopusComposables
47
- import com.octopuscommunity.sdk.ui.octopusDarkColorScheme
48
- import com.octopuscommunity.sdk.ui.octopusLightColorScheme
49
- import kotlinx.coroutines.Dispatchers
50
- import kotlinx.coroutines.withContext
51
- import java.net.URL
52
- import java.util.concurrent.ConcurrentHashMap
53
-
54
- // Cache for loaded images to avoid reloading on recomposition
55
- private val imageCache = ConcurrentHashMap<String, Painter?>()
56
- // Cache for drawable resources list to avoid repeated reflection
57
- private var cachedDrawableResources: List<String>? = null
58
-
59
- class OctopusUIActivity : ComponentActivity() {
60
- private val closeUIReceiver = object : BroadcastReceiver() {
61
- override fun onReceive(context: Context?, intent: Intent?) {
62
- finish()
63
- }
64
- }
65
-
66
- override fun onCreate(savedInstanceState: Bundle?) {
67
- super.onCreate(savedInstanceState)
68
- registerCloseUIReceiver()
69
- setContent {
70
- OctopusUI(onBack = { finish() })
71
- }
72
- }
73
-
74
- override fun onDestroy() {
75
- super.onDestroy()
76
- unregisterReceiver(closeUIReceiver)
77
- }
78
-
79
- private fun registerCloseUIReceiver() {
80
- val intentFilter = IntentFilter(OctopusUIController.CLOSE_UI_ACTION)
81
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
82
- registerReceiver(closeUIReceiver, intentFilter, Context.RECEIVER_NOT_EXPORTED)
83
- } else {
84
- @Suppress("UnspecifiedRegisterReceiverFlag")
85
- registerReceiver(closeUIReceiver, intentFilter)
86
- }
87
- }
88
- }
89
-
90
- @Composable
91
- private fun OctopusUI(onBack: () -> Unit) {
92
- val context = LocalContext.current
93
-
94
- // Get theme config once when UI is created - no need for polling
95
- val themeConfig = OctopusThemeManager.getThemeConfig()
96
-
97
- // Function to detect if system is in dark mode
98
- fun isSystemInDarkTheme(): Boolean {
99
- return (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
100
- }
101
-
102
- // Determine base color scheme based on theme config or system theme
103
- val baseColorScheme = if (themeConfig?.colorScheme == "dark") {
104
- octopusDarkColorScheme()
105
- } else if (themeConfig?.colorScheme == "light") {
106
- octopusLightColorScheme()
107
- } else if (isSystemInDarkTheme()) {
108
- octopusDarkColorScheme()
109
- } else {
110
- octopusLightColorScheme()
111
- }
112
-
113
- // Apply custom colors if theme config is provided
114
- val finalColorScheme = if (themeConfig != null) {
115
- baseColorScheme.copy(
116
- primary = themeConfig.primaryColor?.let {
117
- Color(android.graphics.Color.parseColor(it))
118
- } ?: baseColorScheme.primary,
119
- primaryLow = themeConfig.primaryLowContrastColor?.let {
120
- Color(android.graphics.Color.parseColor(it))
121
- } ?: baseColorScheme.primaryLow,
122
- primaryHigh = themeConfig.primaryHighContrastColor?.let {
123
- Color(android.graphics.Color.parseColor(it))
124
- } ?: baseColorScheme.primaryHigh,
125
- onPrimary = themeConfig.onPrimaryColor?.let {
126
- Color(android.graphics.Color.parseColor(it))
127
- } ?: baseColorScheme.onPrimary
128
- )
129
- } else {
130
- baseColorScheme
131
- }
132
-
133
- // Handle logo loading using built-in Android capabilities
134
- var logoPainter by remember { mutableStateOf<Painter?>(null) }
135
-
136
- LaunchedEffect(themeConfig) {
137
- themeConfig?.logoSource?.let { logoSource ->
138
- val uri = logoSource.getString("uri")
139
- if (uri != null) {
140
- logoPainter = loadImageFromUri(uri, context)
141
- } else {
142
- logoPainter = null
143
- }
144
- } ?: run {
145
- // No theme config or no logo source
146
- logoPainter = null
147
- }
148
- }
149
-
150
- // Create drawables based on theme config
151
- val drawables = if (logoPainter != null) {
152
- OctopusDrawablesDefaults.drawables(logo = logoPainter)
153
- } else {
154
- OctopusDrawablesDefaults.drawables()
155
- }
156
-
157
- // Create typography based on theme config
158
- val typography = createCustomTypography(themeConfig)
159
- val uiConfiguration = OctopusUIConfigurationManager.getUIConfiguration()
160
-
161
- // Apply theme with custom colors, logo, typography, and content padding
162
- OctopusTheme(
163
- colorScheme = finalColorScheme,
164
- drawables = drawables,
165
- typography = typography
166
- ) {
167
- OctopusUIContent(
168
- bottomContentPadding = uiConfiguration?.bottomContentPadding?.toFloat()?.dp,
169
- onBack = onBack
170
- )
171
- }
172
- }
173
-
174
- private fun createCustomTypography(themeConfig: OctopusThemeConfig?): OctopusTypography {
175
- val defaultTypography = OctopusTypographyDefaults.typography()
176
-
177
- if (themeConfig?.fonts == null) {
178
- return defaultTypography
179
- }
180
-
181
- val fontsConfig = themeConfig.fonts!!
182
- val textStyles = fontsConfig.textStyles
183
-
184
- // Create custom typography based on new unified font configuration
185
- if (textStyles != null && textStyles.isNotEmpty()) {
186
- return OctopusTypographyDefaults.typography(
187
- title1 = createTextStyle(textStyles["title1"], defaultTypography.title1),
188
- title2 = createTextStyle(textStyles["title2"], defaultTypography.title2),
189
- body1 = createTextStyle(textStyles["body1"], defaultTypography.body1),
190
- body2 = createTextStyle(textStyles["body2"], defaultTypography.body2),
191
- caption1 = createTextStyle(textStyles["caption1"], defaultTypography.caption1),
192
- caption2 = createTextStyle(textStyles["caption2"], defaultTypography.caption2)
193
- )
194
- }
195
-
196
-
197
- return defaultTypography
198
- }
199
-
200
- private fun createTextStyle(textStyleConfig: OctopusTextStyleConfig?, defaultStyle: TextStyle): TextStyle {
201
- if (textStyleConfig == null) {
202
- return defaultStyle
203
- }
204
-
205
- val fontFamily = when (textStyleConfig.fontType) {
206
- "serif" -> FontFamily.Serif
207
- "monospace" -> FontFamily.Monospace
208
- "default" -> FontFamily.Default
209
- else -> FontFamily.Default
210
- }
211
-
212
- val fontSize = textStyleConfig.fontSize?.let {
213
- // Use points directly as sp (1 point ≈ 1 sp on Android)
214
- it.sp
215
- } ?: defaultStyle.fontSize
216
-
217
- return defaultStyle.copy(
218
- fontFamily = fontFamily,
219
- fontSize = fontSize
220
- )
221
- }
222
-
223
-
224
- @Composable
225
- private fun OctopusUIContent(bottomContentPadding: Dp?, onBack: () -> Unit) {
226
- Box(modifier = Modifier.fillMaxSize()) {
227
- val navController = rememberNavController()
228
-
229
- NavHost(
230
- navController = navController,
231
- startDestination = "OctopusHome"
232
- ) {
233
- composable(route = "OctopusHome") {
234
- OctopusNavigationHandler(
235
- navigateToLogin = {
236
- OctopusEventEmitter.instance?.emitLoginRequired()
237
- },
238
- navigateToProfileEdit = { fieldToEdit ->
239
- OctopusEventEmitter.instance?.emitEditUser(fieldToEdit)
240
- },
241
- navigateToClientObject = {
242
- // TODO : Bridge Post react
243
- }
244
- ) {
245
- OctopusHomeScreen(
246
- navController = navController,
247
- backIcon = true,
248
- floatingActionsPadding = OctopusHomeDefaults.floatingActionsPadding(
249
- bottom = bottomContentPadding ?: 16.dp
250
- ),
251
- onBack = onBack
252
- )
253
- }
254
- }
255
- octopusComposables(
256
- navController = navController,
257
- onNavigateToLogin = {
258
- OctopusEventEmitter.instance?.emitLoginRequired()
259
- },
260
- onNavigateToProfileEdit = { fieldToEdit ->
261
- OctopusEventEmitter.instance?.emitEditUser(fieldToEdit)
262
- }
263
- )
264
- }
265
- }
266
- }
267
-
268
- private suspend fun loadImageFromUri(uri: String, context: Context): Painter? {
269
- // Check cache first
270
- imageCache[uri]?.let { return it }
271
-
272
- return withContext(Dispatchers.IO) {
273
- try {
274
- val result = when {
275
- // Network URLs - load directly
276
- uri.startsWith("http://", ignoreCase = true) ||
277
- uri.startsWith("https://", ignoreCase = true) -> {
278
- loadFromNetwork(uri)
279
- }
280
- // Everything else - try as React Native asset
281
- else -> {
282
- loadReactNativeAsset(uri, context)
283
- }
284
- }
285
-
286
- // Cache result (including null to avoid retrying failed loads)
287
- imageCache[uri] = result
288
- result
289
- } catch (e: Exception) {
290
- Log.w("OctopusUIActivity", "Failed to load image from URI: $uri", e)
291
- imageCache[uri] = null
292
- null
293
- }
294
- }
295
- }
296
-
297
- private fun loadFromNetwork(url: String): Painter? {
298
- return try {
299
- URL(url).openStream().use { inputStream ->
300
- val bitmap = BitmapFactory.decodeStream(inputStream)
301
- bitmap?.let { BitmapPainter(it.asImageBitmap()) }
302
- }
303
- } catch (e: Exception) {
304
- Log.w("OctopusUIActivity", "Failed to load image from network: $url", e)
305
- null
306
- }
307
- }
308
-
309
- private fun loadReactNativeAsset(assetName: String, context: Context): Painter? {
310
- return try {
311
- // First try: Direct drawable resource lookup (most common case)
312
- val drawableResult = loadFromDrawableResources(assetName, context)
313
- if (drawableResult != null) {
314
- return drawableResult
315
- }
316
-
317
- // Second try: Assets folder with strategic path checking
318
- loadFromAssetsFolder(assetName, context)
319
- } catch (e: Exception) {
320
- Log.w("OctopusUIActivity", "Failed to load asset: $assetName", e)
321
- null
322
- }
323
- }
324
-
325
- private fun loadFromAssetsFolder(assetName: String, context: Context): Painter? {
326
- // Check if assetName already has extension
327
- val hasExtension = assetName.contains(".")
328
- val extensions = if (hasExtension) listOf("") else listOf("png", "jpg", "jpeg", "webp")
329
-
330
- val folders = listOf(
331
- "",
332
- "drawable-mdpi",
333
- "drawable-hdpi",
334
- "drawable-xhdpi",
335
- "drawable-xxhdpi",
336
- "drawable-xxxhdpi",
337
- "drawable"
338
- )
339
-
340
- for (folder in folders) {
341
- for (ext in extensions) {
342
- val filename = if (ext.isEmpty()) assetName else "$assetName.$ext"
343
- val path = if (folder.isEmpty()) filename else "$folder/$filename"
344
-
345
- try {
346
- context.assets.open(path).use { inputStream ->
347
- val bitmap = BitmapFactory.decodeStream(inputStream)
348
- if (bitmap != null) {
349
- return BitmapPainter(bitmap.asImageBitmap())
350
- }
351
- }
352
- } catch (e: Exception) {
353
- // Continue to next path
354
- }
355
- }
356
- }
357
-
358
- return null
359
- }
360
-
361
- private fun loadFromDrawableResources(assetName: String, context: Context): Painter? {
362
- return try {
363
- // Try exact match first
364
- val resourceId = context.resources.getIdentifier(
365
- assetName, "drawable", context.packageName
366
- )
367
- if (resourceId != 0) {
368
- return loadBitmapFromResource(context, resourceId)
369
- }
370
-
371
- // Fallback: fuzzy search (only if exact match fails)
372
- val drawableResources = getAllDrawableResources(context)
373
- for (resourceName in drawableResources) {
374
- if (isResourceNameMatch(resourceName, assetName)) {
375
- val resId = context.resources.getIdentifier(
376
- resourceName, "drawable", context.packageName
377
- )
378
- if (resId != 0) {
379
- return loadBitmapFromResource(context, resId)
380
- }
381
- }
382
- }
383
-
384
- null
385
- } catch (e: Exception) {
386
- null
387
- }
388
- }
389
-
390
- private fun loadBitmapFromResource(context: Context, resourceId: Int): Painter? {
391
- return try {
392
- val bitmap = BitmapFactory.decodeResource(context.resources, resourceId)
393
- bitmap?.let { BitmapPainter(it.asImageBitmap()) }
394
- } catch (e: Exception) {
395
- null
396
- }
397
- }
398
-
399
- private fun getAllDrawableResources(context: Context): List<String> {
400
- // Return cached result if available
401
- cachedDrawableResources?.let { return it }
402
-
403
- return try {
404
- val packageName = context.packageName
405
- val drawableResources = mutableListOf<String>()
406
-
407
- // Get all drawable resources by scanning the R.drawable class
408
- val drawableClass = Class.forName("$packageName.R\$drawable")
409
- val fields = drawableClass.declaredFields
410
-
411
- for (field in fields) {
412
- if (field.type == Int::class.javaPrimitiveType) {
413
- drawableResources.add(field.name)
414
- }
415
- }
416
-
417
- // Cache for future use
418
- cachedDrawableResources = drawableResources
419
- drawableResources
420
- } catch (e: Exception) {
421
- Log.w("OctopusUIActivity", "Failed to get drawable resources", e)
422
- emptyList()
423
- }
424
- }
425
-
426
-
427
- private fun isResourceNameMatch(resourceName: String, assetName: String): Boolean {
428
- val normalizedResourceName = resourceName.lowercase()
429
- val normalizedAssetName = assetName.lowercase()
430
-
431
- // Only check if one contains the other - removed dangerous generic patterns
432
- return normalizedResourceName.contains(normalizedAssetName) ||
433
- normalizedAssetName.contains(normalizedResourceName)
434
- }