首页 > 解决方案 > 来电时,InCallService 的 onCallAdded(Call call) 方法在主线程仍在运行时不会被调用

问题描述

我正在寻找什么解决方案

每次在主线程中调用时,如何调用 InCallService 的 onCallAdded(Call call) 方法(使用#repeat 进行循环)。

背景

我正在编写一个小型 Android 自动测试应用程序来自动拨打电话,然后进行网页浏览……等 10 个周期。

我的代码基于这篇文章:使用 android.telecom 和 InCallService 接听来电

它是如何工作的

  1. 这是 MainActivity:当我单击按钮(autoCall)时,开始重复调用 $autoCallNumber。

import android.Manifest
import android.content.Intent
import android.os.Bundle
import android.os.CountDownTimer
import android.telecom.TelecomManager
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.PermissionChecker
import androidx.core.net.toUri
import kotlinx.android.synthetic.main.activity_call.*
import kotlinx.android.synthetic.main.activity_dialer.*
import kotlinx.android.synthetic.main.activity_main.*
import java.util.concurrent.TimeUnit

class MainActivity : AppCompatActivity() {
    private val TAG = "${javaClass.simpleName} Wynne"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()
        offerReplacingDefaultDialer()

        autoCall.setOnClickListener {
            repeat(2) { i ->
                makeCall()
                Log.d(TAG,"We are on the ${i + 1}. loop")
                TimeUnit.SECONDS.sleep(5);
            }
        }

    }

    private fun makeCall() {
        if (PermissionChecker.checkSelfPermission(this, Manifest.permission.CALL_PHONE) == PermissionChecker.PERMISSION_GRANTED) {
            var User: GeneralSettings = applicationContext as GeneralSettings

            User.ongoingCalltype = "MO"
            val uri = "tel:${User.autoCallNumber}".toUri()
            startActivity(Intent(Intent.ACTION_CALL, uri))

        } else {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.CALL_PHONE),
                DialerActivity.REQUEST_PERMISSION
            )
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        if (requestCode == DialerActivity.REQUEST_PERMISSION && PermissionChecker.PERMISSION_GRANTED in grantResults) {
            makeCall()
        }
    }

    private fun offerReplacingDefaultDialer() {
        if (getSystemService(TelecomManager::class.java).defaultDialerPackage != packageName) {
            Intent(TelecomManager.ACTION_CHANGE_DEFAULT_DIALER)
                .putExtra(TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME, packageName)
                .let(::startActivity)
        }
    }
}
  1. 这是对象:Ongoingcall(让我处理接听或挂断)
package com.github.arekolek.phone

import android.telecom.Call
import android.telecom.VideoProfile
import android.util.Log
import io.reactivex.subjects.BehaviorSubject
import timber.log.Timber

object OngoingCall {
    val state: BehaviorSubject<Int> = BehaviorSubject.create() 
    private val TAG = "${javaClass.simpleName} Wynne"

    private val callback = object : Call.Callback() {
        override fun onStateChanged(call: Call, newState: Int) {
            Log.d(TAG,"${call.toString()}")
            state.onNext(newState) 
            Log.d(TAG,"New state : $newState")

        }
    }

    var call: Call? = null
        set(value) {

            field?.unregisterCallback(callback)
            value?.let {
                it.registerCallback(callback)
                state.onNext(it.state)

            }

            field = value
        }

    fun answer() {
        call!!.answer(VideoProfile.STATE_AUDIO_ONLY)
    }

    fun hangup() {
        call!!.disconnect()
    }
}

  1. 这是 CallService(重写 InCallService 的 onCallAdded/onCallRemoved 以在呼叫到达/移除时处理活动)
package com.github.arekolek.phone

import android.telecom.Call
import android.telecom.InCallService
import android.util.Log

class CallService : InCallService() {
    private val TAG = "${javaClass.simpleName} Wynne"

    override fun onCallAdded(call: Call) {
        OngoingCall.call = call 
        try {
            CallActivity.start(this, call)
            Log.d(TAG,"Start Call Activity")
        }
        catch ( e : Exception ) {
            Log.d(TAG,"$e")
        }

    }

    override fun onCallRemoved(call: Call) {
        OngoingCall.call = null 
    }
}
  1. 这是呼叫活动(更新 UI 上的呼叫信息和挂断未知呼叫...等)
package com.github.arekolek.phone

import android.R.id.button1
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.telecom.Call
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.isVisible
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.rxkotlin.addTo
import kotlinx.android.synthetic.main.activity_call.*
import java.util.concurrent.TimeUnit


