@qusaieilouti99/call-manager 0.1.95 → 0.1.97

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.
@@ -4,33 +4,37 @@ package com.margelo.nitro.qusaieilouti99.callmanager
4
4
  import android.app.Activity
5
5
  import android.app.KeyguardManager
6
6
  import android.content.Context
7
+ import android.graphics.Color
8
+ import android.graphics.RenderEffect
9
+ import android.graphics.Shader
10
+ import android.graphics.drawable.Drawable
11
+ import android.graphics.drawable.GradientDrawable
7
12
  import android.os.Build
8
13
  import android.os.Bundle
9
14
  import android.os.Handler
10
15
  import android.os.Looper
11
16
  import android.util.Log
17
+ import android.util.TypedValue
18
+ import android.view.Gravity
19
+ import android.view.View
20
+ import android.view.ViewGroup
12
21
  import android.view.WindowManager
13
- import android.widget.Button
22
+ import android.widget.FrameLayout
23
+ import android.widget.ImageView
24
+ import android.widget.LinearLayout
14
25
  import android.widget.TextView
26
+ import androidx.core.content.ContextCompat
27
+ import com.bumptech.glide.Glide
15
28
 
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
- */
21
29
  class CallActivity : Activity(), CallEngine.CallEndListener {
22
-
23
- private enum class FinishReason {
24
- ANSWER, DECLINE, TIMEOUT, MANUAL_DISMISS, EXTERNAL_END
25
- }
26
-
30
+ private enum class FinishReason { ANSWER, DECLINE, TIMEOUT,
31
+ MANUAL_DISMISS, EXTERNAL_END }
27
32
  private var finishReason: FinishReason? = null
28
33
  private var callId: String = ""
29
34
  private var callType: String = "Audio"
30
-
31
- private val timeoutHandler = Handler(Looper.getMainLooper())
35
+ private val timeoutMs = 60_000L
36
+ private val handler = Handler(Looper.getMainLooper())
32
37
  private val timeoutRunnable = Runnable {
33
- Log.d(TAG, "CallActivity timeout triggered for callId: $callId")
34
38
  finishReason = FinishReason.TIMEOUT
35
39
  CallEngine.stopRingtone()
36
40
  CallEngine.cancelIncomingCallUI()
@@ -40,55 +44,214 @@ class CallActivity : Activity(), CallEngine.CallEndListener {
40
44
 
41
45
  override fun onCreate(savedInstanceState: Bundle?) {
42
46
  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)
47
+ val isSamsungBypass = intent.getBooleanExtra(
48
+ "SAMSUNG_LOCK_SCREEN_BYPASS", false
49
+ )
50
+ setupLockScreenBypass(isSamsungBypass)
48
51
 
49
- setContentView(R.layout.activity_call)
50
-
51
- // Read incoming‐call params
52
+ // Read params
52
53
  callId = intent.getStringExtra("callId") ?: ""
53
54
  callType = intent.getStringExtra("callType") ?: "Audio"
54
- Log.d(TAG, "CallActivity received callId: $callId, callType: $callType")
55
+ val callerName = intent.getStringExtra("callerName") ?: "Unknown"
56
+ val avatarUrl = intent.getStringExtra("callerAvatar")
55
57
 
56
- // Register for call‐end callbacks BEFORE timeout or user can dismiss
57
58
  CallEngine.registerCallEndListener(this)
59
+ buildUi(callerName, avatarUrl)
60
+ handler.postDelayed(timeoutRunnable, timeoutMs)
61
+ Log.d(TAG, "CallActivity setup complete for $callId")
62
+ }
58
63
 
59
- // Bind UI
60
- val callerName = intent.getStringExtra("callerName") ?: "Unknown"
61
- findViewById<TextView>(R.id.caller_name).text = callerName
64
+ private fun buildUi(name: String, avatarUrl: String?) {
65
+ val root = FrameLayout(this).apply {
66
+ layoutParams = ViewGroup.LayoutParams(
67
+ ViewGroup.LayoutParams.MATCH_PARENT,
68
+ ViewGroup.LayoutParams.MATCH_PARENT
69
+ )
70
+ }
62
71
 
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()
72
+ // 1) Background image + blur
73
+ val bg = ImageView(this).apply {
74
+ layoutParams = FrameLayout.LayoutParams(
75
+ ViewGroup.LayoutParams.MATCH_PARENT,
76
+ ViewGroup.LayoutParams.MATCH_PARENT
77
+ )
78
+ scaleType = ImageView.ScaleType.CENTER_CROP
79
+ }
80
+ if (!avatarUrl.isNullOrEmpty()) {
81
+ Glide.with(this).load(avatarUrl).into(bg)
82
+ } else {
83
+ bg.setImageResource(android.R.drawable.sym_def_app_icon)
84
+ }
85
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
86
+ bg.setRenderEffect(
87
+ RenderEffect.createBlurEffect(25f, 25f, Shader.TileMode.CLAMP)
88
+ )
70
89
  }
90
+ root.addView(bg)
71
91
 
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()
92
+ // 2) Dark scrim
93
+ root.addView(View(this).apply {
94
+ layoutParams = FrameLayout.LayoutParams(
95
+ ViewGroup.LayoutParams.MATCH_PARENT,
96
+ ViewGroup.LayoutParams.MATCH_PARENT
97
+ )
98
+ setBackgroundColor(Color.parseColor("#80000000"))
99
+ })
100
+
101
+ // 3) Top call-type label
102
+ val callTypeLabel = TextView(this).apply {
103
+ text = when (callType.lowercase()) {
104
+ "video" -> "Incoming video call"
105
+ else -> "Incoming audio call"
106
+ }
107
+ setTextColor(Color.WHITE)
108
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 18f)
109
+ gravity = Gravity.CENTER
110
+ layoutParams = FrameLayout.LayoutParams(
111
+ ViewGroup.LayoutParams.WRAP_CONTENT,
112
+ ViewGroup.LayoutParams.WRAP_CONTENT
113
+ ).apply {
114
+ gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL
115
+ topMargin = dp(40)
116
+ }
117
+ }
118
+ root.addView(callTypeLabel)
119
+
120
+ // 4) Center content: profile + name
121
+ val centerCol = LinearLayout(this).apply {
122
+ orientation = LinearLayout.VERTICAL
123
+ gravity = Gravity.CENTER_HORIZONTAL
124
+ layoutParams = FrameLayout.LayoutParams(
125
+ ViewGroup.LayoutParams.MATCH_PARENT,
126
+ ViewGroup.LayoutParams.WRAP_CONTENT
127
+ ).apply {
128
+ gravity = Gravity.CENTER
129
+ topMargin = dp(20)
130
+ }
131
+ }
132
+ // Profile circle
133
+ val profile = ImageView(this).apply {
134
+ val sz = dp(140)
135
+ layoutParams = LinearLayout.LayoutParams(sz, sz)
136
+ scaleType = ImageView.ScaleType.CENTER_CROP
137
+ background = GradientDrawable().apply {
138
+ shape = GradientDrawable.OVAL
139
+ setColor(Color.LTGRAY)
140
+ }
141
+ clipToOutline = true
142
+ }
143
+ if (!avatarUrl.isNullOrEmpty()) {
144
+ Glide.with(this).load(avatarUrl).circleCrop().into(profile)
145
+ }
146
+ centerCol.addView(profile)
147
+
148
+ // Caller name
149
+ centerCol.addView(TextView(this).apply {
150
+ text = name
151
+ setTextColor(Color.WHITE)
152
+ setTextSize(TypedValue.COMPLEX_UNIT_SP, 28f)
153
+ typeface = android.graphics.Typeface.DEFAULT_BOLD
154
+ gravity = Gravity.CENTER
155
+ layoutParams = LinearLayout.LayoutParams(
156
+ ViewGroup.LayoutParams.WRAP_CONTENT,
157
+ ViewGroup.LayoutParams.WRAP_CONTENT
158
+ ).apply { topMargin = dp(24) }
159
+ })
160
+
161
+ root.addView(centerCol)
162
+
163
+ // 5) Bottom actions
164
+ val actions = LinearLayout(this).apply {
165
+ orientation = LinearLayout.HORIZONTAL
166
+ gravity = Gravity.CENTER
167
+ layoutParams = FrameLayout.LayoutParams(
168
+ ViewGroup.LayoutParams.MATCH_PARENT,
169
+ ViewGroup.LayoutParams.WRAP_CONTENT
170
+ ).apply {
171
+ gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
172
+ bottomMargin = dp(48)
173
+ }
174
+ }
175
+
176
+ val declineBtn = createCircleButton(
177
+ android.R.drawable.ic_menu_close_clear_cancel,
178
+ Color.parseColor("#F44336")
179
+ ).apply { setOnClickListener { onDecline() } }
180
+
181
+ val answerBtn = createCircleButton(
182
+ android.R.drawable.ic_menu_call,
183
+ Color.parseColor("#4CAF50")
184
+ ).apply { setOnClickListener { onAnswer() } }
185
+
186
+ actions.addView(declineBtn)
187
+ actions.addView(View(this).apply {
188
+ layoutParams = LinearLayout.LayoutParams(dp(60), 0)
189
+ })
190
+ actions.addView(answerBtn)
191
+ root.addView(actions)
192
+
193
+ setContentView(root)
194
+ }
195
+
196
+ private fun createCircleButton(iconRes: Int, bgColor: Int): FrameLayout {
197
+ val size = dp(70)
198
+ return FrameLayout(this).apply {
199
+ layoutParams = LinearLayout.LayoutParams(size, size)
200
+ isClickable = true; isFocusable = true
201
+ foreground = getRipple()
202
+ // circle bg
203
+ addView(View(context).apply {
204
+ layoutParams = FrameLayout.LayoutParams(
205
+ ViewGroup.LayoutParams.MATCH_PARENT,
206
+ ViewGroup.LayoutParams.MATCH_PARENT
207
+ )
208
+ background = GradientDrawable().apply {
209
+ shape = GradientDrawable.OVAL
210
+ setColor(bgColor)
211
+ }
212
+ })
213
+ // icon
214
+ addView(ImageView(context).apply {
215
+ layoutParams = FrameLayout.LayoutParams(dp(36), dp(36))
216
+ .apply { gravity = Gravity.CENTER }
217
+ setImageResource(iconRes)
218
+ setColorFilter(Color.WHITE)
219
+ })
79
220
  }
221
+ }
222
+
223
+ private fun getRipple(): Drawable? {
224
+ val tv = TypedValue()
225
+ theme.resolveAttribute(
226
+ android.R.attr.selectableItemBackgroundBorderless, tv, true
227
+ )
228
+ return ContextCompat.getDrawable(this, tv.resourceId)
229
+ }
230
+
231
+ private fun onAnswer() {
232
+ finishReason = FinishReason.ANSWER
233
+ CallEngine.stopRingtone()
234
+ CallEngine.cancelIncomingCallUI()
235
+ CallEngine.answerCall(callId)
236
+ finishCallActivity()
237
+ }
80
238
 
