Skip to content

space auto #286

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,28 @@
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />

<application>
<service
android:name=".service.TVConnectionService"
android:exported="true"
android:foregroundServiceType="microphone"
android:foregroundServiceType="microphone|phoneCall"
android:label="@string/connection_service_name"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>

<activity
android:name=".ui.IncomingCallActivity"
android:exported="false"
android:theme="@style/TransparentCallTheme"
android:showWhenLocked="true"
android:turnScreenOn="true"
android:launchMode="singleTask" />

<meta-data
android:name="flutterEmbedding"
android:value="2" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import android.content.DialogInterface
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.res.Resources
import android.view.ContextThemeWrapper
import android.os.Build
import android.os.Bundle
import android.telecom.CallAudioState
Expand Down Expand Up @@ -1330,6 +1332,7 @@ class TwilioVoicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamH
Log.d(TAG, "onDetachedFromActivityForConfigChanges")
unregisterReceiver()
activity = null
permissionResultHandler.clear()
}

override fun onReattachedToActivityForConfigChanges(activityPluginBinding: ActivityPluginBinding) {
Expand All @@ -1344,6 +1347,7 @@ class TwilioVoicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamH
Log.d(TAG, "onDetachedFromActivity")
unregisterReceiver()
activity = null
permissionResultHandler.clear()
}
//endregion

Expand Down Expand Up @@ -1623,21 +1627,32 @@ class TwilioVoicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamH
}

logEvent("requestPermissionFor$permissionName")

permissionResultHandler[requestCode] = onPermissionResult