class CallActivity : AppCompatActivity() {
    private val TAG = javaClass.simpleName

    private val disposables = CompositeDisposable()

    private lateinit var ongoingCallNumber: String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_call)
        ongoingCallNumber = intent.data.schemeSpecificPart
    }

    override fun onStart() {
        super.onStart()

        var User: GeneralSettings = applicationContext as GeneralSettings

        OngoingCall.state
            .subscribe(::updateUi)
            .addTo(disposables)

        OngoingCall.state
            .filter { it == Call.STATE_DISCONNECTED }
            .delay(1, TimeUnit.SECONDS)
            .firstElement()
            .subscribe { finish() }
            .addTo(disposables)

        if (User.ongoingCalltype == "MO") {
            Handler().postDelayed(Runnable { hangup.performClick() }, 5000)
            Log.d(TAG, "Wynne : End MO call after 5 second")
            User.ongoingCalltype = ""

        } else if (ongoingCallNumber == User.autoAnswerNumber) {
            Handler().post(Runnable { answer.performClick() })
            Log.d(TAG, "Wynne : Answer incoming call ${User.autoAnswerNumber} ")
        }
        else{
            Handler().post(Runnable { hangup.performClick() })
            Log.d(TAG, "Wynne : End Unknown incoming call $ongoingCallNumber ")

        }
    }

    @SuppressLint("SetTextI18n")
    private fun updateUi(state: Int) {
        callInfo.text = "${state.asString().toLowerCase().capitalize()}\n$ongoingCallNumber"

        answer.isVisible = state == Call.STATE_RINGING
        hangup.isVisible = state in listOf(
            Call.STATE_DIALING,
            Call.STATE_RINGING,
            Call.STATE_ACTIVE
        )
    }

    override fun onStop() {
        super.onStop()
        disposables.clear()
    }

    companion object {
        fun start(context: Context, call: Call) {
            Intent(context, CallActivity::class.java)
                .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                .setData(call.details.handle)
                .let(context::startActivity)
        }
    }
}
  1. 这是AndroidManifest(绑定.CallService(重写InCallService))
<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.github.arekolek.phone"
    >

    <uses-permission android:name="android.permission.CALL_PHONE" />

    <application
        android:name=".GeneralSettings"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        >

        <activity
            android:name=".MainActivity"
            android:windowSoftInputMode="stateAlwaysVisible|adjustResize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>

            <intent-filter>
                <!-- Handle links from other applications -->
                <action android:name="android.intent.action.VIEW" />
                <action android:name="android.intent.action.DIAL" />
                <!-- Populate the system chooser -->
                <category android:name="android.intent.category.DEFAULT" />
                <!-- Handle links in browsers -->
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="tel" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.DIAL" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

        <service
            android:name=".CallService"
            android:permission="android.permission.BIND_INCALL_SERVICE">
            <meta-data
                android:name="android.telecom.IN_CALL_SERVICE_UI"
                android:value="true"
                />
            <intent-filter>
                <action android:name="android.telecom.InCallService" />
            </intent-filter>
        </service>

        <activity
            android:name=".CallActivity">
        </activity>
    </application>

</manifest>
  1. 这是GeneralSettings(全局变量)
package com.github.arekolek.phone

import android.app.Application

class GeneralSettings : Application() {
    val autoCallNumber: String = "0988102544"
    var ongoingCalltype: String = ""
    val autoAnswerNumber: String = "0905112980"

}

那有什么问题

CallService 的 onCallAdded() 应该在 #repeat 中的每个循环之后调用 makeCall()。

但是,根据调试消息,我假设在 autoCall.setOnClickListener 完成之前不会调用 CallService 的 onCallAdded() 。

这是调试消息

16:52:24.859 D/MainActivity Wynne: We are on the 1. loop
16:52:29.895 D/MainActivity Wynne: We are on the 2. loop
16:52:35.041 D/CallService Wynne: Start Call Activity
16:52:35.048 D/CallService Wynne: Start Call Activity
16:52:35.116 D/CallActivity: Wynne : End MO call after 5 second
16:52:40.161 D/OngoingCall Wynne: New state : 10
16:52:40.419 D/OngoingCall Wynne: New state : 7
16:52:41.551 D/CallActivity: Wynne : End Unknown incoming call 0988102544 

有人可以告诉我:

每次在主线程中运行 makeCall() 时,如何调用 CallService 的 onCallAdded()?这样我就可以在下一个周期之前断开呼叫。

感谢您花时间阅读我的问题。

标签: androidkotlintelecommunication

解决方案


推荐阅读