81
- // Start auto‐timeout
82
- timeoutHandler.postDelayed(timeoutRunnable, 60_000)
83
- Log.d(TAG, "CallActivity setup complete")
239
+ private fun onDecline() {
240
+ finishReason = FinishReason.DECLINE
241
+ CallEngine.stopRingtone()
242
+ CallEngine.cancelIncomingCallUI()
243
+ CallEngine.endCall(callId)
244
+ finishCallActivity()
84
245
  }
85
246
 
86
- // **SAMSUNG FIX**: Enhanced lock screen bypass method
87
- private fun setupLockScreenBypass(isSamsungSpecific: Boolean) {
88
- // Standard lock screen bypass
247
+ private fun dp(v: Int): Int = TypedValue.applyDimension(
248
+ TypedValue.COMPLEX_UNIT_DIP, v.toFloat(),
249
+ resources.displayMetrics
250
+ ).toInt()
251
+
252
+ private fun setupLockScreenBypass(isSamsung: Boolean) {
89
253
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
90
- setShowWhenLocked(true)
91
- setTurnScreenOn(true)
254
+ setShowWhenLocked(true); setTurnScreenOn(true)
92
255
  } else {
93
256
  window.addFlags(
94
257
  WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
@@ -96,53 +259,30 @@ class CallActivity : Activity(), CallEngine.CallEndListener {
96
259
  WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD
97
260
  )
98
261
  }
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
262
+ if (isSamsung) {
105
263
  window.addFlags(
106
264
  WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
107
265
  WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
108
266
  WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
109
267
  WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
110
268
  )
111
-
112
- // **SAMSUNG SPECIFIC**: Request keyguard dismissal
113
- val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
269
+ val km = getSystemService(Context.KEYGUARD_SERVICE)
270
+ as KeyguardManager
114
271
  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
- })
272
+ km.requestDismissKeyguard(this,
273
+ object : KeyguardManager.KeyguardDismissCallback() {
274
+ override fun onDismissSucceeded() {
275
+ Log.d(TAG, "Samsung keyguard dismissed")
276
+ }
277
+ override fun onDismissError() {
278
+ Log.w(TAG, "Samsung keyguard dismiss error")
279
+ }
280
+ })
123
281
  }
