@qusaieilouti99/call-manager 0.1.94 → 0.1.96

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,181 +1,574 @@
1
- // File: CallActivity.kt
1
+ // CallActivity.kt
2
2
  package com.margelo.nitro.qusaieilouti99.callmanager
3
3
 
4
+ import android.animation.ObjectAnimator
5
+ import android.animation.ValueAnimator
4
6
  import android.app.Activity
5
7
  import android.app.KeyguardManager
6
8
  import android.content.Context
9
+ import android.graphics.*
10
+ import android.graphics.drawable.GradientDrawable
7
11
  import android.os.Build
8
12
  import android.os.Bundle
9
13
  import android.os.Handler
10
14
  import android.os.Looper
11
15
  import android.util.Log
16
+ import android.util.TypedValue
17
+ import android.view.View
18
+ import android.view.ViewGroup
12
19
  import android.view.WindowManager
13
- import android.widget.Button
14
- import android.widget.TextView
15
-
16
- /**
17
- * Full‐screen incoming‐call UI. Implements CallEndListener
18
- * so it always auto‐finishes when the engine ends the call.
19
- * **SAMSUNG FIX**: Enhanced lock screen bypass for Samsung devices
20
- */
20
+ import android.view.animation.LinearInterpolator
21
+ import android.widget.*
22
+ import androidx.core.content.ContextCompat
23
+ import androidx.core.view.ViewCompat
24
+
21
25
  class CallActivity : Activity(), CallEngine.CallEndListener {
22
26
 
23
- private enum class FinishReason {
24
- ANSWER, DECLINE, TIMEOUT, MANUAL_DISMISS, EXTERNAL_END
25
- }
26
-
27
- private var finishReason: FinishReason? = null
28
- private var callId: String = ""
29
- private var callType: String = "Audio"
30
-
31
- private val timeoutHandler = Handler(Looper.getMainLooper())
32
- private val timeoutRunnable = Runnable {
33
- Log.d(TAG, "CallActivity timeout triggered for callId: $callId")
34
- finishReason = FinishReason.TIMEOUT
35
- CallEngine.stopRingtone()
36
- CallEngine.cancelIncomingCallUI()
37
- CallEngine.endCall(callId)
38
- finishCallActivity()
39
- }
40
-
41
- override fun onCreate(savedInstanceState: Bundle?) {
42
- super.onCreate(savedInstanceState)
43
- Log.d(TAG, "CallActivity onCreate")
44
-
45
- // **SAMSUNG FIX**: Enhanced lock screen bypass
46
- val isSamsungLockScreenBypass = intent.getBooleanExtra("SAMSUNG_LOCK_SCREEN_BYPASS", false)
47
- setupLockScreenBypass(isSamsungLockScreenBypass)
48
-
49
- setContentView(R.layout.activity_call)
50
-
51
- // Read incoming‐call params
52
- callId = intent.getStringExtra("callId") ?: ""
53
- callType = intent.getStringExtra("callType") ?: "Audio"
54
- Log.d(TAG, "CallActivity received callId: $callId, callType: $callType")
55
-
56
- // Register for call‐end callbacks BEFORE timeout or user can dismiss
57
- CallEngine.registerCallEndListener(this)
58
-
59
- // Bind UI
60
- val callerName = intent.getStringExtra("callerName") ?: "Unknown"
61
- findViewById<TextView>(R.id.caller_name).text = callerName
62
-
63
- findViewById<Button>(R.id.answer_btn).setOnClickListener {
64
- Log.d(TAG, "Answer clicked for callId: $callId")
65
- finishReason = FinishReason.ANSWER
66
- CallEngine.stopRingtone()
67
- CallEngine.cancelIncomingCallUI()
68
- CallEngine.answerCall(callId)
69
- finishCallActivity()
70
- }
71
-
72
- findViewById<Button>(R.id.decline_btn).setOnClickListener {
73
- Log.d(TAG, "Decline clicked for callId: $callId")
74
- finishReason = FinishReason.DECLINE
75
- CallEngine.stopRingtone()
76
- CallEngine.cancelIncomingCallUI()
77
- CallEngine.endCall(callId)
78
- finishCallActivity()
79
- }
80
-
81
- // Start autotimeout
82
- timeoutHandler.postDelayed(timeoutRunnable, 60_000)
83
- Log.d(TAG, "CallActivity setup complete")
84
- }
85
-
86
- // **SAMSUNG FIX**: Enhanced lock screen bypass method
87
- private fun setupLockScreenBypass(isSamsungSpecific: Boolean) {
88
- // Standard lock screen bypass
89
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
90
- setShowWhenLocked(true)
91
- setTurnScreenOn(true)
92
- } else {
93
- window.addFlags(
94
- WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
95
- WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
96
- WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
97
- )
98
- }
99
-
100
- // **SAMSUNG SPECIFIC**: Additional fixes for Samsung One UI 7
101
- if (isSamsungSpecific) {
102
- Log.d(TAG, "Applying Samsung-specific lock screen bypass")
103
-
104
- // **CRITICAL**: Samsung requires BOTH new and old methods
105
- window.addFlags(
106
- WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
107
- WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
108
- WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
109
- WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
110
- )
111
-
112
- // **SAMSUNG SPECIFIC**: Request keyguard dismissal
113
- val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
114
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
115
- keyguardManager.requestDismissKeyguard(this, object : KeyguardManager.KeyguardDismissCallback() {
116
- override fun onDismissSucceeded() {
117
- Log.d(TAG, "Samsung keyguard dismissed successfully")
118
- }
119
- override fun onDismissError() {
120
- Log.w(TAG, "Samsung keyguard dismissal failed")
121
- }
122
- })
123
- }
124
- }
125
- }
126
-
127
- override fun onDestroy() {
128
- super.onDestroy()
129
- Log.d(TAG, "CallActivity onDestroy for callId: $callId. Reason: $finishReason")
130
-
131
- // Unregister listener
132
- CallEngine.unregisterCallEndListener(this)
133
-
134
- // Cancel timeout
135
- timeoutHandler.removeCallbacks(timeoutRunnable)
136
-
137
- // If user never answered, clean up ringtone/UI
138
- if (finishReason != FinishReason.ANSWER) {
139
- CallEngine.stopRingtone()
140
- CallEngine.cancelIncomingCallUI()
141
- }
142
- }
143
-
144
- override fun onBackPressed() {
145
- Log.d(TAG, "onBackPressed for callId: $callId → treat as decline")
146
- finishReason = FinishReason.MANUAL_DISMISS
147
- CallEngine.stopRingtone()
148
- CallEngine.cancelIncomingCallUI()
149
- CallEngine.endCall(callId)
150
- finishCallActivity()
151
- }
152
-
153
- /**
154
- * Called by CallEngine whenever ANY call ends.
155
- * We only care about our own callId.
156
- */
157
- override fun onCallEnded(endedCallId: String) {
158
- if (endedCallId == callId && !isFinishing) {
159
- Log.d(TAG, "CallActivity onCallEnded callback for callId: $callId")
160
- finishReason = FinishReason.EXTERNAL_END
161
- runOnUiThread { finishCallActivity() }
162
- }
163
- }
164
-
165
- private fun finishCallActivity() {
166
- if (isFinishing) {
167
- Log.d(TAG, "Already finishing, skip.")
168
- return
169
- }
170
- Log.d(TAG, "Finishing CallActivity for callId: $callId")
171
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
172
- finishAndRemoveTask()
173
- } else {
174
- finish()
175
- }
176
- }
177
-
178
- companion object {
179
- private const val TAG = "CallActivity"
180
- }
27
+ private enum class FinishReason {
28
+ ANSWER, DECLINE, TIMEOUT, MANUAL_DISMISS, EXTERNAL_END
29
+ }
30
+
31
+ private var finishReason: FinishReason? = null
32
+ private var callId: String = ""
33
+ private var callType: String = "Audio"
34
+
35
+ // UI Components
36
+ private lateinit var rootContainer: FrameLayout
37
+ private lateinit var callerNameText: TextView
38
+ private lateinit var profileImage: ImageView
39
+ private lateinit var answerButton: FrameLayout
40
+ private lateinit var declineButton: FrameLayout
41
+
42
+ // Animation
43
+ private var pulseAnimator: ValueAnimator? = null
44
+
45
+ private val timeoutHandler = Handler(Looper.getMainLooper())
46
+ private val timeoutRunnable = Runnable {
47
+ Log.d(TAG, "CallActivity timeout triggered for callId: $callId")
48
+ finishReason = FinishReason.TIMEOUT
49
+ CallEngine.stopRingtone()
50
+ CallEngine.cancelIncomingCallUI()
51
+ CallEngine.endCall(callId)
52
+ finishCallActivity()
53
+ }
54
+
55
+ override fun onCreate(savedInstanceState: Bundle?) {
56
+ super.onCreate(savedInstanceState)
57
+ Log.d(TAG, "CallActivity onCreate")
58
+
59
+ // Enhanced lock screen bypass
60
+ val isSamsungLockScreenBypass = intent.getBooleanExtra("SAMSUNG_LOCK_SCREEN_BYPASS", false)
61
+ setupLockScreenBypass(isSamsungLockScreenBypass)
62
+
63
+ // Create UI programmatically
64
+ createProfessionalUI()
65
+
66
+ // Read incoming call params
67
+ callId = intent.getStringExtra("callId") ?: ""
68
+ callType = intent.getStringExtra("callType") ?: "Audio"
69
+ Log.d(TAG, "CallActivity received callId: $callId, callType: $callType")
70
+
71
+ // Register for call-end callbacks
72
+ CallEngine.registerCallEndListener(this)
73
+
74
+ // Bind data
75
+ val callerName = intent.getStringExtra("callerName") ?: "Unknown"
76
+ val callerAvatar = intent.getStringExtra("callerAvatar")
77
+ updateCallerInfo(callerName, callerAvatar)
78
+
79
+ // Setup button listeners
80
+ setupButtonListeners()
81
+
82
+ // Start animations
83
+ startPulseAnimation()
84
+
85
+ // Start auto-timeout
86
+ timeoutHandler.postDelayed(timeoutRunnable, 60_000)
87
+ Log.d(TAG, "CallActivity setup complete")
88
+ }
89
+
90
+ private fun createProfessionalUI() {
91
+ // Root container with gradient background
92
+ rootContainer = FrameLayout(this).apply {
93
+ layoutParams = ViewGroup.LayoutParams(
94
+ ViewGroup.LayoutParams.MATCH_PARENT,
95
+ ViewGroup.LayoutParams.MATCH_PARENT
96
+ )
97
+ background = createGradientBackground()
98
+ }
99
+
100
+ // Main content container
101
+ val contentContainer = LinearLayout(this).apply {
102
+ layoutParams = FrameLayout.LayoutParams(
103
+ FrameLayout.LayoutParams.MATCH_PARENT,
104
+ FrameLayout.LayoutParams.MATCH_PARENT
105
+ ).apply {
106
+ setPadding(dp(32), dp(80), dp(32), dp(48))
107
+ }
108
+ orientation = LinearLayout.VERTICAL
109
+ gravity = android.view.Gravity.CENTER_HORIZONTAL
110
+ }
111
+
112
+ // Add components to content container
113
+ contentContainer.addView(createCallTypeIndicator())
114
+ contentContainer.addView(createSpacerView(40))
115
+ contentContainer.addView(createProfileSection())
116
+ contentContainer.addView(createSpacerView(32))
117
+ contentContainer.addView(createCallerInfoSection())
118
+ contentContainer.addView(createFlexibleSpacerView())
119
+ contentContainer.addView(createQuickActionsSection())
120
+ contentContainer.addView(createSpacerView(32))
121
+ contentContainer.addView(createCallActionsSection())
122
+
123
+ rootContainer.addView(contentContainer)
124
+ setContentView(rootContainer)
125
+ }
126
+
127
+ private fun createGradientBackground(): GradientDrawable {
128
+ return GradientDrawable().apply {
129
+ colors = intArrayOf(
130
+ Color.parseColor("#FF0D1B2A"),
131
+ Color.parseColor("#FF1B263B"),
132
+ Color.parseColor("#FF415A77")
133
+ )
134
+ orientation = GradientDrawable.Orientation.TL_BR
135
+ gradientType = GradientDrawable.LINEAR_GRADIENT
136
+ }
137
+ }
138
+
139
+ private fun createCallTypeIndicator(): LinearLayout {
140
+ return LinearLayout(this).apply {
141
+ layoutParams = LinearLayout.LayoutParams(
142
+ LinearLayout.LayoutParams.WRAP_CONTENT,
143
+ LinearLayout.LayoutParams.WRAP_CONTENT
144
+ )
145
+ orientation = LinearLayout.HORIZONTAL
146
+ gravity = android.view.Gravity.CENTER_VERTICAL
147
+
148
+ // Call icon
149
+ addView(createCallIcon())
150
+
151
+ // Call type text
152
+ addView(TextView(this@CallActivity).apply {
153
+ layoutParams = LinearLayout.LayoutParams(
154
+ LinearLayout.LayoutParams.WRAP_CONTENT,
155
+ LinearLayout.LayoutParams.WRAP_CONTENT
156
+ ).apply { setMargins(dp(8), 0, 0, 0) }
157
+ text = "Incoming call"
158
+ setTextColor(Color.parseColor("#80FFFFFF"))
159
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
160
+ typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
161
+ })
162
+ }
163
+ }
164
+
165
+ private fun createCallIcon(): View {
166
+ return View(this).apply {
167
+ layoutParams = LinearLayout.LayoutParams(dp(18), dp(18))
168
+ background = GradientDrawable().apply {
169
+ shape = GradientDrawable.OVAL
170
+ setColor(Color.parseColor("#80FFFFFF"))
171
+ }
172
+ }
173
+ }
174
+
175
+ private fun createProfileSection(): FrameLayout {
176
+ return FrameLayout(this).apply {
177
+ layoutParams = LinearLayout.LayoutParams(dp(180), dp(180))
178
+
179
+ // Outer pulse ring
180
+ addView(createPulseRing(180, "#30FFFFFF"))
181
+
182
+ // Middle pulse ring
183
+ addView(createPulseRing(160, "#40FFFFFF"))
184
+
185
+ // Profile container
186
+ val profileContainer = FrameLayout(this@CallActivity).apply {
187
+ layoutParams = FrameLayout.LayoutParams(dp(140), dp(140)).apply {
188
+ gravity = android.view.Gravity.CENTER
189
+ }
190
+ }
191
+
192
+ // Profile background
193
+ profileContainer.addView(View(this@CallActivity).apply {
194
+ layoutParams = FrameLayout.LayoutParams(dp(140), dp(140))
195
+ background = GradientDrawable().apply {
196
+ shape = GradientDrawable.OVAL
197
+ setColor(Color.WHITE)
198
+ setStroke(dp(3), Color.parseColor("#E0E0E0"))
199
+ }
200
+ })
201
+
202
+ // Profile image
203
+ profileImage = ImageView(this@CallActivity).apply {
204
+ layoutParams = FrameLayout.LayoutParams(dp(136), dp(136)).apply {
205
+ gravity = android.view.Gravity.CENTER
206
+ }
207
+ scaleType = ImageView.ScaleType.CENTER_CROP
208
+ background = GradientDrawable().apply {
209
+ shape = GradientDrawable.OVAL
210
+ setColor(Color.parseColor("#F5F5F5"))
211
+ }
212
+ }
213
+ profileContainer.addView(profileImage)
214
+
215
+ addView(profileContainer)
216
+ }
217
+ }
218
+
219
+ private fun createPulseRing(size: Int, color: String): View {
220
+ return View(this).apply {
221
+ layoutParams = FrameLayout.LayoutParams(dp(size), dp(size)).apply {
222
+ gravity = android.view.Gravity.CENTER
223
+ }
224
+ background = GradientDrawable().apply {
225
+ shape = GradientDrawable.OVAL
226
+ setStroke(dp(2), Color.parseColor(color))
227
+ setColor(Color.parseColor("#10FFFFFF"))
228
+ }
229
+ }
230
+ }
231
+
232
+ private fun createCallerInfoSection(): LinearLayout {
233
+ return LinearLayout(this).apply {
234
+ layoutParams = LinearLayout.LayoutParams(
235
+ LinearLayout.LayoutParams.WRAP_CONTENT,
236
+ LinearLayout.LayoutParams.WRAP_CONTENT
237
+ )
238
+ orientation = LinearLayout.VERTICAL
239
+ gravity = android.view.Gravity.CENTER
240
+
241
+ // Caller name
242
+ callerNameText = TextView(this@CallActivity).apply {
243
+ layoutParams = LinearLayout.LayoutParams(
244
+ LinearLayout.LayoutParams.WRAP_CONTENT,
245
+ LinearLayout.LayoutParams.WRAP_CONTENT
246
+ ).apply { setMargins(0, 0, 0, dp(4)) }
247
+ text = "John Doe" // Default, will be updated
248
+ setTextColor(Color.WHITE)
249
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 32f)
250
+ typeface = Typeface.create(Typeface.DEFAULT, Typeface.NORMAL)
251
+ gravity = android.view.Gravity.CENTER
252
+ }
253
+ addView(callerNameText)
254
+
255
+ // Caller number/label
256
+ addView(TextView(this@CallActivity).apply {
257
+ layoutParams = LinearLayout.LayoutParams(
258
+ LinearLayout.LayoutParams.WRAP_CONTENT,
259
+ LinearLayout.LayoutParams.WRAP_CONTENT
260
+ )
261
+ text = "Mobile"
262
+ setTextColor(Color.parseColor("#B3FFFFFF"))
263
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
264
+ gravity = android.view.Gravity.CENTER
265
+ })
266
+ }
267
+ }
268
+
269
+ private fun createQuickActionsSection(): LinearLayout {
270
+ return LinearLayout(this).apply {
271
+ layoutParams = LinearLayout.LayoutParams(
272
+ LinearLayout.LayoutParams.MATCH_PARENT,
273
+ LinearLayout.LayoutParams.WRAP_CONTENT
274
+ )
275
+ orientation = LinearLayout.HORIZONTAL
276
+ gravity = android.view.Gravity.CENTER
277
+
278
+ // Remind me action
279
+ addView(createQuickAction("⏰", "Remind me").apply {
280
+ layoutParams = (layoutParams as LinearLayout.LayoutParams).apply {
281
+ setMargins(0, 0, dp(40), 0)
282
+ }
283
+ })
284
+
285
+ // Message action
286
+ addView(createQuickAction("💬", "Message"))
287
+ }
288
+ }
289
+
290
+ private fun createQuickAction(icon: String, label: String): LinearLayout {
291
+ return LinearLayout(this).apply {
292
+ layoutParams = LinearLayout.LayoutParams(
293
+ LinearLayout.LayoutParams.WRAP_CONTENT,
294
+ LinearLayout.LayoutParams.WRAP_CONTENT
295
+ )
296
+ orientation = LinearLayout.VERTICAL
297
+ gravity = android.view.Gravity.CENTER
298
+ setPadding(dp(12), dp(12), dp(12), dp(12))
299
+ background = createQuickActionBackground()
300
+ isClickable = true
301
+
302
+ // Icon
303
+ addView(TextView(this@CallActivity).apply {
304
+ layoutParams = LinearLayout.LayoutParams(
305
+ LinearLayout.LayoutParams.WRAP_CONTENT,
306
+ LinearLayout.LayoutParams.WRAP_CONTENT
307
+ ).apply { setMargins(0, 0, 0, dp(4)) }
308
+ text = icon
309
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 24f)
310
+ })
311
+
312
+ // Label
313
+ addView(TextView(this@CallActivity).apply {
314
+ layoutParams = LinearLayout.LayoutParams(
315
+ LinearLayout.LayoutParams.WRAP_CONTENT,
316
+ LinearLayout.LayoutParams.WRAP_CONTENT
317
+ )
318
+ text = label
319
+ setTextColor(Color.WHITE)
320
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 12f)
321
+ })
322
+ }
323
+ }
324
+
325
+ private fun createQuickActionBackground(): GradientDrawable {
326
+ return GradientDrawable().apply {
327
+ shape = GradientDrawable.RECTANGLE
328
+ cornerRadius = dp(16).toFloat()
329
+ setColor(Color.parseColor("#20FFFFFF"))
330
+ setStroke(dp(1), Color.parseColor("#30FFFFFF"))
331
+ }
332
+ }
333
+
334
+ private fun createCallActionsSection(): LinearLayout {
335
+ return LinearLayout(this).apply {
336
+ layoutParams = LinearLayout.LayoutParams(
337
+ LinearLayout.LayoutParams.MATCH_PARENT,
338
+ LinearLayout.LayoutParams.WRAP_CONTENT
339
+ )
340
+ orientation = LinearLayout.HORIZONTAL
341
+ gravity = android.view.Gravity.CENTER
342
+
343
+ // Decline button
344
+ declineButton = createCallActionButton("✕", "#FFF44336", "#FFD32F2F").apply {
345
+ layoutParams = LinearLayout.LayoutParams(dp(80), dp(80)).apply {
346
+ setMargins(0, 0, dp(80), 0)
347
+ }
348
+ }
349
+ addView(declineButton)
350
+
351
+ // Answer button
352
+ answerButton = createCallActionButton("✓", "#FF4CAF50", "#FF388E3C").apply {
353
+ layoutParams = LinearLayout.LayoutParams(dp(80), dp(80))
354
+ }
355
+ addView(answerButton)
356
+ }
357
+ }
358
+
359
+ private fun createCallActionButton(text: String, startColor: String, endColor: String): FrameLayout {
360
+ return FrameLayout(this).apply {
361
+ // Shadow
362
+ addView(View(this@CallActivity).apply {
363
+ layoutParams = FrameLayout.LayoutParams(dp(80), dp(80))
364
+ background = GradientDrawable().apply {
365
+ shape = GradientDrawable.OVAL
366
+ colors = intArrayOf(
367
+ Color.parseColor("#40000000"),
368
+ Color.parseColor("#20000000"),
369
+ Color.parseColor("#00000000")
370
+ )
371
+ gradientType = GradientDrawable.RADIAL_GRADIENT
372
+ gradientRadius = dp(40).toFloat()
373
+ }
374
+ })
375
+
376
+ // Button
377
+ addView(TextView(this@CallActivity).apply {
378
+ layoutParams = FrameLayout.LayoutParams(dp(72), dp(72)).apply {
379
+ gravity = android.view.Gravity.CENTER
380
+ }
381
+ this.text = text
382
+ setTextColor(Color.WHITE)
383
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 28f)
384
+ typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
385
+ gravity = android.view.Gravity.CENTER
386
+ background = GradientDrawable().apply {
387
+ shape = GradientDrawable.OVAL
388
+ colors = intArrayOf(
389
+ Color.parseColor(startColor),
390
+ Color.parseColor(endColor)
391
+ )
392
+ orientation = GradientDrawable.Orientation.TL_BR
393
+ setStroke(dp(2), Color.parseColor("#FF66BB6A"))
394
+ }
395
+ isClickable = true
396
+ })
397
+ }
398
+ }
399
+
400
+ private fun createSpacerView(heightDp: Int): View {
401
+ return View(this).apply {
402
+ layoutParams = LinearLayout.LayoutParams(
403
+ LinearLayout.LayoutParams.MATCH_PARENT,
404
+ dp(heightDp)
405
+ )
406
+ }
407
+ }
408
+
409
+ private fun createFlexibleSpacerView(): View {
410
+ return View(this).apply {
411
+ layoutParams = LinearLayout.LayoutParams(
412
+ LinearLayout.LayoutParams.MATCH_PARENT,
413
+ 0,
414
+ 1f
415
+ )
416
+ }
417
+ }
418
+
419
+ private fun updateCallerInfo(callerName: String, callerAvatar: String?) {
420
+ callerNameText.text = callerName
421
+
422
+ if (callerAvatar != null && callerAvatar.isNotEmpty()) {
423
+ // Load profile picture (you can use Glide, Picasso, or similar)
424
+ // For now, just show default
425
+ profileImage.setImageResource(android.R.drawable.sym_def_app_icon)
426
+ } else {
427
+ // Create default avatar with initials
428
+ profileImage.setImageDrawable(createDefaultAvatar(callerName))
429
+ }
430
+ }
431
+
432
+ private fun createDefaultAvatar(name: String): GradientDrawable {
433
+ return GradientDrawable().apply {
434
+ shape = GradientDrawable.OVAL
435
+ setColor(Color.parseColor("#FF4CAF50"))
436
+ }
437
+ }
438
+
439
+ private fun setupButtonListeners() {
440
+ answerButton.setOnClickListener {
441
+ Log.d(TAG, "Answer clicked for callId: $callId")
442
+ finishReason = FinishReason.ANSWER
443
+ CallEngine.stopRingtone()
444
+ CallEngine.cancelIncomingCallUI()
445
+ CallEngine.answerCall(callId)
446
+ finishCallActivity()
447
+ }
448
+
449
+ declineButton.setOnClickListener {
450
+ Log.d(TAG, "Decline clicked for callId: $callId")
451
+ finishReason = FinishReason.DECLINE
452
+ CallEngine.stopRingtone()
453
+ CallEngine.cancelIncomingCallUI()
454
+ CallEngine.endCall(callId)
455
+ finishCallActivity()
456
+ }
457
+ }
458
+
459
+ private fun startPulseAnimation() {
460
+ pulseAnimator = ValueAnimator.ofFloat(1f, 1.2f).apply {
461
+ duration = 1500
462
+ repeatCount = ValueAnimator.INFINITE
463
+ repeatMode = ValueAnimator.REVERSE
464
+ interpolator = LinearInterpolator()
465
+
466
+ addUpdateListener { animation ->
467
+ val scale = animation.animatedValue as Float
468
+ // Apply to pulse rings (you can get references to them if needed)
469
+ // For now, this is a placeholder for the animation logic
470
+ }
471
+ }
472
+ pulseAnimator?.start()
473
+ }
474
+
475
+ private fun setupLockScreenBypass(isSamsungSpecific: Boolean) {
476
+ // Standard lock screen bypass
477
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
478
+ setShowWhenLocked(true)
479
+ setTurnScreenOn(true)
480
+ } else {
481
+ window.addFlags(
482
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
483
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
484
+ WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
485
+ )
486
+ }
487
+
488
+ // Samsung specific fixes
489
+ if (isSamsungSpecific) {
490
+ Log.d(TAG, "Applying Samsung-specific lock screen bypass")
491
+ window.addFlags(
492
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
493
+ WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
494
+ WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
495
+ WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
496
+ )
497
+
498
+ val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
499
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
500
+ keyguardManager.requestDismissKeyguard(this, object : KeyguardManager.KeyguardDismissCallback() {
501
+ override fun onDismissSucceeded() {
502
+ Log.d(TAG, "Samsung keyguard dismissed successfully")
503
+ }
504
+ override fun onDismissError() {
505
+ Log.w(TAG, "Samsung keyguard dismissal failed")
506
+ }
507
+ })
508
+ }
509
+ }
510
+ }
511
+
512
+ // Helper function to convert dp to pixels
513
+ private fun dp(value: Int): Int {
514
+ return TypedValue.applyDimension(
515
+ TypedValue.COMPLEX_UNIT_DIP,
516
+ value.toFloat(),
517
+ resources.displayMetrics
518
+ ).toInt()
519
+ }
520
+
521
+ override fun onDestroy() {
522
+ super.onDestroy()
523
+ Log.d(TAG, "CallActivity onDestroy for callId: $callId. Reason: $finishReason")
524
+
525
+ // Stop animations
526
+ pulseAnimator?.cancel()
527
+
528
+ // Unregister listener
529
+ CallEngine.unregisterCallEndListener(this)
530
+
531
+ // Cancel timeout
532
+ timeoutHandler.removeCallbacks(timeoutRunnable)
533
+
534
+ // If user never answered, clean up ringtone/UI
535
+ if (finishReason != FinishReason.ANSWER) {
536
+ CallEngine.stopRingtone()
537
+ CallEngine.cancelIncomingCallUI()
538
+ }
539
+ }
540
+
541
+ override fun onBackPressed() {
542
+ Log.d(TAG, "onBackPressed for callId: $callId → treat as decline")
543
+ finishReason = FinishReason.MANUAL_DISMISS
544
+ CallEngine.stopRingtone()
545
+ CallEngine.cancelIncomingCallUI()
546
+ CallEngine.endCall(callId)
547
+ finishCallActivity()
548
+ }
549
+
550
+ override fun onCallEnded(endedCallId: String) {
551
+ if (endedCallId == callId && !isFinishing) {
552
+ Log.d(TAG, "CallActivity onCallEnded callback for callId: $callId")
553
+ finishReason = FinishReason.EXTERNAL_END
554
+ runOnUiThread { finishCallActivity() }
555
+ }
556
+ }
557
+
558
+ private fun finishCallActivity() {
559
+ if (isFinishing) {
560
+ Log.d(TAG, "Already finishing, skip.")
561
+ return
562
+ }
563
+ Log.d(TAG, "Finishing CallActivity for callId: $callId")
564
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
565
+ finishAndRemoveTask()
566
+ } else {
567
+ finish()
568
+ }
569
+ }
570
+
571
+ companion object {
572
+ private const val TAG = "CallActivity"
573
+ }
181
574
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qusaieilouti99/call-manager",
3
- "version": "0.1.94",
3
+ "version": "0.1.96",
4
4
  "description": "Call manager",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -1,12 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <ripple xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:color="#30FFFFFF">
