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.
- package/LICENSE +21 -0
- package/README.md +197 -0
- package/android/build.gradle +32 -0
- package/android/src/main/AndroidManifest.xml +33 -0
- package/android/src/main/java/expo/modules/callkittelecom/ExpoCallKitTelecomModule.kt +384 -0
- package/android/src/main/java/expo/modules/callkittelecom/IncomingCallActivity.kt +275 -0
- package/android/src/main/java/expo/modules/callkittelecom/events/CallEventEmitter.kt +151 -0
- package/android/src/main/java/expo/modules/callkittelecom/events/CallEvents.kt +59 -0
- package/android/src/main/java/expo/modules/callkittelecom/managers/CallAudioManager.kt +361 -0
- package/android/src/main/java/expo/modules/callkittelecom/managers/CallManager.kt +891 -0
- package/android/src/main/java/expo/modules/callkittelecom/managers/CallNotificationManager.kt +445 -0
- package/android/src/main/java/expo/modules/callkittelecom/managers/CaptureSessionManager.kt +27 -0
- package/android/src/main/java/expo/modules/callkittelecom/managers/DialtonePlayer.kt +171 -0
- package/android/src/main/java/expo/modules/callkittelecom/managers/FulfillRequestManager.kt +150 -0
- package/android/src/main/java/expo/modules/callkittelecom/managers/VoIPPushManager.kt +54 -0
- package/android/src/main/java/expo/modules/callkittelecom/models/CallModels.kt +269 -0
- package/android/src/main/java/expo/modules/callkittelecom/services/CallNotificationReceiver.kt +54 -0
- package/android/src/main/java/expo/modules/callkittelecom/services/ExpoCallKitTelecomMessagingService.kt +161 -0
- package/android/src/main/java/expo/modules/callkittelecom/store/CallStore.kt +181 -0
- package/android/src/main/java/expo/modules/callkittelecom/utils/CallKitTelecomLog.kt +52 -0
- package/android/src/main/java/expo/modules/callkittelecom/utils/PermissionUtils.kt +28 -0
- package/android/src/main/res/drawable/expo_callkit_telecom_bg_answer.xml +9 -0
- package/android/src/main/res/drawable/expo_callkit_telecom_bg_avatar.xml +5 -0
- package/android/src/main/res/drawable/expo_callkit_telecom_bg_decline.xml +9 -0
- package/android/src/main/res/drawable/expo_callkit_telecom_ic_answer.xml +9 -0
- package/android/src/main/res/drawable/expo_callkit_telecom_ic_decline.xml +9 -0
- package/android/src/main/res/drawable/expo_callkit_telecom_ic_videocam.xml +9 -0
- package/android/src/main/res/layout/activity_incoming_call.xml +169 -0
- package/app.json +8 -0
- package/app.plugin.js +1 -0
- package/build/Calls.d.ts +577 -0
- package/build/Calls.d.ts.map +1 -0
- package/build/Calls.js +715 -0
- package/build/Calls.js.map +1 -0
- package/build/Calls.types.d.ts +203 -0
- package/build/Calls.types.d.ts.map +1 -0
- package/build/Calls.types.js +2 -0
- package/build/Calls.types.js.map +1 -0
- package/build/ExpoCallKitTelecomModule.d.ts +3 -0
- package/build/ExpoCallKitTelecomModule.d.ts.map +1 -0
- package/build/ExpoCallKitTelecomModule.js +4 -0
- package/build/ExpoCallKitTelecomModule.js.map +1 -0
- package/build/hooks/index.d.ts +2 -0
- package/build/hooks/index.d.ts.map +1 -0
- package/build/hooks/index.js +2 -0
- package/build/hooks/index.js.map +1 -0
- package/build/hooks/useVoIPPushToken.d.ts +14 -0
- package/build/hooks/useVoIPPushToken.d.ts.map +1 -0
- package/build/hooks/useVoIPPushToken.js +26 -0
- package/build/hooks/useVoIPPushToken.js.map +1 -0
- package/build/index.d.ts +4 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +4 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +10 -0
- package/ios/AppDelegateSubscriber.swift +93 -0
- package/ios/ExpoCallKitTelecom.podspec +31 -0
- package/ios/ExpoCallKitTelecomLogger.swift +55 -0
- package/ios/ExpoCallKitTelecomModule.swift +503 -0
- package/ios/Managers/AudioManager.swift +363 -0
- package/ios/Managers/CallEventEmitter.swift +199 -0
- package/ios/Managers/CallManager+CXProviderDelegate.swift +195 -0
- package/ios/Managers/CallManager.swift +714 -0
- package/ios/Managers/CaptureSessionManager.swift +54 -0
- package/ios/Managers/DialtonePlayer.swift +126 -0
- package/ios/Managers/FulfillRequestManager.swift +154 -0
- package/ios/Managers/VoIPPushManager+PKPushRegistryDelegate.swift +123 -0
- package/ios/Managers/VoIPPushManager.swift +58 -0
- package/ios/Models/CallEvents.swift +263 -0
- package/ios/Models/CallOptions.swift +15 -0
- package/ios/Models/CallParticipant.swift +37 -0
- package/ios/Models/CallSession.swift +80 -0
- package/ios/Models/IncomingCallEvent.swift +196 -0
- package/ios/Stores/CallStore.swift +149 -0
- package/package.json +56 -0
- package/plugin/build/constants.d.ts +3 -0
- package/plugin/build/constants.js +7 -0
- package/plugin/build/withExpoCallKitTelecom.d.ts +67 -0
- package/plugin/build/withExpoCallKitTelecom.js +16 -0
- package/plugin/build/withExpoCallKitTelecomAndroid.d.ts +3 -0
- package/plugin/build/withExpoCallKitTelecomAndroid.js +177 -0
- package/plugin/build/withExpoCallKitTelecomIos.d.ts +3 -0
- package/plugin/build/withExpoCallKitTelecomIos.js +195 -0
- package/plugin/src/constants.ts +4 -0
- package/plugin/src/withExpoCallKitTelecom.ts +83 -0
- package/plugin/src/withExpoCallKitTelecomAndroid.ts +293 -0
- package/plugin/src/withExpoCallKitTelecomIos.ts +276 -0
- package/src/Calls.ts +848 -0
- package/src/Calls.types.ts +275 -0
- package/src/ExpoCallKitTelecomModule.ts +4 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/useVoIPPushToken.ts +34 -0
- 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
|
+
<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
package/app.plugin.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require("./plugin/build/withExpoCallKitTelecom");
|