val shouldShowRationale = ActivityCompat.shouldShowRequestPermissionRationale(activity!!, manifestPermission)
if (shouldShowRationale) {
var proceedClicked = false
val clickListener =
DialogInterface.OnClickListener { _: DialogInterface?, _: Int ->
DialogInterface.OnClickListener { dialog: DialogInterface?, _: Int ->
proceedClicked = true
dialog?.dismiss()
}

val dismissListener = DialogInterface.OnDismissListener { _: DialogInterface? ->
if (proceedClicked) {
ActivityCompat.requestPermissions(
activity!!, arrayOf(manifestPermission), requestCode
)
} else {
logEvent("Request${permissionName}AccessDismissed")
permissionResultHandler[requestCode]?.invoke(false)
permissionResultHandler.remove(requestCode)
}
val dismissListener = DialogInterface.OnDismissListener { _: DialogInterface? ->
logEvent("Request" + permissionName + "Access")
}
showPermissionRationaleDialog(activity!!, "$permissionName Permissions", description, clickListener, dismissListener)
} else {
ActivityCompat.requestPermissions(activity!!, arrayOf(manifestPermission), requestCode)
permissionResultHandler[requestCode] = onPermissionResult
}
}

Expand All @@ -1648,7 +1663,8 @@ class TwilioVoicePlugin : FlutterPlugin, MethodCallHandler, EventChannel.StreamH
onClickListener: DialogInterface.OnClickListener,
onDismissListener: DialogInterface.OnDismissListener
) {
val builder = AlertDialog.Builder(context)
val themedContext = ContextThemeWrapper(context, R.style.AppDialogTheme)
val builder = AlertDialog.Builder(themedContext)
builder.setTitle(title)
builder.setMessage(message)
builder.setPositiveButton(R.string.proceed, onClickListener)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,11 @@ class VoiceFirebaseMessagingService : FirebaseMessagingService(), MessageListene
putExtra(TVConnectionService.EXTRA_CANCEL_CALL_INVITE, cancelledCallInvite)
// applicationContext.startService(this)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
applicationContext.startForegroundService(this) // Ensure it's started as a foreground service
try {
applicationContext.startForegroundService(this) // Ensure it's started as a foreground service
} catch (e: Exception) {
applicationContext.startService(this)
}
} else {
applicationContext.startService(this)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,40 @@ package com.twilio.twilio_voice.service

import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.telecom.CallAudioState
import android.telecom.Connection
import android.telecom.DisconnectCause
import android.telecom.StatusHints
import android.util.Log
import android.graphics.drawable.Icon
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import com.twilio.twilio_voice.R
import com.twilio.twilio_voice.call.TVParameters
import com.twilio.twilio_voice.receivers.TVBroadcastReceiver
import com.twilio.twilio_voice.types.CallAudioStateExtension.copyWith
import com.twilio.twilio_voice.types.CallDirection
import com.twilio.twilio_voice.types.CallExceptionExtension.toBundle
import com.twilio.twilio_voice.types.CompletionHandler
import com.twilio.twilio_voice.types.ContextExtension.hasMicrophoneAccess
import com.twilio.twilio_voice.types.TVNativeCallActions
import com.twilio.twilio_voice.types.TVNativeCallEvents
import com.twilio.twilio_voice.types.ValueBundleChanged
import com.twilio.voice.Call
import com.twilio.voice.CallException
import com.twilio.voice.CallInvite
import com.twilio.twilio_voice.types.ContextExtension
import android.app.NotificationChannel
import android.app.NotificationManager
import androidx.core.app.NotificationCompat
import android.app.PendingIntent
import android.os.Handler
import android.os.Looper
import com.twilio.twilio_voice.service.TVConnectionService
import android.content.BroadcastReceiver
import android.content.IntentFilter



class TVCallInviteConnection(
Expand All @@ -46,12 +62,55 @@ class TVCallInviteConnection(

override fun onAnswer() {
Log.d(TAG, "onAnswer: onAnswer")
super.onAnswer()
twilioCall = callInvite.accept(context, this)
onAction?.onChange(TVNativeCallActions.ACTION_ANSWERED, Bundle().apply {
putParcelable(TVBroadcastReceiver.EXTRA_CALL_INVITE, callInvite)
putInt(TVBroadcastReceiver.EXTRA_CALL_DIRECTION, callDirection.id)
})

var isCallAccepted = false

fun acceptCall(receiver: BroadcastReceiver) {
try {
LocalBroadcastManager.getInstance(context).unregisterReceiver(receiver)
} catch (_: Exception) {}

if (isCallAccepted) {
return
}

isCallAccepted = true

super.onAnswer()
twilioCall = callInvite.accept(context, this)
onAction?.onChange(TVNativeCallActions.ACTION_ANSWERED, Bundle().apply {
putParcelable(TVBroadcastReceiver.EXTRA_CALL_INVITE, callInvite)
putInt(TVBroadcastReceiver.EXTRA_CALL_DIRECTION, callDirection.id)
})
}

val incomingCallServiceReadyReceiver = object : android.content.BroadcastReceiver() {
override fun onReceive(c: android.content.Context?, i: android.content.Intent?) {
acceptCall(this)
}
}

LocalBroadcastManager.getInstance(context).registerReceiver(
incomingCallServiceReadyReceiver,
android.content.IntentFilter(TVConnectionService.ACTION_INCOMING_CALL_SERVICE_READY)
)

Handler(Looper.getMainLooper()).postDelayed({
acceptCall(incomingCallServiceReadyReceiver)
}, 3000)

try {
val launchIntent = Intent(context, com.twilio.twilio_voice.ui.IncomingCallActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or
Intent.FLAG_ACTIVITY_SINGLE_TOP or
Intent.FLAG_ACTIVITY_CLEAR_TOP or
Intent.FLAG_ACTIVITY_NO_USER_ACTION)
}
context.startActivity(launchIntent)
} catch (e: Exception) {
Log.w(TAG, "Unable to launch IncomingCallActivity from onAnswer: $e")
acceptCall(incomingCallServiceReadyReceiver)
}
}

fun acceptInvite() {
Expand Down Expand Up @@ -159,6 +218,7 @@ open class TVCallConnection(
onDisconnected?.withValue(disconnectCause)
onEvent?.onChange(TVNativeCallEvents.EVENT_CONNECT_FAILURE, callException.toBundle())
onCallStateListener?.withValue(call.state)
destroy()
}

/**
Expand Down Expand Up @@ -332,6 +392,47 @@ open class TVCallConnection(
}

override fun onAnswer(videoState: Int) {
if (!context.hasMicrophoneAccess()) {
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channelId = "twilio_voice_permissions"
val channelName = "Twilio Voice Permissions"

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
channelId,
channelName,
NotificationManager.IMPORTANCE_HIGH
).apply {
description = "Phone call permissions notifications"
}
notificationManager.createNotificationChannel(channel)
}

// Create intent to open app settings
val settingsIntent = Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {
data = android.net.Uri.fromParts("package", context.packageName, null)
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
val pendingIntent = PendingIntent.getActivity(
context,
0,
settingsIntent,
PendingIntent.FLAG_IMMUTABLE
)

val notification = NotificationCompat.Builder(context, channelId)
.setContentTitle("Unable to Answer Call - Microphone Access Needed")
.setContentText("An incoming call is waiting, but you need to enable microphone access in settings to accept calls.")
.setSmallIcon(context.resources.getIdentifier("ic_stat_onesignal_default", "drawable", context.packageName))
.setLargeIcon(android.graphics.BitmapFactory.decodeResource(context.resources, context.resources.getIdentifier("ic_onesignal_large_icon_default", "drawable", context.packageName)))
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.build()
notificationManager.notify(1, notification)
return
}

super.onAnswer(videoState)
Log.d(TAG, "onAnswer: onAnswer")
}
Expand Down
Loading