4
- <item>
5
- <shape android:shape="oval">
6
- <solid android:color="#40FFFFFF" />
7
- <stroke
8
- android:width="2dp"
9
- android:color="#60FFFFFF" />
10
- </shape>
11
- </item>
12
- </ripple>
@@ -1,15 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <ripple xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:color="#40FFFFFF">
4
- <item>
5
- <shape android:shape="oval">
6
- <gradient
7
- android:startColor="#4CAF50"
8
- android:endColor="#388E3C"
9
- android:angle="135" />
10
- <stroke
11
- android:width="3dp"
12
- android:color="#66BB6A" />
13
- </shape>
14
- </item>
15
- </ripple>
@@ -1,10 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <shape xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:shape="oval">
4
- <gradient
5
- android:startColor="#40000000"
6
- android:centerColor="#20000000"
7
- android:endColor="#00000000"
8
- android:type="radial"
9
- android:gradientRadius="40dp" />
10
- </shape>
@@ -1,8 +0,0 @@
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="#FFFFFF" />
5
- <stroke
6
- android:width="4dp"
7
- android:color="#40FFFFFF" />
8
- </shape>
@@ -1,10 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <shape xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:shape="oval">
4
- <gradient
5
- android:startColor="#40FFFFFF"
6
- android:centerColor="#20FFFFFF"
7
- android:endColor="#00FFFFFF"
8
- android:type="radial"
9
- android:gradientRadius="70dp" />
10
- </shape>
@@ -1,24 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
3
- <!-- Base gradient -->
4
- <item>
5
- <shape>
6
- <gradient
7
- android:startColor="#1a237e"
8
- android:centerColor="#283593"
9
- android:endColor="#3949ab"
10
- android:angle="135" />
11
- </shape>
12
- </item>
13
-
14
- <!-- Abstract pattern overlay -->
15
- <item>
16
- <shape>
17
- <gradient
18
- android:startColor="#20FFFFFF"
19
- android:centerColor="#10FFFFFF"
20
- android:endColor="#05FFFFFF"
21
- android:angle="45" />
22
- </shape>
23
- </item>
24
- </layer-list>
@@ -1,15 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <ripple xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:color="#40FFFFFF">
4
- <item>
5
- <shape android:shape="oval">
6
- <gradient
7
- android:startColor="#F44336"
8
- android:endColor="#D32F2F"
9
- android:angle="135" />
10
- <stroke
11
- android:width="3dp"
12
- android:color="#EF5350" />
13
- </shape>
14
- </item>
15
- </ripple>
@@ -1,10 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <shape xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:shape="oval">
4
- <gradient
5
- android:startColor="#40000000"
6
- android:centerColor="#20000000"
7
- android:endColor="#00000000"
8
- android:type="radial"
9
- android:gradientRadius="40dp" />
10
- </shape>
@@ -1,8 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <shape xmlns:android="http://schemas.android.com/apk/res/android">
3
- <gradient
4
- android:startColor="#30000000"
5
- android:centerColor="#20000000"
6
- android:endColor="#40000000"
7
- android:angle="90" />
8
- </shape>
@@ -1,166 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:id="@+id/call_root"
4
- android:layout_width="match_parent"
5
- android:layout_height="match_parent"
6
- android:background="@drawable/call_background">
7
-
8
- <!-- Overlay for better text readability -->
9
- <View
10
- android:layout_width="match_parent"
11
- android:layout_height="match_parent"
12
- android:background="@drawable/overlay_gradient" />
13
-
14
- <!-- Top section with incoming call info -->
15
- <LinearLayout
16
- android:id="@+id/top_section"
17
- android:layout_width="match_parent"
18
- android:layout_height="wrap_content"
19
- android:layout_centerInParent="true"
20
- android:layout_marginTop="80dp"
21
- android:gravity="center"
22
- android:orientation="vertical"
23
- android:padding="32dp">
24
-
25
- <TextView
26
- android:layout_width="wrap_content"
27
- android:layout_height="wrap_content"
28
- android:text="Incoming call"
29
- android:textColor="#B3FFFFFF"
30
- android:textSize="16sp"
31
- android:layout_marginBottom="24dp"
32
- android:letterSpacing="0.1" />
33
-
34
- <!-- Avatar with glow effect -->
35
- <FrameLayout
36
- android:layout_width="140dp"
37
- android:layout_height="140dp"
38
- android:layout_marginBottom="24dp">
39
-
40
- <View
41
- android:layout_width="140dp"
42
- android:layout_height="140dp"
43
- android:background="@drawable/avatar_glow" />
44
-
45
- <ImageView
46
- android:id="@+id/avatar"
47
- android:layout_width="120dp"
48
- android:layout_height="120dp"
49
- android:layout_gravity="center"
50
- android:background="@drawable/avatar_bg"
51
- android:scaleType="centerCrop"
52
- android:src="@android:drawable/sym_def_app_icon" />
53
- </FrameLayout>
54
-
55
- <TextView
56
- android:id="@+id/caller_name"
57
- android:layout_width="wrap_content"
58
- android:layout_height="wrap_content"
59
- android:text="Caller Name"
60
- android:textColor="#FFFFFF"
61
- android:textSize="28sp"
62
- android:textStyle="bold"
63
- android:layout_marginBottom="8dp"
64
- android:shadowColor="#33000000"
65
- android:shadowDx="0"
66
- android:shadowDy="2"
67
- android:shadowRadius="4" />
68
-
69
- <TextView
70
- android:layout_width="wrap_content"
71
- android:layout_height="wrap_content"
72
- android:text="Mobile"
73
- android:textColor="#B3FFFFFF"
74
- android:textSize="16sp"
75
- android:layout_marginBottom="48dp" />
76
-
77
- </LinearLayout>
78
-
79
- <!-- Bottom section with action buttons -->
80
- <LinearLayout
81
- android:layout_width="match_parent"
82
- android:layout_height="wrap_content"
83
- android:layout_alignParentBottom="true"
84
- android:layout_marginBottom="80dp"
85
- android:gravity="center"
86
- android:orientation="horizontal"
87
- android:padding="32dp">
88
-
89
- <!-- Additional quick actions -->
90
- <FrameLayout
91
- android:layout_width="56dp"
92
- android:layout_height="56dp"
93
- android:layout_marginEnd="32dp">
94
-
95
- <ImageView
96
- android:layout_width="56dp"
97
- android:layout_height="56dp"
98
- android:background="@drawable/action_button_secondary"
99
- android:src="@android:drawable/ic_menu_call"
100
- android:padding="16dp"
101
- android:tint="#FFFFFF" />
102
- </FrameLayout>
103
-
104
- <!-- Decline Button -->
105
- <FrameLayout
106
- android:layout_width="80dp"
107
- android:layout_height="80dp"
108
- android:layout_marginEnd="48dp">
109
-
110
- <View
111
- android:layout_width="80dp"
112
- android:layout_height="80dp"
113
- android:background="@drawable/decline_button_shadow" />
114
-
115
- <Button
116
- android:id="@+id/decline_btn"
117
- android:layout_width="72dp"
118
- android:layout_height="72dp"
119
- android:layout_gravity="center"
120
- android:background="@drawable/decline_button"
121
- android:text="✕"
122
- android:textColor="#FFFFFF"
123
- android:textSize="24sp"
124
- android:textStyle="bold" />
125
- </FrameLayout>
126
-
127
- <!-- Answer Button -->
128
- <FrameLayout
129
- android:layout_width="80dp"
130
- android:layout_height="80dp">
131
-
132
- <View
133
- android:layout_width="80dp"
134
- android:layout_height="80dp"
135
- android:background="@drawable/answer_button_shadow" />
136
-
137
- <Button
138
- android:id="@+id/answer_btn"
139
- android:layout_width="72dp"
140
- android:layout_height="72dp"
141
- android:layout_gravity="center"
142
- android:background="@drawable/answer_button"
143
- android:text="✓"
144
- android:textColor="#FFFFFF"
145
- android:textSize="24sp"
146
- android:textStyle="bold" />
147
- </FrameLayout>
148
-
149
- <!-- Message quick action -->
150
- <FrameLayout
151
- android:layout_width="56dp"
152
- android:layout_height="56dp"
153
- android:layout_marginStart="32dp">
154
-
155
- <ImageView
156
- android:layout_width="56dp"
157
- android:layout_height="56dp"
158
- android:background="@drawable/action_button_secondary"
159
- android:src="@android:drawable/ic_menu_send"
160
- android:padding="16dp"
161
- android:tint="#FFFFFF" />
162
- </FrameLayout>
163
-
164
- </LinearLayout>
165
-
166
- </RelativeLayout>