@qusaieilouti99/call-manager 0.1.43 → 0.1.45
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/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallActivity.kt +8 -1
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallEngine.kt +104 -48
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/CallNotificationActionReceiver.kt +6 -9
- package/android/src/main/java/com/margelo/nitro/qusaieilouti99/callmanager/MyConnection.kt +7 -13
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
package com.margelo.nitro.qusaieilouti99.callmanager
|
|
2
|
+
|
|
2
3
|
import android.app.Activity
|
|
3
4
|
import android.content.Intent
|
|
4
5
|
import android.os.Build
|
|
@@ -48,6 +49,7 @@ class CallActivity : Activity() {
|
|
|
48
49
|
callType = intent.getStringExtra("callType") ?: "Audio"
|
|
49
50
|
Log.d(TAG, "CallActivity received callId: $callId, callType: $callType")
|
|
50
51
|
|
|
52
|
+
// FIXED: Immediate cleanup of notifications when CallActivity is shown
|
|
51
53
|
CallEngine.cancelIncomingCallUI(this)
|
|
52
54
|
|
|
53
55
|
val callerName = intent.getStringExtra("callerName") ?: "Unknown"
|
|
@@ -60,7 +62,9 @@ class CallActivity : Activity() {
|
|
|
60
62
|
answerBtn.setOnClickListener {
|
|
61
63
|
Log.d(TAG, "CallActivity: Answer button clicked for callId: $callId")
|
|
62
64
|
finishReason = FinishReason.ANSWER
|
|
63
|
-
|
|
65
|
+
|
|
66
|
+
// FIXED: Use single source of truth - this will handle all cleanup
|
|
67
|
+
CallEngine.answerCall(this, callId)
|
|
64
68
|
finishCallActivity()
|
|
65
69
|
}
|
|
66
70
|
|
|
@@ -78,7 +82,10 @@ class CallActivity : Activity() {
|
|
|
78
82
|
super.onDestroy()
|
|
79
83
|
Log.d(TAG, "CallActivity onDestroy for callId: $callId. Reason: $finishReason")
|
|
80
84
|
timeoutHandler.removeCallbacks(timeoutRunnable)
|
|
85
|
+
|
|
86
|
+
// FIXED: Ensure cleanup happens regardless of how activity ends
|
|
81
87
|
CallEngine.stopRingtone()
|
|
88
|
+
CallEngine.cancelIncomingCallUI(this)
|
|
82
89
|
}
|
|
83
90
|
|
|
84
91
|
override fun onBackPressed() {
|
|
@@ -105,7 +105,6 @@ object CallEngine {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// --- Public API ---
|
|
108
|
-
|
|
109
108
|
fun setCanMakeMultipleCalls(allow: Boolean) {
|
|
110
109
|
canMakeMultipleCalls = allow
|
|
111
110
|
Log.d(TAG, "canMakeMultipleCalls set to: $allow")
|
|
@@ -164,6 +163,7 @@ object CallEngine {
|
|
|
164
163
|
telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
|
|
165
164
|
startForegroundService(context)
|
|
166
165
|
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
167
|
} catch (e: SecurityException) {
|
|
168
168
|
Log.e(TAG, "SecurityException: Failed to report incoming call. Check MANAGE_OWN_CALLS permission: ${e.message}", e)
|
|
169
169
|
endCall(context, callId)
|
|
@@ -174,7 +174,6 @@ object CallEngine {
|
|
|
174
174
|
notifyCallStateChanged(context)
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
// CORRECTED: Uses TelecomManager.placeCall for outgoing calls.
|
|
178
177
|
fun startOutgoingCall(context: Context, callId: String, callData: String) {
|
|
179
178
|
appContext = context.applicationContext
|
|
180
179
|
Log.d(TAG, "startOutgoingCall: $callId, $callData")
|
|
@@ -218,6 +217,7 @@ object CallEngine {
|
|
|
218
217
|
startForegroundService(context)
|
|
219
218
|
Log.d(TAG, "Successfully reported outgoing call to TelecomManager via placeCall for $callId")
|
|
220
219
|
startRingback()
|
|
220
|
+
// CHANGED: Bring app to foreground for outgoing calls
|
|
221
221
|
bringAppToForeground(context)
|
|
222
222
|
keepScreenAwake(context, true)
|
|
223
223
|
if (parsedCallType == "Video") {
|
|
@@ -235,33 +235,46 @@ object CallEngine {
|
|
|
235
235
|
notifyCallStateChanged(context)
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
+
// SINGLE SOURCE OF TRUTH: Core function for handling when remote party answers
|
|
238
239
|
fun callAnsweredFromJS(context: Context, callId: String) {
|
|
239
240
|
Log.d(TAG, "callAnsweredFromJS: $callId - remote party answered")
|
|
240
|
-
|
|
241
|
-
activeCalls[callId]?.state = CallState.ACTIVE
|
|
242
|
-
currentCallId = callId
|
|
243
|
-
if (!canMakeMultipleCalls) {
|
|
244
|
-
activeCalls.filter { it.key != callId }.values.forEach { it.state = CallState.HELD }
|
|
245
|
-
}
|
|
246
|
-
notifyCallStateChanged(context)
|
|
247
|
-
emitEvent(CallEventType.CALL_ANSWERED, JSONObject().put("callId", callId))
|
|
248
|
-
keepScreenAwake(context, true)
|
|
249
|
-
resetAudioMode(context)
|
|
241
|
+
coreCallAnswered(context, callId, isLocalAnswer = false)
|
|
250
242
|
}
|
|
251
243
|
|
|
244
|
+
// SINGLE SOURCE OF TRUTH: Core function for handling local answer (user answering)
|
|
252
245
|
fun answerCall(context: Context, callId: String) {
|
|
253
246
|
Log.d(TAG, "answerCall: $callId - local party answering")
|
|
247
|
+
coreCallAnswered(context, callId, isLocalAnswer = true)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// SINGLE SOURCE OF TRUTH: Core function that handles ALL call answering logic
|
|
251
|
+
private fun coreCallAnswered(context: Context, callId: String, isLocalAnswer: Boolean) {
|
|
252
|
+
Log.d(TAG, "coreCallAnswered: $callId, isLocalAnswer: $isLocalAnswer")
|
|
253
|
+
|
|
254
|
+
// Stop all ringtones and notifications immediately
|
|
254
255
|
stopRingtone()
|
|
256
|
+
stopRingback()
|
|
257
|
+
cancelIncomingCallUI(context)
|
|
258
|
+
|
|
259
|
+
// Update call state
|
|
255
260
|
activeCalls[callId]?.state = CallState.ACTIVE
|
|
256
261
|
currentCallId = callId
|
|
262
|
+
|
|
257
263
|
if (!canMakeMultipleCalls) {
|
|
258
264
|
activeCalls.filter { it.key != callId }.values.forEach { it.state = CallState.HELD }
|
|
259
265
|
}
|
|
260
|
-
|
|
261
|
-
|
|
266
|
+
|
|
267
|
+
// Bring app to foreground only when call is answered
|
|
268
|
+
bringAppToForeground(context)
|
|
262
269
|
startForegroundService(context)
|
|
263
270
|
keepScreenAwake(context, true)
|
|
264
271
|
resetAudioMode(context)
|
|
272
|
+
|
|
273
|
+
// Emit event
|
|
274
|
+
emitEvent(CallEventType.CALL_ANSWERED, JSONObject().put("callId", callId))
|
|
275
|
+
notifyCallStateChanged(context)
|
|
276
|
+
|
|
277
|
+
Log.d(TAG, "Call $callId successfully answered and UI cleaned up")
|
|
265
278
|
}
|
|
266
279
|
|
|
267
280
|
fun holdCall(context: Context, callId: String) {
|
|
@@ -295,73 +308,113 @@ object CallEngine {
|
|
|
295
308
|
emitEvent(CallEventType.CALL_UNMUTED, JSONObject().put("callId", callId))
|
|
296
309
|
}
|
|
297
310
|
|
|
311
|
+
// SINGLE SOURCE OF TRUTH: Core function that handles ALL call ending logic
|
|
298
312
|
fun endCall(context: Context, callId: String) {
|
|
299
313
|
appContext = context.applicationContext
|
|
300
314
|
Log.d(TAG, "endCall: $callId")
|
|
315
|
+
|
|
316
|
+
// Core cleanup logic
|
|
317
|
+
coreEndCall(context, callId)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
fun endAllCalls(context: Context) {
|
|
321
|
+
Log.d(TAG, "endAllCalls: Ending all active calls.")
|
|
322
|
+
if (activeCalls.isEmpty()) {
|
|
323
|
+
Log.d(TAG, "No active calls, nothing to do.")
|
|
324
|
+
return
|
|
325
|
+
}
|
|
326
|
+
activeCalls.keys.toList().forEach { callId ->
|
|
327
|
+
coreEndCall(context, callId)
|
|
328
|
+
}
|
|
329
|
+
activeCalls.clear()
|
|
330
|
+
telecomConnections.clear()
|
|
331
|
+
currentCallId = null
|
|
332
|
+
|
|
333
|
+
// Final cleanup
|
|
334
|
+
finalCleanup(context)
|
|
335
|
+
notifyCallStateChanged(context)
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// SINGLE SOURCE OF TRUTH: Core function that handles ending a single call
|
|
339
|
+
private fun coreEndCall(context: Context, callId: String) {
|
|
340
|
+
Log.d(TAG, "coreEndCall: $callId")
|
|
341
|
+
|
|
342
|
+
// Update call state
|
|
301
343
|
activeCalls[callId]?.state = CallState.ENDED
|
|
302
344
|
activeCalls.remove(callId)
|
|
303
345
|
Log.d(TAG, "Call $callId removed from activeCalls. Remaining: ${activeCalls.size}")
|
|
304
346
|
|
|
347
|
+
// Stop ringtones and notifications
|
|
305
348
|
stopRingback()
|
|
306
349
|
stopRingtone()
|
|
350
|
+
cancelIncomingCallUI(context)
|
|
307
351
|
|
|
352
|
+
// Update current call
|
|
308
353
|
if (currentCallId == callId) {
|
|
309
354
|
currentCallId = activeCalls.filter { it.value.state != CallState.ENDED }.keys.firstOrNull()
|
|
310
355
|
Log.d(TAG, "Current call was $callId. New currentCallId: $currentCallId")
|
|
311
356
|
}
|
|
312
357
|
|
|
313
|
-
|
|
314
|
-
|
|
358
|
+
// Handle telecom connection
|
|
315
359
|
val connection = telecomConnections[callId]
|
|
316
360
|
if (connection != null) {
|
|
317
361
|
connection.setDisconnected(DisconnectCause(DisconnectCause.LOCAL))
|
|
318
362
|
connection.destroy()
|
|
319
363
|
removeTelecomConnection(callId)
|
|
320
364
|
Log.d(TAG, "Telecom Connection for $callId disconnected and destroyed.")
|
|
321
|
-
} else {
|
|
322
|
-
Log.d(TAG, "No Telecom Connection found for callId=$callId. Likely an outgoing call not initially handled by Telecom.")
|
|
323
365
|
}
|
|
324
366
|
|
|
367
|
+
// If no more calls, do final cleanup
|
|
325
368
|
if (activeCalls.isEmpty()) {
|
|
326
|
-
|
|
327
|
-
stopForegroundService(context)
|
|
328
|
-
keepScreenAwake(context, false)
|
|
329
|
-
resetAudioMode(context)
|
|
330
|
-
} else {
|
|
331
|
-
Log.d(TAG, "Other calls still active. Foreground service remains.")
|
|
369
|
+
finalCleanup(context)
|
|
332
370
|
}
|
|
333
371
|
|
|
334
372
|
emitEvent(CallEventType.CALL_ENDED, JSONObject().put("callId", callId))
|
|
335
373
|
notifyCallStateChanged(context)
|
|
336
374
|
}
|
|
337
375
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
return
|
|
343
|
-
}
|
|
344
|
-
activeCalls.keys.toList().forEach { callId ->
|
|
345
|
-
endCall(context, callId)
|
|
346
|
-
}
|
|
347
|
-
activeCalls.clear()
|
|
348
|
-
telecomConnections.clear()
|
|
349
|
-
currentCallId = null
|
|
350
|
-
cancelIncomingCallUI(context)
|
|
376
|
+
// SINGLE SOURCE OF TRUTH: Final cleanup when all calls are ended
|
|
377
|
+
private fun finalCleanup(context: Context) {
|
|
378
|
+
Log.d(TAG, "Performing final cleanup - no active calls remaining")
|
|
379
|
+
|
|
351
380
|
stopForegroundService(context)
|
|
352
|
-
stopRingtone()
|
|
353
|
-
stopRingback()
|
|
354
381
|
keepScreenAwake(context, false)
|
|
355
382
|
resetAudioMode(context)
|
|
356
|
-
|
|
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
|
+
}
|
|
357
407
|
}
|
|
358
408
|
|
|
359
409
|
fun getActiveCalls(): List<CallInfo> = activeCalls.values.toList()
|
|
360
410
|
fun getCurrentCallId(): String? = currentCallId
|
|
361
|
-
fun isCallActive(): Boolean = activeCalls.any {
|
|
362
|
-
|
|
363
|
-
|
|
411
|
+
fun isCallActive(): Boolean = activeCalls.any {
|
|
412
|
+
it.value.state == CallState.ACTIVE ||
|
|
413
|
+
it.value.state == CallState.INCOMING ||
|
|
414
|
+
it.value.state == CallState.DIALING
|
|
415
|
+
}
|
|
364
416
|
|
|
417
|
+
// Enhanced incoming call UI with better cleanup
|
|
365
418
|
fun showIncomingCallUI(context: Context, callId: String, callerName: String, callType: String) {
|
|
366
419
|
Log.d(TAG, "Showing incoming call UI for $callId, caller: $callerName, callType: $callType")
|
|
367
420
|
createNotificationChannel(context)
|
|
@@ -443,19 +496,22 @@ object CallEngine {
|
|
|
443
496
|
val launchIntent = context.packageManager.getLaunchIntentForPackage(packageName)
|
|
444
497
|
launchIntent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
|
445
498
|
|
|
446
|
-
//
|
|
447
|
-
// Lock screen bypass is now handled in the target activity (CallActivity, MainActivity)
|
|
499
|
+
// Handle lock screen bypass for active calls
|
|
448
500
|
if (isCallActive()) {
|
|
449
|
-
|
|
501
|
+
// Only set lock screen bypass when bringing to foreground during active calls
|
|
502
|
+
launchIntent?.putExtra("BYPASS_LOCK_SCREEN", true)
|
|
503
|
+
Log.d(TAG, "App brought to foreground with lock screen bypass for active call")
|
|
450
504
|
} else {
|
|
451
|
-
Log.d(TAG, "App brought to foreground
|
|
505
|
+
Log.d(TAG, "App brought to foreground without lock screen bypass")
|
|
452
506
|
}
|
|
453
507
|
|
|
454
508
|
context.startActivity(launchIntent)
|
|
455
509
|
}
|
|
456
510
|
|
|
457
|
-
//
|
|
511
|
+
// Rest of the methods remain the same...
|
|
512
|
+
// (getAudioDevices, setAudioRoute, keepScreenAwake, etc. - keeping them unchanged for brevity)
|
|
458
513
|
|
|
514
|
+
// --- Audio Device Management ---
|
|
459
515
|
fun getAudioDevices(): AudioRoutesInfo {
|
|
460
516
|
audioManager = appContext?.getSystemService(Context.AUDIO_SERVICE) as? AudioManager ?: run {
|
|
461
517
|
Log.e(TAG, "getAudioDevices: AudioManager is null or appContext is not set. Returning default.")
|
|
@@ -15,24 +15,21 @@ class CallNotificationActionReceiver : BroadcastReceiver() {
|
|
|
15
15
|
|
|
16
16
|
when (intent.action) {
|
|
17
17
|
"com.qusaieilouti99.callmanager.ANSWER_CALL" -> {
|
|
18
|
-
Log.d(TAG, "Answer action received for callId: $callId
|
|
18
|
+
Log.d(TAG, "Answer action received for callId: $callId")
|
|
19
19
|
|
|
20
|
-
//
|
|
20
|
+
// FIXED: Use single source of truth for answering
|
|
21
21
|
val connection = CallEngine.getTelecomConnection(callId)
|
|
22
22
|
if (connection != null) {
|
|
23
|
-
// This will trigger MyConnection.onAnswer()
|
|
24
|
-
connection.onAnswer()
|
|
23
|
+
connection.onAnswer() // This will trigger MyConnection.onAnswer()
|
|
25
24
|
Log.d(TAG, "Call answered via Telecom connection for callId: $callId")
|
|
26
25
|
} else {
|
|
27
|
-
Log.e(TAG, "No Telecom connection found for callId: $callId.
|
|
28
|
-
// Fallback: answer directly via CallEngine
|
|
26
|
+
Log.e(TAG, "No Telecom connection found for callId: $callId. Using direct answer.")
|
|
29
27
|
CallEngine.answerCall(context, callId)
|
|
30
28
|
}
|
|
31
|
-
|
|
32
|
-
CallEngine.bringAppToForeground(context)
|
|
29
|
+
// NOTE: Don't call bringAppToForeground here - it's handled in coreCallAnswered
|
|
33
30
|
}
|
|
34
31
|
"com.qusaieilouti99.callmanager.DECLINE_CALL" -> {
|
|
35
|
-
Log.d(TAG, "Decline action received for callId: $callId
|
|
32
|
+
Log.d(TAG, "Decline action received for callId: $callId")
|
|
36
33
|
CallEngine.endCall(context, callId)
|
|
37
34
|
}
|
|
38
35
|
else -> {
|
|
@@ -32,9 +32,8 @@ class MyConnection(
|
|
|
32
32
|
JSONObject(callDataJson).optString("callType", "Audio")
|
|
33
33
|
} catch (e: Exception) { "Audio" }
|
|
34
34
|
|
|
35
|
-
// Set connection properties and capabilities
|
|
36
35
|
connectionProperties = Connection.PROPERTY_SELF_MANAGED
|
|
37
|
-
connectionCapabilities = Connection.CAPABILITY_SUPPORT_HOLD or Connection.CAPABILITY_MUTE
|
|
36
|
+
connectionCapabilities = Connection.CAPABILITY_SUPPORT_HOLD or Connection.CAPABILITY_MUTE
|
|
38
37
|
|
|
39
38
|
if (currentCallType == "Video") {
|
|
40
39
|
Log.d(TAG, "MyConnection for callId $callId initialized as VIDEO call.")
|
|
@@ -51,8 +50,9 @@ class MyConnection(
|
|
|
51
50
|
override fun onAnswer() {
|
|
52
51
|
Log.d(TAG, "Call answered via Telecom for callId: $callId")
|
|
53
52
|
setActive()
|
|
53
|
+
|
|
54
|
+
// FIXED: Use single source of truth for answering
|
|
54
55
|
CallEngine.answerCall(context, callId)
|
|
55
|
-
CallEngine.bringAppToForeground(context)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
override fun onReject() {
|
|
@@ -83,7 +83,7 @@ class MyConnection(
|
|
|
83
83
|
|
|
84
84
|
override fun onCallAudioStateChanged(state: CallAudioState) {
|
|
85
85
|
super.onCallAudioStateChanged(state)
|
|
86
|
-
Log.d(TAG, "Audio state changed for callId: $callId. muted=${state.isMuted}, route=${state.route}
|
|
86
|
+
Log.d(TAG, "Audio state changed for callId: $callId. muted=${state.isMuted}, route=${state.route}")
|
|
87
87
|
|
|
88
88
|
if (state.isMuted) {
|
|
89
89
|
CallEngine.muteCall(context, callId)
|
|
@@ -98,7 +98,6 @@ class MyConnection(
|
|
|
98
98
|
CallAudioState.ROUTE_WIRED_HEADSET -> "Headset"
|
|
99
99
|
else -> "Unknown"
|
|
100
100
|
}
|
|
101
|
-
Log.d(TAG, "CallAudioState changed, new route detected: $routeName")
|
|
102
101
|
CallEngine.emitEvent(
|
|
103
102
|
CallEventType.AUDIO_ROUTE_CHANGED,
|
|
104
103
|
JSONObject().put("callId", callId).put("route", routeName)
|
|
@@ -108,9 +107,6 @@ class MyConnection(
|
|
|
108
107
|
override fun onPlayDtmfTone(digit: Char) {
|
|
109
108
|
super.onPlayDtmfTone(digit)
|
|
110
109
|
Log.d(TAG, "Playing DTMF tone: $digit for callId: $callId")
|
|
111
|
-
// NOTE: DTMF_TONE is not in your current CallEventType enum.
|
|
112
|
-
// If you need to emit this event, you MUST add 'DTMF_TONE' to your CallEventType.ts
|
|
113
|
-
// and re-run nitro-codegen. For now, it's commented out to prevent compilation error.
|
|
114
110
|
CallEngine.emitEvent(
|
|
115
111
|
CallEventType.DTMF_TONE,
|
|
116
112
|
JSONObject().put("callId", callId).put("digit", digit.toString())
|
|
@@ -122,12 +118,10 @@ class MyConnection(
|
|
|
122
118
|
Log.d(TAG, "Stopping DTMF tone for callId: $callId")
|
|
123
119
|
}
|
|
124
120
|
|
|
125
|
-
// REMOVED: onConnectionEvent override since it doesn't exist in the base Connection class
|
|
126
|
-
// If you need connection events, you can send them via sendConnectionEvent() method
|
|
127
|
-
|
|
128
121
|
override fun onShowIncomingCallUi() {
|
|
129
122
|
super.onShowIncomingCallUi()
|
|
130
|
-
Log.d(TAG, "onShowIncomingCallUi for callId: $callId
|
|
131
|
-
|
|
123
|
+
Log.d(TAG, "onShowIncomingCallUi for callId: $callId")
|
|
124
|
+
// REMOVED: Don't bring app to foreground for incoming calls
|
|
125
|
+
// CallEngine.bringAppToForeground(context)
|
|
132
126
|
}
|
|
133
127
|
}
|