首页 > 解决方案 > Android ConnectionService 来电

问题描述

我正在尝试iOS callkit在 Android 上实现行为。我收到来自 firebase 的推送通知,我想向用户显示“来电”屏幕。为此,我使用ConnectionServiceandroid.telecom和其他类。

这是我的呼叫管理器类:

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 ")
}

}

我的 Connection 实现是这样的:

    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 的文档 - 我应该在onShowIncomingCallUi()方法中执行一些操作。但它只是不被系统调用。

我该如何解决?

标签: androidvoipcallkit

解决方案


I was able to get it to work using a test app and Android Pie running on a Pixel 2 XL.

From my testing the important parts are to ensure:

  • That Connection.PROPERTY_SELF_MANAGED is set on the connection. Requires a minimum of API 26.
  • You have to register your phone account.
  • You have to set PhoneAccount.CAPABILITY_SELF_MANAGED in your capabilities when registering the phone account. That is the only capability that I set. Setting other capabilities caused it to throw an exception.
  • Finally, you need to ensure that you have these two permissions set in AndroidManifest.xml. android.permission.MANAGE_OWN_CALLS and android.permission.READ_CALL_LOG

So, I would check your manifest to ensure you have the permissions and also ensure the capabilities are set correctly. It looks like everything else was set correctly in your code above.

Hope that helps!


推荐阅读