expo-callkit-telecom 0.1.0

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 (93) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +197 -0
  3. package/android/build.gradle +32 -0
  4. package/android/src/main/AndroidManifest.xml +33 -0
  5. package/android/src/main/java/expo/modules/callkittelecom/ExpoCallKitTelecomModule.kt +384 -0
  6. package/android/src/main/java/expo/modules/callkittelecom/IncomingCallActivity.kt +275 -0
  7. package/android/src/main/java/expo/modules/callkittelecom/events/CallEventEmitter.kt +151 -0
  8. package/android/src/main/java/expo/modules/callkittelecom/events/CallEvents.kt +59 -0
  9. package/android/src/main/java/expo/modules/callkittelecom/managers/CallAudioManager.kt +361 -0
  10. package/android/src/main/java/expo/modules/callkittelecom/managers/CallManager.kt +891 -0
  11. package/android/src/main/java/expo/modules/callkittelecom/managers/CallNotificationManager.kt +445 -0
  12. package/android/src/main/java/expo/modules/callkittelecom/managers/CaptureSessionManager.kt +27 -0
  13. package/android/src/main/java/expo/modules/callkittelecom/managers/DialtonePlayer.kt +171 -0
  14. package/android/src/main/java/expo/modules/callkittelecom/managers/FulfillRequestManager.kt +150 -0
  15. package/android/src/main/java/expo/modules/callkittelecom/managers/VoIPPushManager.kt +54 -0
  16. package/android/src/main/java/expo/modules/callkittelecom/models/CallModels.kt +269 -0
  17. package/android/src/main/java/expo/modules/callkittelecom/services/CallNotificationReceiver.kt +54 -0
  18. package/android/src/main/java/expo/modules/callkittelecom/services/ExpoCallKitTelecomMessagingService.kt +161 -0
  19. package/android/src/main/java/expo/modules/callkittelecom/store/CallStore.kt +181 -0
  20. package/android/src/main/java/expo/modules/callkittelecom/utils/CallKitTelecomLog.kt +52 -0
  21. package/android/src/main/java/expo/modules/callkittelecom/utils/PermissionUtils.kt +28 -0
  22. package/android/src/main/res/drawable/expo_callkit_telecom_bg_answer.xml +9 -0
  23. package/android/src/main/res/drawable/expo_callkit_telecom_bg_avatar.xml +5 -0
  24. package/android/src/main/res/drawable/expo_callkit_telecom_bg_decline.xml +9 -0
  25. package/android/src/main/res/drawable/expo_callkit_telecom_ic_answer.xml +9 -0
  26. package/android/src/main/res/drawable/expo_callkit_telecom_ic_decline.xml +9 -0
  27. package/android/src/main/res/drawable/expo_callkit_telecom_ic_videocam.xml +9 -0
  28. package/android/src/main/res/layout/activity_incoming_call.xml +169 -0
  29. package/app.json +8 -0
  30. package/app.plugin.js +1 -0
  31. package/build/Calls.d.ts +577 -0
  32. package/build/Calls.d.ts.map +1 -0
  33. package/build/Calls.js +715 -0
  34. package/build/Calls.js.map +1 -0
  35. package/build/Calls.types.d.ts +203 -0
  36. package/build/Calls.types.d.ts.map +1 -0
  37. package/build/Calls.types.js +2 -0
  38. package/build/Calls.types.js.map +1 -0
  39. package/build/ExpoCallKitTelecomModule.d.ts +3 -0
  40. package/build/ExpoCallKitTelecomModule.d.ts.map +1 -0
  41. package/build/ExpoCallKitTelecomModule.js +4 -0
  42. package/build/ExpoCallKitTelecomModule.js.map +1 -0
  43. package/build/hooks/index.d.ts +2 -0
  44. package/build/hooks/index.d.ts.map +1 -0
  45. package/build/hooks/index.js +2 -0
  46. package/build/hooks/index.js.map +1 -0
  47. package/build/hooks/useVoIPPushToken.d.ts +14 -0
  48. package/build/hooks/useVoIPPushToken.d.ts.map +1 -0
  49. package/build/hooks/useVoIPPushToken.js +26 -0
  50. package/build/hooks/useVoIPPushToken.js.map +1 -0
  51. package/build/index.d.ts +4 -0
  52. package/build/index.d.ts.map +1 -0
  53. package/build/index.js +4 -0
  54. package/build/index.js.map +1 -0
  55. package/expo-module.config.json +10 -0
  56. package/ios/AppDelegateSubscriber.swift +93 -0
  57. package/ios/ExpoCallKitTelecom.podspec +31 -0
  58. package/ios/ExpoCallKitTelecomLogger.swift +55 -0
  59. package/ios/ExpoCallKitTelecomModule.swift +503 -0
  60. package/ios/Managers/AudioManager.swift +363 -0
  61. package/ios/Managers/CallEventEmitter.swift +199 -0
  62. package/ios/Managers/CallManager+CXProviderDelegate.swift +195 -0
  63. package/ios/Managers/CallManager.swift +714 -0
  64. package/ios/Managers/CaptureSessionManager.swift +54 -0
  65. package/ios/Managers/DialtonePlayer.swift +126 -0
  66. package/ios/Managers/FulfillRequestManager.swift +154 -0
  67. package/ios/Managers/VoIPPushManager+PKPushRegistryDelegate.swift +123 -0
  68. package/ios/Managers/VoIPPushManager.swift +58 -0
  69. package/ios/Models/CallEvents.swift +263 -0
  70. package/ios/Models/CallOptions.swift +15 -0
  71. package/ios/Models/CallParticipant.swift +37 -0
  72. package/ios/Models/CallSession.swift +80 -0
  73. package/ios/Models/IncomingCallEvent.swift +196 -0
  74. package/ios/Stores/CallStore.swift +149 -0
  75. package/package.json +56 -0
  76. package/plugin/build/constants.d.ts +3 -0
  77. package/plugin/build/constants.js +7 -0
  78. package/plugin/build/withExpoCallKitTelecom.d.ts +67 -0
  79. package/plugin/build/withExpoCallKitTelecom.js +16 -0
  80. package/plugin/build/withExpoCallKitTelecomAndroid.d.ts +3 -0
  81. package/plugin/build/withExpoCallKitTelecomAndroid.js +177 -0
  82. package/plugin/build/withExpoCallKitTelecomIos.d.ts +3 -0
  83. package/plugin/build/withExpoCallKitTelecomIos.js +195 -0
  84. package/plugin/src/constants.ts +4 -0
  85. package/plugin/src/withExpoCallKitTelecom.ts +83 -0
  86. package/plugin/src/withExpoCallKitTelecomAndroid.ts +293 -0
  87. package/plugin/src/withExpoCallKitTelecomIos.ts +276 -0
  88. package/src/Calls.ts +848 -0
  89. package/src/Calls.types.ts +275 -0
  90. package/src/ExpoCallKitTelecomModule.ts +4 -0
  91. package/src/hooks/index.ts +1 -0
  92. package/src/hooks/useVoIPPushToken.ts +34 -0
  93. package/src/index.ts +3 -0
