1
+ package com.iterable.iterableapi
2
+
3
+ import android.content.Context
4
+ import android.content.pm.PackageManager
5
+
6
+ object IterableMobileFrameworkDetector {
7
+ private const val TAG = " FrameworkDetector"
8
+ private lateinit var context: Context
9
+
10
+ @Volatile
11
+ private var cachedFrameworkType: IterableAPIMobileFrameworkType ? = null
12
+
13
+ private var hasClass: (String ) -> Boolean = { className ->
14
+ try {
15
+ Class .forName(className)
16
+ true
17
+ } catch (e: ClassNotFoundException ) {
18
+ false
19
+ }
20
+ }
21
+
22
+ fun initialize (context : Context ) {
23
+ if (context.applicationContext != null ) {
24
+ this .context = context.applicationContext
25
+ } else {
26
+ this .context = context
27
+ }
28
+ if (cachedFrameworkType == null ) {
29
+ cachedFrameworkType = detectFrameworkInternal(context)
30
+ }
31
+ }
32
+
33
+ @JvmStatic
34
+ fun detectFramework (context : Context ): IterableAPIMobileFrameworkType {
35
+ return cachedFrameworkType ? : synchronized(this ) {
36
+ cachedFrameworkType ? : detectFrameworkInternal(context).also {
37
+ cachedFrameworkType = it
38
+ }
39
+ }
40
+ }
41
+
42
+ fun frameworkType (): IterableAPIMobileFrameworkType {
43
+ return cachedFrameworkType ? : detectFramework(context)
44
+ }
45
+
46
+ private fun detectFrameworkInternal (context : Context ): IterableAPIMobileFrameworkType {
47
+ val hasFlutter = hasFrameworkClasses(FrameworkClasses .flutter)
48
+ val hasReactNative = hasFrameworkClasses(FrameworkClasses .reactNative)
49
+
50
+ return when {
51
+ hasFlutter && hasReactNative -> {
52
+ IterableLogger .d(TAG , " Both Flutter and React Native frameworks detected. This is unexpected." )
53
+ when {
54
+ context.packageName.endsWith(" .flutter" ) -> IterableAPIMobileFrameworkType .FLUTTER
55
+ hasManifestMetadata(context, ManifestMetadata .flutter) -> IterableAPIMobileFrameworkType .FLUTTER
56
+ hasManifestMetadata(context, ManifestMetadata .reactNative) -> IterableAPIMobileFrameworkType .REACT_NATIVE
57
+ else -> IterableAPIMobileFrameworkType .REACT_NATIVE
58
+ }
59
+ }
60
+ hasFlutter -> IterableAPIMobileFrameworkType .FLUTTER
61
+ hasReactNative -> IterableAPIMobileFrameworkType .REACT_NATIVE
62
+ else -> {
63
+ when {
64
+ hasManifestMetadata(context, ManifestMetadata .flutter) -> IterableAPIMobileFrameworkType .FLUTTER
65
+ hasManifestMetadata(context, ManifestMetadata .reactNative) -> IterableAPIMobileFrameworkType .REACT_NATIVE
66
+ else -> IterableAPIMobileFrameworkType .NATIVE
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ private object FrameworkClasses {
73
+ val flutter = listOf (
74
+ " io.flutter.embedding.engine.FlutterEngine" ,
75
+ " io.flutter.plugin.common.MethodChannel" ,
76
+ " io.flutter.embedding.android.FlutterActivity" ,
77
+ " io.flutter.embedding.android.FlutterFragment"
78
+ )
79
+
80
+ val reactNative = listOf (
81
+ " com.facebook.react.ReactRootView" ,
82
+ " com.facebook.react.bridge.ReactApplicationContext" ,
83
+ " com.facebook.react.ReactActivity" ,
84
+ " com.facebook.react.ReactApplication" ,
85
+ " com.facebook.react.bridge.ReactContext"
86
+ )
87
+ }
88
+
89
+ private object ManifestMetadata {
90
+ val flutter = listOf (
91
+ " flutterEmbedding" ,
92
+ " io.flutter.embedding.android.NormalTheme" ,
93
+ " io.flutter.embedding.android.SplashScreenDrawable"
94
+ )
95
+
96
+ val reactNative = listOf (
97
+ " react_native_version" ,
98
+ " expo.modules.updates.ENABLED" ,
99
+ " com.facebook.react.selected.ReactActivity"
100
+ )
101
+ }
102
+
103
+ private fun hasFrameworkClasses (classNames : List <String >): Boolean {
104
+ return classNames.any { hasClass(it) }
105
+ }
106
+
107
+ private fun hasManifestMetadata (context : Context , metadataKeys : List <String >): Boolean {
108
+ return try {
109
+ val packageInfo = context.packageManager.getPackageInfo(
110
+ context.packageName,
111
+ PackageManager .GET_META_DATA
112
+ )
113
+ val metadata = packageInfo.applicationInfo.metaData
114
+ metadataKeys.any { key -> metadata?.containsKey(key) == true }
115
+ } catch (e: Exception ) {
116
+ IterableLogger .e(TAG , " Error checking manifest metadata: ${e.message} " )
117
+ false
118
+ }
119
+ }
120
+ }
0 commit comments