124
282
  }
125
283
  }
126
284
 
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
285
  override fun onBackPressed() {
145
- Log.d(TAG, "onBackPressed for callId: $callId → treat as decline")
146
286
  finishReason = FinishReason.MANUAL_DISMISS
147
287
  CallEngine.stopRingtone()
148
288
  CallEngine.cancelIncomingCallUI()
@@ -150,24 +290,25 @@ class CallActivity : Activity(), CallEngine.CallEndListener {
150
290
  finishCallActivity()
151
291
  }
152
292
 
153
- /**
154
- * Called by CallEngine whenever ANY call ends.
155
- * We only care about our own callId.
156
- */
157
293
  override fun onCallEnded(endedCallId: String) {
158
294
  if (endedCallId == callId && !isFinishing) {
159
- Log.d(TAG, "CallActivity onCallEnded callback for callId: $callId")
160
295
  finishReason = FinishReason.EXTERNAL_END
161
296
  runOnUiThread { finishCallActivity() }
162
297
  }
163
298
  }
164
299
 
165
- private fun finishCallActivity() {
166
- if (isFinishing) {
167
- Log.d(TAG, "Already finishing, skip.")
168
- return
300
+ override fun onDestroy() {
301
+ super.onDestroy()
302
+ CallEngine.unregisterCallEndListener(this)
303
+ handler.removeCallbacks(timeoutRunnable)
304
+ if (finishReason != FinishReason.ANSWER) {
305
+ CallEngine.stopRingtone()
306
+ CallEngine.cancelIncomingCallUI()
169
307
  }
170
- Log.d(TAG, "Finishing CallActivity for callId: $callId")
308
+ }
309
+
310
+ private fun finishCallActivity() {
311
+ if (isFinishing) return
171
312
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
172
313
  finishAndRemoveTask()
173
314
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qusaieilouti99/call-manager",
3
- "version": "0.1.95",
3
+ "version": "0.1.97",
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" android:color="#40FFFFFF">
3
- <item>
4
- <shape android:shape="oval">
5
- <gradient
6
- android:startColor="#FF4CAF50"
7
- android:endColor="#FF388E3C"
8
- android:angle="135" />
9
- <stroke android:width="2dp" android:color="#FF66BB6A" />
10
- </shape>
11
- </item>
12
- </ripple>
@@ -1,9 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
3
- <gradient
4
- android:startColor="#40000000"
5
- android:centerColor="#20000000"
6
- android:endColor="#00000000"
7
- android:type="radial"
8
- android:gradientRadius="40dp" />
9
- </shape>
@@ -1,12 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#40FFFFFF">
3
- <item>
4
- <shape android:shape="oval">
5
- <gradient
6
- android:startColor="#FFF44336"
7
- android:endColor="#FFD32F2F"
8
- android:angle="135" />
9
- <stroke android:width="2dp" android:color="#FFEF5350" />
10
- </shape>
11
- </item>
12
- </ripple>
@@ -1,10 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <vector xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:width="136dp"
4
- android:height="136dp"
5
- android:viewportWidth="24"
6
- android:viewportHeight="24">
7
- <path
8
- android:fillColor="#FF9E9E9E"
9
- android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
10
- </vector>
@@ -1,21 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
3
- <item>
4
- <shape>
5
- <gradient
6
- android:startColor="#FF0D1B2A"
7
- android:centerColor="#FF1B263B"
8
- android:endColor="#FF415A77"
9
- android:angle="135" />
10
- </shape>
11
- </item>
12
- <item>
13
- <shape>
14
- <gradient
15
- android:startColor="#15FFFFFF"
16
- android:centerColor="#08FFFFFF"
17
- android:endColor="#00FFFFFF"
18
- android:angle="45" />
19
- </shape>
20
- </item>
21
- </layer-list>
@@ -1,5 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
3
- <solid android:color="#FFFFFF" />
4
- <stroke android:width="3dp" android:color="#E0E0E0" />
5
- </shape>
@@ -1,4 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
3
- <solid android:color="#F5F5F5" />
4
- </shape>
@@ -1,5 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
3
- <stroke android:width="1dp" android:color="#40FFFFFF" />
4
- <solid android:color="#08FFFFFF" />
5
- </shape>
@@ -1,5 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
3
- <stroke android:width="2dp" android:color="#30FFFFFF" />
4
- <solid android:color="#10FFFFFF" />
5
- </shape>
@@ -1,10 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="#30FFFFFF">
3
- <item>
4
- <shape android:shape="rectangle">
5
- <solid android:color="#20FFFFFF" />
6
- <corners android:radius="16dp" />
7
- <stroke android:width="1dp" android:color="#30FFFFFF" />
8
- </shape>
9
- </item>
10
- </ripple>
@@ -1,244 +0,0 @@
1
- <?xml version="1.0" encoding="utf-8"?>
2
- <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
- android:layout_width="match_parent"
4
- android:layout_height="match_parent"
5
- android:background="#FF0D1B2A">
6
-
7
- <!-- Professional gradient background -->
8
- <View
9
- android:layout_width="match_parent"
10
- android:layout_height="match_parent"
11
- android:background="@drawable/professional_bg" />
12
-
13
- <!-- Main content container -->
14
- <LinearLayout
15
- android:layout_width="match_parent"
16
- android:layout_height="match_parent"
17
- android:orientation="vertical"
18
- android:gravity="center_horizontal"
19
- android:fitsSystemWindows="true">
20
-
21
- <!-- Top spacing -->
22
- <View
23
- android:layout_width="match_parent"
24
- android:layout_height="0dp"
25
- android:layout_weight="1" />
26
-
27
- <!-- Call type indicator -->
28
- <LinearLayout
29
- android:layout_width="wrap_content"
30
- android:layout_height="wrap_content"
31
- android:orientation="horizontal"
32
- android:gravity="center_vertical"
33
- android:layout_marginBottom="32dp">
34
-
35
- <ImageView
36
- android:layout_width="18dp"
37
- android:layout_height="18dp"
38
- android:src="@android:drawable/ic_menu_call"
39
- android:tint="#80FFFFFF"
40
- android:layout_marginEnd="8dp" />
41
-
42
- <TextView
43
- android:layout_width="wrap_content"
44
- android:layout_height="wrap_content"
45
- android:text="Incoming call"
46
- android:textColor="#80FFFFFF"
47
- android:textSize="14sp"
48
- android:fontFamily="sans-serif-medium" />
49
- </LinearLayout>
50
-
51
- <!-- Profile picture with animated rings -->
52
- <FrameLayout
53
- android:layout_width="180dp"
54
- android:layout_height="180dp"
55
- android:layout_marginBottom="32dp">
56
-
57
- <!-- Outer pulse ring -->
58
- <View
59
- android:layout_width="180dp"
60
- android:layout_height="180dp"
61
- android:background="@drawable/pulse_ring_outer" />
62
-
63
- <!-- Middle pulse ring -->
64
- <View
65
- android:layout_width="160dp"
66
- android:layout_height="160dp"
67
- android:layout_gravity="center"
68
- android:background="@drawable/pulse_ring_middle" />
69
-
70
- <!-- Profile picture -->
71
- <FrameLayout
72
- android:layout_width="140dp"
73
- android:layout_height="140dp"
74
- android:layout_gravity="center">
75
-
76
- <!-- Profile background -->
77
- <View
78
- android:layout_width="140dp"
79
- android:layout_height="140dp"
80
- android:background="@drawable/profile_bg" />
81
-
82
- <!-- Actual profile image -->
83
- <ImageView
84
- android:id="@+id/avatar"
85
- android:layout_width="136dp"
86
- android:layout_height="136dp"
87
- android:layout_gravity="center"
88
- android:background="@drawable/profile_image_bg"
89
- android:scaleType="centerCrop"
90
- android:src="@drawable/default_avatar" />
91
- </FrameLayout>
92
- </FrameLayout>
93
-
94
- <!-- Caller information -->
95
- <LinearLayout
96
- android:layout_width="wrap_content"
97
- android:layout_height="wrap_content"
98
- android:orientation="vertical"
99
- android:gravity="center"
100
- android:layout_marginBottom="24dp">
101
-
102
- <TextView
103
- android:id="@+id/caller_name"
104
- android:layout_width="wrap_content"
105
- android:layout_height="wrap_content"
106
- android:text="John Doe"
107
- android:textColor="#FFFFFF"
108
- android:textSize="32sp"
109
- android:fontFamily="sans-serif-light"
110
- android:gravity="center"
111
- android:layout_marginBottom="4dp" />
112
-
113
- <TextView
114
- android:layout_width="wrap_content"
115
- android:layout_height="wrap_content"
116
- android:text="Mobile • +1 (555) 123-4567"
117
- android:textColor="#B3FFFFFF"
118
- android:textSize="16sp"
119
- android:fontFamily="sans-serif" />
120
- </LinearLayout>
121
-
122
- <!-- Middle spacing -->
123
- <View
124
- android:layout_width="match_parent"
125
- android:layout_height="0dp"
126
- android:layout_weight="2" />
127
-
128
- <!-- Quick actions (Samsung style) -->
129
- <LinearLayout
130
- android:layout_width="match_parent"
131
- android:layout_height="wrap_content"
132
- android:orientation="horizontal"
133
- android:gravity="center"
134
- android:layout_marginBottom="32dp">
135
-
136
- <!-- Remind me -->
137
- <LinearLayout
138
- android:layout_width="wrap_content"
139
- android:layout_height="wrap_content"
140
- android:orientation="vertical"
141
- android:gravity="center"
142
- android:padding="12dp"
143
- android:layout_marginEnd="40dp"
144
- android:background="@drawable/quick_action_bg"
145
- android:clickable="true">
146
-
147
- <ImageView
148
- android:layout_width="24dp"
149
- android:layout_height="24dp"
150
- android:src="@android:drawable/ic_menu_recent_history"
151
- android:tint="#FFFFFF"
152
- android:layout_marginBottom="4dp" />
153
-
154
- <TextView
155
- android:layout_width="wrap_content"
156
- android:layout_height="wrap_content"
157
- android:text="Remind me"
158
- android:textColor="#FFFFFF"
159
- android:textSize="12sp" />
160
- </LinearLayout>
161
-
162
- <!-- Message -->
163
- <LinearLayout
164
- android:layout_width="wrap_content"
165
- android:layout_height="wrap_content"
166
- android:orientation="vertical"
167
- android:gravity="center"
168
- android:padding="12dp"
169
- android:background="@drawable/quick_action_bg"
170
- android:clickable="true">
171
-
172
- <ImageView
173
- android:layout_width="24dp"
174
- android:layout_height="24dp"
175
- android:src="@android:drawable/ic_menu_send"
176
- android:tint="#FFFFFF"
177
- android:layout_marginBottom="4dp" />
178
-
179
- <TextView
180
- android:layout_width="wrap_content"
181
- android:layout_height="wrap_content"
182
- android:text="Message"
183
- android:textColor="#FFFFFF"
184
- android:textSize="12sp" />
185
- </LinearLayout>
186
- </LinearLayout>
187
-
188
- <!-- Main call actions -->
189
- <LinearLayout
190
- android:layout_width="match_parent"
191
- android:layout_height="wrap_content"
192
- android:orientation="horizontal"
193
- android:gravity="center"
194
- android:layout_marginBottom="48dp">
195
-
196
- <!-- Decline button -->
197
- <FrameLayout
198
- android:layout_width="80dp"
199
- android:layout_height="80dp"
200
- android:layout_marginEnd="80dp">
201
-
202
- <!-- Button shadow -->
203
- <View
204
- android:layout_width="80dp"
205
- android:layout_height="80dp"
206
- android:background="@drawable/button_shadow" />
207
-
208
- <Button
209
- android:id="@+id/decline_btn"
210
- android:layout_width="72dp"
211
- android:layout_height="72dp"
212
- android:layout_gravity="center"
213
- android:background="@drawable/decline_btn_bg"
214
- android:text="✕"
215
- android:textColor="#FFFFFF"
216
- android:textSize="28sp"
217
- android:textStyle="bold" />
218
- </FrameLayout>
219
-
220
- <!-- Answer button -->
221
- <FrameLayout
222
- android:layout_width="80dp"
223
- android:layout_height="80dp">
224
-
225
- <!-- Button shadow -->
226
- <View
227
- android:layout_width="80dp"
228
- android:layout_height="80dp"
229
- android:background="@drawable/button_shadow" />
230
-
231
- <Button
232
- android:id="@+id/answer_btn"
233
- android:layout_width="72dp"
234
- android:layout_height="72dp"
235
- android:layout_gravity="center"
236
- android:background="@drawable/answer_btn_bg"
237
- android:text="✓"
238
- android:textColor="#FFFFFF"
239
- android:textSize="28sp"
240
- android:textStyle="bold" />
241
- </FrameLayout>
242
- </LinearLayout>
243
- </LinearLayout>
244
- </FrameLayout>