@@ -0,0 +1,181 @@
1
+ package expo.modules.callkittelecom.store
2
+
3
+ import expo.modules.callkittelecom.events.CallEventEmitter
4
+ import expo.modules.callkittelecom.events.CallEvents
5
+ import expo.modules.callkittelecom.models.CallSession
6
+ import expo.modules.callkittelecom.models.CallSessionStatus
7
+ import kotlinx.coroutines.channels.awaitClose
8
+ import kotlinx.coroutines.flow.Flow
9
+ import kotlinx.coroutines.flow.callbackFlow
10
+ import java.util.LinkedHashMap
11
+ import java.util.UUID
12
+
13
+ /**
14
+ * Thread-safe in-memory store for active call sessions.
15
+ *
16
+ * Emits session add/update/remove events whenever the backing state changes.
17
+ */
18
+ object CallStore {
19
+ private val lock = Any()
20
+ private val sessions = LinkedHashMap<UUID, CallSession>()
21
+
22
+ private data class SessionObserver(
23
+ val callback: (CallSession) -> Unit,
24
+ val filter: (CallSession) -> Boolean,
25
+ )
26
+
27
+ private val sessionObservers = mutableMapOf<UUID, MutableMap<UUID, SessionObserver>>()
28
+
29
+ /** Returns the first active session (single-call model). */
30
+ fun firstSession(): CallSession? =
31
+ synchronized(lock) {
32
+ sessions.values.firstOrNull()
33
+ }
34
+
35
+ /** Returns all sessions in insertion order. */
36
+ fun allSessions(): List<CallSession> =
37
+ synchronized(lock) {
38
+ sessions.values.toList()
39
+ }
40
+
41
+ /** Returns a session by call UUID, or null if missing. */
42
+ fun session(id: UUID): CallSession? =
43
+ synchronized(lock) {
44
+ sessions[id]
45
+ }
46
+
47
+ /** Adds a session and emits `onCallSessionAdded` if it did not already exist. */
48
+ fun add(session: CallSession) {
49
+ val sessionMap: Map<String, Any?>?
50
+ synchronized(lock) {
51
+ if (sessions.containsKey(session.id)) {
52
+ sessionMap = null
53
+ } else {
54
+ sessions[session.id] = session
55
+ sessionMap = session.toMap()
56
+ }
57
+ }
58
+ if (sessionMap != null) {
59
+ CallEventEmitter.send(
60
+ CallEvents.CALL_SESSION_ADDED,
61
+ mapOf("session" to sessionMap),
62
+ )
63
+ }
64
+ }
65
+
66
+ /** Removes a session and emits `onCallSessionRemoved` when present. */
67
+ fun remove(id: UUID) {
68
+ val removed =
69
+ synchronized(lock) {
70
+ sessions.remove(id) != null
71
+ }
72
+ if (removed) {
73
+ CallEventEmitter.send(CallEvents.CALL_SESSION_REMOVED, mapOf("id" to id.toString()))
74
+ }
75
+ }
76
+
77
+ /** Removes all sessions and emits `onCallSessionRemoved` for each prior ID. */
78
+ fun removeAll() {
79
+ val ids =
80
+ synchronized(lock) {
81
+ val sessionIds = sessions.keys.toList()
82
+ sessions.clear()
83
+ sessionIds
84
+ }
85
+ ids.forEach { id ->
86
+ CallEventEmitter.send(CallEvents.CALL_SESSION_REMOVED, mapOf("id" to id.toString()))
87
+ }
88
+ }
89
+
90
+ /** Applies a copy-on-write transform and emits `onCallSessionUpdated` when changed. */
91
+ fun update(
92
+ id: UUID,
93
+ transform: (CallSession) -> CallSession,
94
+ ) {
95
+ val sessionMap: Map<String, Any?>
96
+ val matchingObservers: List<Pair<SessionObserver, CallSession>>
97
+
98
+ synchronized(lock) {
99
+ val current = sessions[id] ?: return
100
+ val updated = transform(current)
101
+ if (updated == current) return
102
+ sessions[id] = updated
103
+ sessionMap = updated.toMap()
104
+
105
+ matchingObservers =
106
+ sessionObservers[id]?.values?.mapNotNull { observer ->
107
+ if (observer.filter(updated)) observer to updated else null
108
+ } ?: emptyList()
109
+ }
110
+
111
+ matchingObservers.forEach { (observer, session) ->
112
+ observer.callback(session)
113
+ }
114
+
115
+ CallEventEmitter.send(
116
+ CallEvents.CALL_SESSION_UPDATED,
117
+ mapOf("session" to sessionMap),
118
+ )
119
+ }
120
+
121
+ /** Convenience status-only update helper. */
122
+ fun updateStatus(
123
+ id: UUID,
124
+ status: CallSessionStatus,
125
+ ) {
126
+ update(id) { it.copy(status = status) }
127
+ }
128
+
129
+ /** Convenience mute-state update helper. */
130
+ fun updateMuted(
131
+ id: UUID,
132
+ isMuted: Boolean,
133
+ ) {
134
+ update(id) { it.copy(isMuted = isMuted) }
135
+ }
136
+
137
+ /** Convenience hold-state update helper. */
138
+ fun updateHeld(
139
+ id: UUID,
140
+ isOnHold: Boolean,
141
+ ) {
142
+ update(id) { it.copy(isOnHold = isOnHold) }
143
+ }
144
+
145
+ /**
146
+ * Returns a [Flow] that emits session updates matching the optional filter.
147
+ *
148
+ * Immediately emits the current session if it exists and matches the filter.
149
+ * Useful for observing status transitions on a specific call.
150
+ */
151
+ fun sessionUpdates(
152
+ callId: UUID,
153
+ filter: (CallSession) -> Boolean = { true },
154
+ ): Flow<CallSession> =
155
+ callbackFlow {
156
+ val observerId = UUID.randomUUID()
157
+
158
+ synchronized(lock) {
159
+ val current = sessions[callId]
160
+ if (current != null && filter(current)) {
161
+ trySend(current)
162
+ }
163
+
164
+ val observers = sessionObservers.getOrPut(callId) { mutableMapOf() }
165
+ observers[observerId] =
166
+ SessionObserver(
167
+ callback = { session -> trySend(session) },
168
+ filter = filter,
169
+ )
170
+ }
171
+
172
+ awaitClose {
173
+ synchronized(lock) {
174
+ sessionObservers[callId]?.remove(observerId)
175
+ if (sessionObservers[callId]?.isEmpty() == true) {
176
+ sessionObservers.remove(callId)
177
+ }
178
+ }
179
+ }
180
+ }
181
+ }
@@ -0,0 +1,52 @@
1
+ package expo.modules.callkittelecom.utils
2
+
3
+ import android.content.Context
4
+ import android.content.pm.ApplicationInfo
5
+ import android.util.Log
6
+
7
+ /**
8
+ * Debug-only logging wrapper for expo-calls.
9
+ *
10
+ * All log calls are no-ops in release builds (where `FLAG_DEBUGGABLE` is unset),
11
+ * eliminating both the I/O cost and the string-building cost of log messages.
12
+ * The `msg` parameter is an `inline` lambda so the string is never allocated
13
+ * when logging is disabled.
14
+ */
15
+ object CallKitTelecomLog {
16
+ @Volatile
17
+ @PublishedApi
18
+ internal var enabled = false
19
+ private set
20
+
21
+ /** Call once during module/manager init to auto-detect debuggable flag. */
22
+ fun init(context: Context) {
23
+ enabled = (context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0
24
+ }
25
+
26
+ /** Allows tests or developer settings to override the auto-detected value. */
27
+ fun setEnabled(value: Boolean) {
28
+ enabled = value
29
+ }
30
+
31
+ inline fun d(
32
+ tag: String,
33
+ msg: () -> String,
34
+ ) {
35
+ if (enabled) Log.d(tag, msg())
36
+ }
37
+
38
+ inline fun w(
39
+ tag: String,
40
+ msg: () -> String,
41
+ ) {
42
+ if (enabled) Log.w(tag, msg())
43
+ }
44
+
45
+ inline fun e(
46
+ tag: String,
47
+ tr: Throwable? = null,
48
+ msg: () -> String,
49
+ ) {
50
+ if (enabled) Log.e(tag, msg(), tr)
51
+ }
52
+ }
@@ -0,0 +1,28 @@
1
+ package expo.modules.callkittelecom.utils
2
+
3
+ import android.Manifest
4
+ import android.content.Context
5
+ import android.content.pm.PackageManager
6
+ import androidx.core.content.ContextCompat
7
+
8
+ /** Utility helpers that map Android runtime permission checks to shared string statuses. */
9
+ object PermissionUtils {
10
+ /** Returns `granted` or `denied` for a specific Android permission. */
11
+ private fun permissionStatus(
12
+ context: Context,
13
+ permission: String,
14
+ ): String =
15
+ if (
16
+ ContextCompat.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED
17
+ ) {
18
+ "granted"
19
+ } else {
20
+ "denied"
21
+ }
22
+
23
+ /** Returns microphone permission status for `RECORD_AUDIO`. */
24
+ fun microphonePermission(context: Context): String = permissionStatus(context, Manifest.permission.RECORD_AUDIO)
25
+
26
+ /** Returns camera permission status for `CAMERA`. */
27
+ fun cameraPermission(context: Context): String = permissionStatus(context, Manifest.permission.CAMERA)
28
+ }
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <ripple xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:color="#4DFFFFFF">
4
+ <item>
5
+ <shape android:shape="oval">
6
+ <solid android:color="#34C759" />
7
+ </shape>
8
+ </item>
9
+ </ripple>
@@ -0,0 +1,5 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:shape="oval">
4
+ <solid android:color="#1C1C2E" />
5
+ </shape>
@@ -0,0 +1,9 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <ripple xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:color="#4DFFFFFF">
4
+ <item>
5
+ <shape android:shape="oval">
6
+ <solid android:color="#FF3B30" />
7
+ </shape>
8
+ </item>
9
+ </ripple>
@@ -0,0 +1,9 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:width="28dp"
3
+ android:height="28dp"
4
+ android:viewportWidth="24"
5
+ android:viewportHeight="24">
6
+ <path
7
+ android:fillColor="#FFFFFF"
8
+ android:pathData="M6.62,10.79c1.44,2.83 3.76,5.14 6.59,6.59l2.2,-2.2c0.27,-0.27 0.67,-0.36 1.02,-0.24 1.12,0.37 2.33,0.57 3.57,0.57 0.55,0 1,0.45 1,1V20c0,0.55 -0.45,1 -1,1 -9.39,0 -17,-7.61 -17,-17 0,-0.55 0.45,-1 1,-1h3.5c0.55,0 1,0.45 1,1 0,1.25 0.2,2.45 0.57,3.57 0.11,0.35 0.03,0.74 -0.25,1.02l-2.2,2.2z" />
9
+ </vector>
@@ -0,0 +1,9 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:width="28dp"
3
+ android:height="28dp"
4
+ android:viewportWidth="24"
5
+ android:viewportHeight="24">
6
+ <path
7
+ android:fillColor="#FFFFFF"
8
+ android:pathData="M12,9c-1.6,0 -3.15,0.25 -4.6,0.72v3.1c0,0.39 -0.23,0.74 -0.56,0.9 -0.98,0.49 -1.87,1.12 -2.66,1.85 -0.18,0.18 -0.43,0.28 -0.7,0.28 -0.28,0 -0.53,-0.11 -0.71,-0.29L0.29,13.08c-0.18,-0.17 -0.29,-0.42 -0.29,-0.7 0,-0.28 0.11,-0.53 0.29,-0.71C3.34,8.78 7.46,7 12,7s8.66,1.78 11.71,4.67c0.18,0.18 0.29,0.43 0.29,0.71 0,0.28 -0.11,0.53 -0.29,0.71l-2.48,2.48c-0.18,0.18 -0.43,0.29 -0.71,0.29 -0.27,0 -0.52,-0.11 -0.7,-0.28 -0.79,-0.74 -1.69,-1.36 -2.67,-1.85 -0.33,-0.16 -0.56,-0.5 -0.56,-0.9v-3.1C15.15,9.25 13.6,9 12,9z" />
9
+ </vector>
@@ -0,0 +1,9 @@
1
+ <vector xmlns:android="http://schemas.android.com/apk/res/android"
2
+ android:width="28dp"
3
+ android:height="28dp"
4
+ android:viewportWidth="24"
5
+ android:viewportHeight="24">
6
+ <path
7
+ android:fillColor="#FFFFFF"
8
+ android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z" />
9
+ </vector>
@@ -0,0 +1,169 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:layout_width="match_parent"
4
+ android:layout_height="match_parent"
5
+ android:background="#0D0D0D"
6
+ android:gravity="center_horizontal"
7
+ android:orientation="vertical">
8
+
9
+ <!-- App branding -->
10
+ <LinearLayout
11
+ android:layout_width="wrap_content"
12
+ android:layout_height="wrap_content"
13
+ android:layout_marginTop="56dp"
14
+ android:gravity="center_vertical"
15
+ android:orientation="horizontal">
16
+
17
+ <ImageView
18
+ android:id="@+id/expo_callkit_telecom_app_icon"
19
+ android:layout_width="20dp"
20
+ android:layout_height="20dp"
21
+ android:layout_marginEnd="6dp"
22
+ android:importantForAccessibility="no" />
23
+
24
+ <TextView
25
+ android:id="@+id/expo_callkit_telecom_app_name"
26
+ android:layout_width="wrap_content"
27
+ android:layout_height="wrap_content"
28
+ android:fontFamily="sans-serif-medium"
29
+ android:textColor="#8E8E93"
30
+ android:textSize="14sp" />
31
+ </LinearLayout>
32
+
33
+ <!-- Top spacer pushes caller info toward upper-center -->
34
+ <View
35
+ android:layout_width="0dp"
36
+ android:layout_height="0dp"
37
+ android:layout_weight="2" />
38
+
39
+ <!-- Caller avatar circle with image or initial fallback -->
40
+ <FrameLayout
41
+ android:layout_width="88dp"
42
+ android:layout_height="88dp"
43
+ android:background="@drawable/expo_callkit_telecom_bg_avatar">
44
+
45
+ <ImageView
46
+ android:id="@+id/expo_callkit_telecom_avatar_image"
47
+ android:layout_width="match_parent"
48
+ android:layout_height="match_parent"
49
+ android:importantForAccessibility="no"
50
+ android:scaleType="centerCrop"
51
+ android:visibility="gone" />
52
+
53
+ <TextView
54
+ android:id="@+id/expo_callkit_telecom_avatar_text"
55
+ android:layout_width="wrap_content"
56
+ android:layout_height="wrap_content"
57
+ android:layout_gravity="center"
58
+ android:fontFamily="sans-serif-medium"
59
+ android:textColor="#FFFFFF"
60
+ android:textSize="34sp" />
61
+ </FrameLayout>
62
+
63
+ <View
64
+ android:layout_width="0dp"
65
+ android:layout_height="24dp" />
66
+
67
+ <!-- Caller display name -->
68
+ <TextView
69
+ android:id="@+id/expo_callkit_telecom_caller_name"
70
+ android:layout_width="wrap_content"
71
+ android:layout_height="wrap_content"
72
+ android:fontFamily="sans-serif-medium"
73
+ android:letterSpacing="-0.01"
74
+ android:textColor="#FFFFFF"
75
+ android:textSize="26sp" />
76
+
77
+ <View
78
+ android:layout_width="0dp"
79
+ android:layout_height="8dp" />
80
+
81
+ <!-- Call type subtitle -->
82
+ <TextView
83
+ android:id="@+id/expo_callkit_telecom_subtitle"
84
+ android:layout_width="wrap_content"
85
+ android:layout_height="wrap_content"
86
+ android:fontFamily="sans-serif"
87
+ android:textColor="#8E8E93"
88
+ android:textSize="16sp" />
89
+
90
+ <!-- Middle spacer pushes buttons toward bottom -->
91
+ <View
92
+ android:layout_width="0dp"
93
+ android:layout_height="0dp"
94
+ android:layout_weight="3" />
95
+
96
+ <!-- Answer and decline buttons -->
97
+ <LinearLayout
98
+ android:layout_width="match_parent"
99
+ android:layout_height="wrap_content"
100
+ android:gravity="center"
101
+ android:orientation="horizontal">
102
+
103
+ <!-- Decline button column -->
104
+ <LinearLayout
105
+ android:layout_width="0dp"
106
+ android:layout_height="wrap_content"
107
+ android:layout_weight="1"
108
+ android:gravity="center_horizontal"
109
+ android:orientation="vertical">
110
+
111
+ <ImageButton
112
+ android:id="@+id/expo_callkit_telecom_decline_button"
113
+ android:layout_width="64dp"
114
+ android:layout_height="64dp"
115
+ android:background="@drawable/expo_callkit_telecom_bg_decline"
116
+ android:contentDescription="Decline"
117
+ android:scaleType="center"
118
+ android:src="@drawable/expo_callkit_telecom_ic_decline" />
119
+
120
+ <View
121
+ android:layout_width="0dp"
122
+ android:layout_height="12dp" />
123
+
124
+ <TextView
125
+ android:layout_width="wrap_content"
126
+ android:layout_height="wrap_content"
127
+ android:fontFamily="sans-serif"
128
+ android:text="Decline"
129
+ android:textColor="#8E8E93"
130
+ android:textSize="13sp" />
131
+ </LinearLayout>
132
+
133
+ <!-- Answer button column -->
134
+ <LinearLayout
135
+ android:layout_width="0dp"
136
+ android:layout_height="wrap_content"
137
+ android:layout_weight="1"
138
+ android:gravity="center_horizontal"
139
+ android:orientation="vertical">
140
+
141
+ <ImageButton
142
+ android:id="@+id/expo_callkit_telecom_answer_button"
143
+ android:layout_width="64dp"
144
+ android:layout_height="64dp"
145
+ android:background="@drawable/expo_callkit_telecom_bg_answer"
146
+ android:contentDescription="Answer"
147
+ android:scaleType="center"
148
+ android:src="@drawable/expo_callkit_telecom_ic_answer" />
149
+
150
+ <View
151
+ android:layout_width="0dp"
152
+ android:layout_height="12dp" />
153
+
154
+ <TextView
155
+ android:layout_width="wrap_content"
156
+ android:layout_height="wrap_content"
157
+ android:fontFamily="sans-serif"
158
+ android:text="Answer"
159
+ android:textColor="#8E8E93"
160
+ android:textSize="13sp" />
161
+ </LinearLayout>
162
+ </LinearLayout>
163
+
164
+ <!-- Bottom padding -->
165
+ <View
166
+ android:layout_width="0dp"
167
+ android:layout_height="56dp" />
168
+
169
+ </LinearLayout>
package/app.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "android": {
3
+ "package": "com.mfairley.expocallkittelecom"
4
+ },
5
+ "ios": {
6
+ "bundleIdentifier": "com.mfairley.expocallkittelecom"
7
+ }
8
+ }
package/app.plugin.js ADDED
@@ -0,0 +1 @@
1
+ module.exports = require("./plugin/build/withExpoCallKitTelecom");