Android连接服务来电

Android连接服务来电,android,voip,callkit,Android,Voip,Callkit,我正在尝试在Android上实现iOS callkit的behavior。我收到来自firebase的推送通知,我想向用户显示“来电”屏幕。为了做到这一点,我使用了来自android.telecom的ConnectionService包和其他类 这是我的呼叫经理课程: class CallManager(context: Context) { val telecomManager: TelecomManager var phoneAccountHandle:PhoneAccountHandle

我正在尝试在Android上实现iOS callkit的
behavior。我收到来自firebase的推送通知,我想向用户显示“来电”屏幕。为了做到这一点,我使用了来自android.telecom的
ConnectionService
包和其他类

这是我的呼叫经理课程:

class CallManager(context: Context) {
val telecomManager: TelecomManager
var phoneAccountHandle:PhoneAccountHandle
var context:Context
val number = "3924823202"
init {
    telecomManager = context.getSystemService(Context.TELECOM_SERVICE) as TelecomManager
    this.context = context
    val componentName =  ComponentName(this.context, CallConnectionService::class.java)
    phoneAccountHandle = PhoneAccountHandle(componentName, "Admin")
    val phoneAccount = PhoneAccount.builder(phoneAccountHandle, "Admin").setCapabilities(PhoneAccount.CAPABILITY_SELF_MANAGED).build()


    telecomManager.registerPhoneAccount(phoneAccount)
    val intent = Intent()
    intent.component = ComponentName("com.android.server.telecom", "com.android.server.telecom.settings.EnableAccountPreferenceActivity")
    intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP

}


@TargetApi(Build.VERSION_CODES.M)
fun startOutgoingCall() {
    val extras = Bundle()
    extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true)

    val manager = context.getSystemService(TELECOM_SERVICE) as TelecomManager
    val phoneAccountHandle = PhoneAccountHandle(ComponentName(context.packageName, CallConnectionService::class.java!!.getName()), "estosConnectionServiceId")
    val test = Bundle()
    test.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
    test.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, VideoProfile.STATE_BIDIRECTIONAL)
    test.putParcelable(TelecomManager.EXTRA_OUTGOING_CALL_EXTRAS, extras)
    try {
        manager.placeCall(Uri.parse("tel:$number"), test)
    } catch (e:SecurityException){
        e.printStackTrace()
    }
}

@TargetApi(Build.VERSION_CODES.M)
fun  startIncomingCall(){
    if (this.context.checkSelfPermission(Manifest.permission.MANAGE_OWN_CALLS) == PackageManager.PERMISSION_GRANTED) {
        val extras = Bundle()
        val uri = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null)
        extras.putParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS, uri)
        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
        extras.putBoolean(TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE, true)
        val isCallPermitted = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            telecomManager.isIncomingCallPermitted(phoneAccountHandle)
        } else {
           true
        }
        Log.i("CallManager", "is incoming call permited = $isCallPermitted")
        telecomManager.addNewIncomingCall(phoneAccountHandle, extras)
    }
}
}

以及我的自定义
ConnectionService
实现:

class CallConnectionService : ConnectionService() {
override fun onCreateOutgoingConnection(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?): Connection {
    Log.i("CallConnectionService", "onCreateOutgoingConnection")
    val conn = CallConnection(applicationContext)
    conn.setAddress(request!!.address, PRESENTATION_ALLOWED)
    conn.setInitializing()
    conn.videoProvider = MyVideoProvider()
    conn.setActive()
    return conn
}

override fun onCreateOutgoingConnectionFailed(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?) {
    super.onCreateOutgoingConnectionFailed(connectionManagerPhoneAccount, request)
    Log.i("CallConnectionService", "create outgoing call failed")
}

override fun onCreateIncomingConnection(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?): Connection {
    Log.i("CallConnectionService", "onCreateIncomingConnection")
    val conn = CallConnection(applicationContext)
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
        conn.connectionProperties = Connection.PROPERTY_SELF_MANAGED
    }
    conn.setCallerDisplayName("test call", TelecomManager.PRESENTATION_ALLOWED)
    conn.setAddress(request!!.address, PRESENTATION_ALLOWED)
    conn.setInitializing()
    conn.videoProvider = MyVideoProvider()
    conn.setActive()

