我的任务是找到一种方法(如果有的话)来检测接听者何时接听了拨出电话。
经过一番研究,我尝试了以下方法:
A. PHONE_STATE 动作的 BroadcastReceiver
<receiver android:name=".Interceptor">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
</receiver>
但是当电话被接听时,拨出电话永远不会通知回来。
class Interceptor: BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
try {
when (intent.getStringExtra(TelephonyManager.EXTRA_STATE)) {
TelephonyManager.EXTRA_STATE_RINGING -> {
showToast(context, "ringing")
}
TelephonyManager.EXTRA_STATE_IDLE -> {
showToast(context, "idle")
}
TelephonyManager.EXTRA_STATE_OFFHOOK -> {
showToast(context, "off hook")
}
else -> {
showToast(context, intent.getStringExtra(TelephonyManager.EXTRA_STATE)!!)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun showToast(context: Context, s: String) {
Toast.makeText(context, s, Toast.LENGTH_SHORT).show()
}
}
我知道 READ_PRECISE_PHONE_STATE,但这种权限仅适用于拨号程序、运营商应用程序或 ims 应用程序等应用程序。
B. TelecomManager + ConnectionService
@RequiresApi(Build.VERSION_CODES.M)
fun call(view: View) {
val telecomManager: TelecomManager = getSystemService(TELECOM_SERVICE) as TelecomManager
when {
ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE) != PackageManager.PERMISSION_GRANTED -> {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CALL_PHONE), 2333)
}
ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED -> {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_PHONE_STATE), 2334)
}
else -> {
try {
val phoneAccountHandle = PhoneAccountHandle(ComponentName(applicationContext, MyConnectionService::class.java), "dld")
val build = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
PhoneAccount
.builder(phoneAccountHandle, "1234") // phone account named 1234
.setCapabilities(PhoneAccount.CAPABILITY_CONNECTION_MANAGER or PhoneAccount.CAPABILITY_CALL_PROVIDER)
.build()
} else {
TODO("VERSION.SDK_INT < O")
}
telecomManager.registerPhoneAccount(build)
val parse = Uri.fromParts(PhoneAccount.SCHEME_TEL, PHONENUMBERTOCALL, null) // PHONENUMBERTOCALL == "1112223334", a valid telephone number
val extras = Bundle()
extras.putParcelable(EXTRA_PHONE_ACCOUNT_HANDLE, phoneAccountHandle)
telecomManager.placeCall(parse, extras)
} catch (e: SecurityException) {
e.printStackTrace()
}
}
}
}
@RequiresApi(Build.VERSION_CODES.M)
class MyConnectionService : ConnectionService() {
private val TAG = this@MyConnectionService::javaClass.name
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate: ")
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand: ")
return super.onStartCommand(intent, flags, startId)
}
override fun onCreateIncomingConnection(connectionManagerPhoneAccount: PhoneAccountHandle, request: ConnectionRequest): Connection {
Log.d(TAG, "onCreateIncomingConnection: ")
return super.onCreateIncomingConnection(connectionManagerPhoneAccount, request)
}
override fun onCreateIncomingConnectionFailed(connectionManagerPhoneAccount: PhoneAccountHandle, request: ConnectionRequest) {
Log.d(TAG, "onCreateIncomingConnectionFailed: ")
super.onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, request)
}
override fun onCreateOutgoingConnectionFailed(connectionManagerPhoneAccount: PhoneAccountHandle, request: ConnectionRequest) {
Log.d(TAG, "onCreateOutgoingConnectionFailed: ")
super.onCreateOutgoingConnectionFailed(connectionManagerPhoneAccount, request)
}
@RequiresApi(Build.VERSION_CODES.N_MR1)
override fun onCreateOutgoingConnection(connectionManagerPhoneAccount: PhoneAccountHandle, request: ConnectionRequest): Connection {
Log.d(TAG, "onCreateOutgoingConnection: ")
val connection = MyConnection(false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
connection.connectionProperties = Connection.PROPERTY_SELF_MANAGED
}
connection.setAddress(
request.address,
TelecomManager.PRESENTATION_ALLOWED)
connection.extras = request.extras
connection.setInitialized()
return connection
}
override fun onCreateOutgoingHandoverConnection(fromPhoneAccountHandle: PhoneAccountHandle, request: ConnectionRequest): Connection {
Log.d(TAG, "onCreateOutgoingHandoverConnection: ")
return super.onCreateOutgoingHandoverConnection(fromPhoneAccountHandle, request)
}
override fun onCreateIncomingHandoverConnection(fromPhoneAccountHandle: PhoneAccountHandle, request: ConnectionRequest): Connection {
Log.d(TAG, "onCreateIncomingHandoverConnection: ")
return super.onCreateIncomingHandoverConnection(fromPhoneAccountHandle, request)
}
override fun onHandoverFailed(request: ConnectionRequest, error: Int) {
super.onHandoverFailed(request, error)
Log.d(TAG, "onHandoverFailed: ")
}
override fun onConference(connection1: Connection, connection2: Connection) {
super.onConference(connection1, connection2)
Log.d(TAG, "onConference: ")
}
override fun onRemoteConferenceAdded(conference: RemoteConference) {
super.onRemoteConferenceAdded(conference)
Log.d(TAG, "onRemoteConferenceAdded: ")
}
override fun onRemoteExistingConnectionAdded(connection: RemoteConnection) {
super.onRemoteExistingConnectionAdded(connection)
Log.d(TAG, "onRemoteExistingConnectionAdded: ")
}
override fun onConnectionServiceFocusLost() {
super.onConnectionServiceFocusLost()
Log.d(TAG, "onConnectionServiceFocusLost: ")
}
override fun onConnectionServiceFocusGained() {
super.onConnectionServiceFocusGained()
Log.d(TAG, "onConnectionServiceFocusGained: ")
}
}
@RequiresApi(Build.VERSION_CODES.M)
class MyConnection(isIncoming: Boolean): Connection() {
private val TAG = this@MyConnection::javaClass.name
init {
// Assume all calls are video capable.
// Assume all calls are video capable.
}
override fun onStateChanged(state: Int) {
super.onStateChanged(state)
Log.d(TAG, "onStateChanged: $state")
}
override fun onAnswer() {
super.onAnswer()
this.setActive()
Log.d(TAG, "onAnswer: ")
}
override fun onDisconnect() {
super.onDisconnect()
this.destroy()
Log.d(TAG, "onDisconnect: ")
}
}
但这些回调都不会触发
C.(替代解决方案)读取通话记录
private fun showCallLogs() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_CALL_LOG), PERMISSION_REQUEST_READ_LOGS)
} else {
val sb = StringBuffer()
val cursor = managedQuery(CallLog.Calls.CONTENT_URI, null, null, null, null)
// columns
val number = cursor.getColumnIndex(CallLog.Calls.NUMBER)
val duration = cursor.getColumnIndex(CallLog.Calls.DURATION)
val date = cursor.getColumnIndex(CallLog.Calls.DATE)
val type = cursor.getColumnIndex(CallLog.Calls.TYPE)
sb.append("Call Log : ")
while (cursor.moveToNext()) {
val phoneNumber = cursor.getString(number)
if (phoneNumber == PHONENUMBERTOCALL) {
// -- duration
val callDate = cursor.getString(date)
val callDayTime = Date(callDate.toLong())
// -- duration
if (callDayTime.month in 2..4) {
// -- duration
val callDuration = cursor.getString(duration)
// -- duration
// -- call type
val callType = cursor.getString(type)
val dirCode = callType.toInt()
var dir = ""
when (dirCode) {
CallLog.Calls.OUTGOING_TYPE -> dir = "OUTGOING"
CallLog.Calls.INCOMING_TYPE -> dir = "INCOMING"
CallLog.Calls.MISSED_TYPE -> dir = "MISSED"
}
// -- call type
sb.append("\n call report: \n date: $callDayTime \n call type: $dir \n duration: $callDuration")
sb.append("\n --------------------------------------------------")
}
}
}
cursor.close()
Log.d("INFO", sb.toString())
}
}
此解决方案有效,但在 PlayStore 中发布应用程序时会出现一些问题,因此这将永远见不到光(不幸的是)。
任何帮助表示赞赏,在此先感谢您。