@@ -6,24 +6,40 @@ package com.twilio.twilio_voice.service
66
77import android.content.Context
88import android.content.Intent
9+ import android.os.Build
910import android.os.Bundle
1011import android.telecom.CallAudioState
1112import android.telecom.Connection
1213import android.telecom.DisconnectCause
14+ import android.telecom.StatusHints
1315import android.util.Log
16+ import android.graphics.drawable.Icon
1417import androidx.localbroadcastmanager.content.LocalBroadcastManager
18+ import com.twilio.twilio_voice.R
1519import com.twilio.twilio_voice.call.TVParameters
1620import com.twilio.twilio_voice.receivers.TVBroadcastReceiver
1721import com.twilio.twilio_voice.types.CallAudioStateExtension.copyWith
1822import com.twilio.twilio_voice.types.CallDirection
1923import com.twilio.twilio_voice.types.CallExceptionExtension.toBundle
2024import com.twilio.twilio_voice.types.CompletionHandler
25+ import com.twilio.twilio_voice.types.ContextExtension.hasMicrophoneAccess
2126import com.twilio.twilio_voice.types.TVNativeCallActions
2227import com.twilio.twilio_voice.types.TVNativeCallEvents
2328import com.twilio.twilio_voice.types.ValueBundleChanged
2429import com.twilio.voice.Call
2530import com.twilio.voice.CallException
2631import com.twilio.voice.CallInvite
32+ import com.twilio.twilio_voice.types.ContextExtension
33+ import android.app.NotificationChannel
34+ import android.app.NotificationManager
35+ import androidx.core.app.NotificationCompat
36+ import android.app.PendingIntent
37+ import android.os.Handler
38+ import android.os.Looper
39+ import com.twilio.twilio_voice.service.TVConnectionService
40+ import android.content.BroadcastReceiver
41+ import android.content.IntentFilter
42+
2743
2844
2945class TVCallInviteConnection (
@@ -46,12 +62,55 @@ class TVCallInviteConnection(
4662
4763 override fun onAnswer () {
4864 Log .d(TAG , " onAnswer: onAnswer" )
49- super .onAnswer()
50- twilioCall = callInvite.accept(context, this )
51- onAction?.onChange(TVNativeCallActions .ACTION_ANSWERED , Bundle ().apply {
52- putParcelable(TVBroadcastReceiver .EXTRA_CALL_INVITE , callInvite)
53- putInt(TVBroadcastReceiver .EXTRA_CALL_DIRECTION , callDirection.id)
54- })
65+
66+ var isCallAccepted = false
67+
68+ fun acceptCall (receiver : BroadcastReceiver ) {
69+ try {
70+ LocalBroadcastManager .getInstance(context).unregisterReceiver(receiver)
71+ } catch (_: Exception ) {}
72+
73+ if (isCallAccepted) {
74+ return
75+ }
76+
77+ isCallAccepted = true
78+
79+ super .onAnswer()
80+ twilioCall = callInvite.accept(context, this )
81+ onAction?.onChange(TVNativeCallActions .ACTION_ANSWERED , Bundle ().apply {
82+ putParcelable(TVBroadcastReceiver .EXTRA_CALL_INVITE , callInvite)
83+ putInt(TVBroadcastReceiver .EXTRA_CALL_DIRECTION , callDirection.id)
84+ })
85+ }
86+
87+ val incomingCallServiceReadyReceiver = object : android.content.BroadcastReceiver () {
88+ override fun onReceive (c : android.content.Context ? , i : android.content.Intent ? ) {
89+ acceptCall(this )
90+ }
91+ }
92+
93+ LocalBroadcastManager .getInstance(context).registerReceiver(
94+ incomingCallServiceReadyReceiver,
95+ android.content.IntentFilter (TVConnectionService .ACTION_INCOMING_CALL_SERVICE_READY )
96+ )
97+
98+ Handler (Looper .getMainLooper()).postDelayed({
99+ acceptCall(incomingCallServiceReadyReceiver)
100+ }, 3000 )
101+
102+ try {
103+ val launchIntent = Intent (context, com.twilio.twilio_voice.ui.IncomingCallActivity ::class .java).apply {
104+ addFlags(Intent .FLAG_ACTIVITY_NEW_TASK or
105+ Intent .FLAG_ACTIVITY_SINGLE_TOP or
106+ Intent .FLAG_ACTIVITY_CLEAR_TOP or
107+ Intent .FLAG_ACTIVITY_NO_USER_ACTION )
108+ }
109+ context.startActivity(launchIntent)
110+ } catch (e: Exception ) {
111+ Log .w(TAG , " Unable to launch IncomingCallActivity from onAnswer: $e " )
112+ acceptCall(incomingCallServiceReadyReceiver)
113+ }
55114 }
56115
57116 fun acceptInvite () {
@@ -159,6 +218,7 @@ open class TVCallConnection(
159218 onDisconnected?.withValue(disconnectCause)
160219 onEvent?.onChange(TVNativeCallEvents .EVENT_CONNECT_FAILURE , callException.toBundle())
161220 onCallStateListener?.withValue(call.state)
221+ destroy()
162222 }
163223
164224 /* *
@@ -332,6 +392,47 @@ open class TVCallConnection(
332392 }
333393
334394 override fun onAnswer (videoState : Int ) {
395+ if (! context.hasMicrophoneAccess()) {
396+ val notificationManager = context.getSystemService(Context .NOTIFICATION_SERVICE ) as NotificationManager
397+ val channelId = " twilio_voice_permissions"
398+ val channelName = " Twilio Voice Permissions"
399+
400+ if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .O ) {
401+ val channel = NotificationChannel (
402+ channelId,
403+ channelName,
404+ NotificationManager .IMPORTANCE_HIGH
405+ ).apply {
406+ description = " Phone call permissions notifications"
407+ }
408+ notificationManager.createNotificationChannel(channel)
409+ }
410+
411+ // Create intent to open app settings
412+ val settingsIntent = Intent (android.provider.Settings .ACTION_APPLICATION_DETAILS_SETTINGS ).apply {
413+ data = android.net.Uri .fromParts(" package" , context.packageName, null )
414+ flags = Intent .FLAG_ACTIVITY_NEW_TASK
415+ }
416+ val pendingIntent = PendingIntent .getActivity(
417+ context,
418+ 0 ,
419+ settingsIntent,
420+ PendingIntent .FLAG_IMMUTABLE
421+ )
422+
423+ val notification = NotificationCompat .Builder (context, channelId)
424+ .setContentTitle(" Unable to Answer Call - Microphone Access Needed" )
425+ .setContentText(" An incoming call is waiting, but you need to enable microphone access in settings to accept calls." )
426+ .setSmallIcon(context.resources.getIdentifier(" ic_stat_onesignal_default" , " drawable" , context.packageName))
427+ .setLargeIcon(android.graphics.BitmapFactory .decodeResource(context.resources, context.resources.getIdentifier(" ic_onesignal_large_icon_default" , " drawable" , context.packageName)))
428+ .setPriority(NotificationCompat .PRIORITY_HIGH )
429+ .setAutoCancel(true )
430+ .setContentIntent(pendingIntent)
431+ .build()
432+ notificationManager.notify(1 , notification)
433+ return
434+ }
435+
335436 super .onAnswer(videoState)
336437 Log .d(TAG , " onAnswer: onAnswer" )
337438 }
0 commit comments