    return conn
}

override fun onCreateIncomingConnectionFailed(connectionManagerPhoneAccount: PhoneAccountHandle?, request: ConnectionRequest?) {
    super.onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, request)
    Log.i("CallConnectionService", "create outgoing call failed ")
}
}

我的连接实现如下所示:

    class CallConnection(ctx:Context) : Connection() {

    var ctx:Context = ctx
    val TAG = "CallConnection"

    override fun onShowIncomingCallUi() {
//        super.onShowIncomingCallUi()
        Log.i(TAG, "onShowIncomingCallUi")
        val intent = Intent(Intent.ACTION_MAIN, null)
        intent.flags = Intent.FLAG_ACTIVITY_NO_USER_ACTION or Intent.FLAG_ACTIVITY_NEW_TASK
        intent.setClass(ctx, IncomingCallActivity::class.java!!)
        val pendingIntent = PendingIntent.getActivity(ctx, 1, intent, 0)
        val builder = Notification.Builder(ctx)
        builder.setOngoing(true)
        builder.setPriority(Notification.PRIORITY_HIGH)

        // Set notification content intent to take user to fullscreen UI if user taps on the
        // notification body.
        builder.setContentIntent(pendingIntent)
        // Set full screen intent to trigger display of the fullscreen UI when the notification
        // manager deems it appropriate.
        builder.setFullScreenIntent(pendingIntent, true)

        // Setup notification content.
        builder.setSmallIcon(R.mipmap.ic_launcher)
        builder.setContentTitle("Your notification title")
        builder.setContentText("Your notification content.")

        // Use builder.addAction(..) to add buttons to answer or reject the call.

        val notificationManager = ctx.getSystemService(
                NotificationManager::class.java)

        notificationManager.notify("Call Notification", 37, builder.build())
    }

    override fun onCallAudioStateChanged(state: CallAudioState?) {
        Log.i(TAG, "onCallAudioStateChanged")
    }

    override fun onAnswer() {
        Log.i(TAG, "onAnswer")
    }

    override fun onDisconnect() {
        Log.i(TAG, "onDisconnect")
    }

    override fun onHold() {
        Log.i(TAG, "onHold")
    }

    override fun onUnhold() {
        Log.i(TAG, "onUnhold")
    }

    override fun onReject() {
        Log.i(TAG, "onReject")
    }
}
根据显示用户传入calcustomon UI的文档,我应该在showinMingCallui()方法中执行一些操作。但它只是没有被系统调用


我怎样才能修好它

我能够使用一个测试应用程序和运行在Pixel 2 XL上的Android Pie让它工作

根据我的测试,重要的部分是确保:

  • 已在连接上设置Connection.PROPERTY\u SELF\u MANAGED。至少需要API 26
  • 你必须注册你的电话帐户
  • 注册电话帐户时,您必须设置PhoneAccount.CAPABILITY\u SELF\u在您的功能中管理。这是我设置的唯一功能。设置其他功能导致它抛出异常
  • 最后,您需要确保在AndroidManifest.xml中设置了这两个权限。android.permission.MANAGE_OWN_调用和android.permission.READ_调用日志
因此,我将检查您的清单,以确保您拥有权限,并确保功能设置正确。看起来上面的代码中其他所有内容都设置正确


希望有帮助

你能解决这个问题吗?是的,我已经改变了接听电话的方式,请看这篇文章,我正在尝试使用FCM push添加voip功能。两个问题-当从firebaseMessagingService触发onMessageReceived()时,是否使用CallManager.incomingCall()?另一种情况是,当用户进行调用时(实际上是向其他用户发送推送),是否调用CallManager.startOutgoingCall()?你是如何克服在那种状态下MessageReceived不呼叫时的深度瞌睡的?谢谢您好@WorieN,根据您建议的实现,如果屏幕被锁定,我将获得全屏,但如果屏幕被锁定,我将无法获得全屏unlocked@ShashankSrivastava当使用全屏意图而不是ConnectionService时,你也遇到了同样的问题,你解决了吗?如果我的应用程序在Webview中使用web RTC,我可以使用此回调吗?请参阅我的问题