安卓连接服务传入电话

16

我试图在安卓上实现iOS CallKit的行为。 我从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")
    }
}
根据文档显示用户呼入界面-我应该在onShowIncomingCallUi()方法中进行一些操作。但系统没有调用它。我该如何解决?

你能解决这个问题吗? - Shashank Srivastava
是的,我改变了接收电话的方式,请参阅此帖子https://dev59.com/G1QJ5IYBdhLWcg3wu4mx - WorieN
1
我正在尝试使用FCM推送添加VoIP功能。两个问题 - 当firebaseMessagingService触发onMessageReceived()时,您是否使用CallManager.incomingCall()?在其他情况下,当用户拨打电话(实际上向其他用户发送推送)时,您是否调用CallManager.startOutgoingCall()?您如何克服DEEP DOZE,因为在该状态下不会调用onMessageReceived?谢谢! - Alex
嗨@WorieN,按照您建议的实现方式,如果屏幕被锁定,我可以获得全屏,但如果屏幕解锁,则无法获得全屏。 - Shashank Srivastava
@ShashankSrivastava 当使用全屏意图时,您也遇到了同样的问题,而不是ConnectionService,您解决了吗? - famfamfam
嗨@Alex,我遇到了同样的问题,你找到解决方法了吗? - famfamfam
1个回答

7

我使用测试应用程序和在Pixel 2 XL上运行的Android Pie,使其正常工作。

从我的测试中,重要的部分是确保:

  • 连接上设置了Connection.PROPERTY_SELF_MANAGED。需要API 26以上版本。
  • 您必须注册您的电话帐户。
  • 在注册电话帐户时,必须在您的功能中设置PhoneAccount.CAPABILITY_SELF_MANAGED。这是我设置的唯一功能。设置其他功能会导致它抛出异常。
  • 最后,您需要确保在AndroidManifest.xml中设置了此权限:android.permission.MANAGE_OWN_CALLS

因此,我建议您检查清单以确保您具有所需的权限,并确保正确设置了功能。看起来您在上面的代码中已经正确设置了其他所有内容。

希望这能帮到您!


如果我的应用程序在Webview中使用Web RTC,我可以使用这个回调函数吗? - Zar E Ahmer
请看我的问题 https://dev59.com/sr3pa4cB1Zd3GeqPlMiI - Zar E Ahmer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接