@qusaieilouti99/call-manager 0.1.46 → 0.1.48
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.
|
@@ -1,67 +1,22 @@
|
|
|
1
|
-
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
1
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+
package="com.margelo.nitro.qusaieilouti99.callmanager">
|
|
2
3
|
|
|
3
|
-
<!--
|
|
4
|
+
<!-- Essential permissions that the library requires -->
|
|
4
5
|
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
|
|
5
|
-
|
|
6
|
+
<uses-permission android:name="android.permission.BIND_TELECOM_CONNECTION_SERVICE" />
|
|
6
7
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
|
7
|
-
<!-- Required for changing audio routes like speakerphone -->
|
|
8
8
|
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
|
|
9
|
-
<!-- For keeping screen awake during calls -->
|
|
10
9
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
|
11
|
-
<!-- For vibrating the device (e.g., incoming call) -->
|
|
12
10
|
<uses-permission android:name="android.permission.VIBRATE" />
|
|
11
|
+
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
|
12
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
13
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
14
|
+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
|
|
13
15
|
|
|
14
|
-
<!-- Bluetooth permissions
|
|
15
|
-
<!-- For Android 11 (API 30) and lower -->
|
|
16
|
+
<!-- Bluetooth permissions -->
|
|
16
17
|
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
|
|
17
18
|
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />
|
|
18
|
-
<!-- For Android 12 (API 31) and higher -->
|
|
19
19
|
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
|
20
20
|
|
|
21
|
-
<!--
|
|
22
|
-
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
|
23
|
-
<!-- Required for foreground service with phoneCall type on Android 12 (API 31) and higher -->
|
|
24
|
-
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
|
|
25
|
-
|
|
26
|
-
<application>
|
|
27
|
-
<!-- Your CallActivity for full-screen incoming call UI -->
|
|
28
|
-
<activity
|
|
29
|
-
android:name=".CallActivity"
|
|
30
|
-
android:exported="true"
|
|
31
|
-
android:showOnLockScreen="true"
|
|
32
|
-
android:turnScreenOn="true"
|
|
33
|
-
android:theme="@android:style/Theme.DeviceDefault.NoActionBar"
|
|
34
|
-
android:screenOrientation="portrait"
|
|
35
|
-
android:launchMode="singleTop"
|
|
36
|
-
android:excludeFromRecents="true"
|
|
37
|
-
android:taskAffinity=""
|
|
38
|
-
android:windowSoftInputMode="adjustResize">
|
|
39
|
-
<intent-filter>
|
|
40
|
-
<action android:name="android.intent.action.VIEW"/>
|
|
41
|
-
<category android:name="android.intent.category.DEFAULT"/>
|
|
42
|
-
</intent-filter>
|
|
43
|
-
</activity>
|
|
44
|
-
|
|
45
|
-
<!-- The ConnectionService, crucial for Telecom API integration -->
|
|
46
|
-
<service
|
|
47
|
-
android:name=".MyConnectionService"
|
|
48
|
-
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
|
|
49
|
-
android:exported="true">
|
|
50
|
-
<intent-filter>
|
|
51
|
-
<action android:name="android.telecom.ConnectionService" />
|
|
52
|
-
</intent-filter>
|
|
53
|
-
</service>
|
|
54
|
-
|
|
55
|
-
<!-- The foreground service to keep the call alive in the background -->
|
|
56
|
-
<service
|
|
57
|
-
android:name=".CallForegroundService"
|
|
58
|
-
android:enabled="true"
|
|
59
|
-
android:exported="false"
|
|
60
|
-
android:foregroundServiceType="phoneCall" />
|
|
61
|
-
|
|
62
|
-
<!-- Receiver for notification actions (Answer/Decline) -->
|
|
63
|
-
<receiver
|
|
64
|
-
android:name=".CallNotificationActionReceiver"
|
|
65
|
-
android:exported="false" />
|
|
66
|
-
</application>
|
|
21
|
+
<!-- No components needed here - they're declared in app manifest -->
|
|
67
22
|
</manifest>
|
|
@@ -24,6 +24,7 @@ import android.telecom.VideoProfile
|
|
|
24
24
|
import android.util.Log
|
|
25
25
|
import android.graphics.Color
|
|
26
26
|
import android.app.Person
|
|
27
|
+
import android.view.WindowManager
|
|
27
28
|
import org.json.JSONArray
|
|
28
29
|
import org.json.JSONObject
|
|
29
30
|
import java.util.UUID
|
|
@@ -49,6 +50,14 @@ object CallEngine {
|
|
|
49
50
|
private var currentCallId: String? = null
|
|
50
51
|
private var canMakeMultipleCalls: Boolean = true
|
|
51
52
|
|
|
53
|
+
// --- Lock Screen Bypass State Management ---
|
|
54
|
+
private var lockScreenBypassActive = false
|
|
55
|
+
private val lockScreenBypassCallbacks = mutableSetOf<LockScreenBypassCallback>()
|
|
56
|
+
|
|
57
|
+
interface LockScreenBypassCallback {
|
|
58
|
+
fun onLockScreenBypassChanged(shouldBypass: Boolean)
|
|
59
|
+
}
|
|
60
|
+
|
|
52
61
|
data class CallInfo(
|
|
53
62
|
val callId: String,
|
|
54
63
|
val callData: String,
|
|
@@ -89,6 +98,34 @@ object CallEngine {
|
|
|
89
98
|
|
|
90
99
|
fun getAppContext(): Context? = appContext
|
|
91
100
|
|
|
101
|
+
// --- Lock Screen Bypass Management (Single Source of Truth) ---
|
|
102
|
+
fun registerLockScreenBypassCallback(callback: LockScreenBypassCallback) {
|
|
103
|
+
lockScreenBypassCallbacks.add(callback)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
fun unregisterLockScreenBypassCallback(callback: LockScreenBypassCallback) {
|
|
107
|
+
lockScreenBypassCallbacks.remove(callback)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private fun updateLockScreenBypass() {
|
|
111
|
+
val shouldBypass = isCallActive()
|
|
112
|
+
if (lockScreenBypassActive != shouldBypass) {
|
|
113
|
+
lockScreenBypassActive = shouldBypass
|
|
114
|
+
Log.d(TAG, "Lock screen bypass state changed: $lockScreenBypassActive")
|
|
115
|
+
|
|
116
|
+
// Notify all registered callbacks
|
|
117
|
+
lockScreenBypassCallbacks.forEach { callback ->
|
|
118
|
+
try {
|
|
119
|
+
callback.onLockScreenBypassChanged(shouldBypass)
|
|
120
|
+
} catch (e: Exception) {
|
|
121
|
+
Log.w(TAG, "Error notifying lock screen bypass callback", e)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
fun isLockScreenBypassActive(): Boolean = lockScreenBypassActive
|
|
128
|
+
|
|
92
129
|
// --- Telecom Connection Management ---
|
|
93
130
|
fun addTelecomConnection(callId: String, connection: Connection) {
|
|
94
131
|
telecomConnections[callId] = connection
|
|
@@ -163,7 +200,6 @@ object CallEngine {
|
|
|
163
200
|
telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
|
|
164
201
|
startForegroundService(context)
|
|
165
202
|
Log.d(TAG, "Successfully reported incoming call to TelecomManager for $callId")
|
|
166
|
-
// REMOVED: Don't bring app to foreground for incoming calls - only when answered
|
|
167
203
|
} catch (e: SecurityException) {
|
|
168
204
|
Log.e(TAG, "SecurityException: Failed to report incoming call. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
|
|
169
205
|
endCall(context, callId)
|
|
@@ -171,6 +207,7 @@ object CallEngine {
|
|
|
171
207
|
Log.e(TAG, "Failed to report incoming call: ${e.message}", e)
|
|
172
208
|
endCall(context, callId)
|
|
173
209
|
}
|
|
210
|
+
updateLockScreenBypass()
|
|
174
211
|
notifyCallStateChanged(context)
|
|
175
212
|
}
|
|
176
213
|
|
|
@@ -217,7 +254,6 @@ object CallEngine {
|
|
|
217
254
|
startForegroundService(context)
|
|
218
255
|
Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
|
|
219
256
|
startRingback()
|
|
220
|
-
// CHANGED: Bring app to foreground for outgoing calls
|
|
221
257
|
bringAppToForeground(context)
|
|
222
258
|
keepScreenAwake(context, true)
|
|
223
259
|
if (parsedCallType == "Video") {
|
|
@@ -232,6 +268,7 @@ object CallEngine {
|
|
|
232
268
|
Log.e(TAG, "Failed to start outgoing call via placeCall: ${e.message}", e)
|
|
233
269
|
endCall(context, callId)
|
|
234
270
|
}
|
|
271
|
+
updateLockScreenBypass()
|
|
235
272
|
notifyCallStateChanged(context)
|
|
236
273
|
}
|
|
237
274
|
|
|
@@ -270,6 +307,8 @@ object CallEngine {
|
|
|
270
307
|
keepScreenAwake(context, true)
|
|
271
308
|
resetAudioMode(context)
|
|
272
309
|
|
|
310
|
+
updateLockScreenBypass()
|
|
311
|
+
|
|
273
312
|
// Emit event
|
|
274
313
|
emitEvent(CallEventType.CALL_ANSWERED, JSONObject().put("callId", callId))
|
|
275
314
|
notifyCallStateChanged(context)
|
|
@@ -279,9 +318,11 @@ object CallEngine {
|
|
|
279
318
|
|
|
280
319
|
fun holdCall(context: Context, callId: String) {
|
|
281
320
|
Log.d(TAG, "holdCall: $callId")
|
|
321
|
+
activeCalls[callId]?.state = CallState.HELD
|
|
282
322
|
val connection = telecomConnections[callId]
|
|
283
323
|
connection?.setOnHold()
|
|
284
324
|
emitEvent(CallEventType.CALL_HELD, JSONObject().put("callId", callId))
|
|
325
|
+
updateLockScreenBypass()
|
|
285
326
|
notifyCallStateChanged(context)
|
|
286
327
|
}
|
|
287
328
|
|
|
@@ -291,6 +332,7 @@ object CallEngine {
|
|
|
291
332
|
val connection = telecomConnections[callId]
|
|
292
333
|
connection?.setActive()
|
|
293
334
|
emitEvent(CallEventType.CALL_UNHELD, JSONObject().put("callId", callId))
|
|
335
|
+
updateLockScreenBypass()
|
|
294
336
|
notifyCallStateChanged(context)
|
|
295
337
|
}
|
|
296
338
|
|
|
@@ -332,6 +374,7 @@ object CallEngine {
|
|
|
332
374
|
|
|
333
375
|
// Final cleanup
|
|
334
376
|
finalCleanup(context)
|
|
377
|
+
updateLockScreenBypass()
|
|
335
378
|
notifyCallStateChanged(context)
|
|
336
379
|
}
|
|
337
380
|
|
|
@@ -369,6 +412,7 @@ object CallEngine {
|
|
|
369
412
|
finalCleanup(context)
|
|
370
413
|
}
|
|
371
414
|
|
|
415
|
+
updateLockScreenBypass()
|
|
372
416
|
emitEvent(CallEventType.CALL_ENDED, JSONObject().put("callId", callId))
|
|
373
417
|
notifyCallStateChanged(context)
|
|
374
418
|
}
|
|
@@ -380,30 +424,6 @@ object CallEngine {
|
|
|
380
424
|
stopForegroundService(context)
|
|
381
425
|
keepScreenAwake(context, false)
|
|
382
426
|
resetAudioMode(context)
|
|
383
|
-
|
|
384
|
-
// FIXED: Clear lock screen bypass when calls end
|
|
385
|
-
clearLockScreenBypass(context)
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// NEW: Function to clear lock screen bypass
|
|
389
|
-
private fun clearLockScreenBypass(context: Context) {
|
|
390
|
-
try {
|
|
391
|
-
if (context is Activity) {
|
|
392
|
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
|
393
|
-
context.setShowWhenLocked(false)
|
|
394
|
-
context.setTurnScreenOn(false)
|
|
395
|
-
} else {
|
|
396
|
-
context.window.clearFlags(
|
|
397
|
-
android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
|
|
398
|
-
android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
|
|
399
|
-
android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
|
|
400
|
-
)
|
|
401
|
-
}
|
|
402
|
-
Log.d(TAG, "Lock screen bypass flags cleared for Activity")
|
|
403
|
-
}
|
|
404
|
-
} catch (e: Exception) {
|
|
405
|
-
Log.w(TAG, "Could not clear lock screen bypass flags: ${e.message}")
|
|
406
|
-
}
|
|
407
427
|
}
|
|
408
428
|
|
|
409
429
|
fun getActiveCalls(): List<CallInfo> = activeCalls.values.toList()
|
|
@@ -491,25 +511,34 @@ object CallEngine {
|
|
|
491
511
|
context.stopService(intent)
|
|
492
512
|
}
|
|
493
513
|
|
|
514
|
+
// FIXED: Corrected bringAppToForeground method
|
|
494
515
|
fun bringAppToForeground(context: Context) {
|
|
495
516
|
val packageName = context.packageName
|
|
496
517
|
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
|
|
497
|
-
launchIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
|
518
|
+
launchIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP)
|
|
498
519
|
|
|
499
520
|
// Handle lock screen bypass for active calls
|
|
500
521
|
if (isCallActive()) {
|
|
501
|
-
// Only set lock screen bypass when bringing to foreground during active calls
|
|
502
522
|
launchIntent?.putExtra("BYPASS_LOCK_SCREEN", true)
|
|
503
|
-
|
|
523
|
+
launchIntent?.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
|
524
|
+
Log.d(TAG, "App brought to foreground with lock screen bypass request for active call")
|
|
504
525
|
} else {
|
|
526
|
+
launchIntent?.removeExtra("BYPASS_LOCK_SCREEN")
|
|
505
527
|
Log.d(TAG, "App brought to foreground without lock screen bypass")
|
|
506
528
|
}
|
|
507
529
|
|
|
508
|
-
|
|
509
|
-
|
|
530
|
+
try {
|
|
531
|
+
context.startActivity(launchIntent)
|
|
532
|
+
|
|
533
|
+
// Small delay to ensure activity is created before updating bypass
|
|
534
|
+
android.os.Handler(android.os.Looper.getMainLooper()).postDelayed({
|
|
535
|
+
updateLockScreenBypass()
|
|
536
|
+
}, 100)
|
|
510
537
|
|
|
511
|
-
|
|
512
|
-
|
|
538
|
+
} catch (e: Exception) {
|
|
539
|
+
Log.e(TAG, "Failed to bring app to foreground: ${e.message}")
|
|
540
|
+
}
|
|
541
|
+
}
|
|
513
542
|
|
|
514
543
|
// --- Audio Device Management ---
|
|
515
544
|
fun getAudioDevices(): AudioRoutesInfo {
|