1

我的任务是找到一种方法(如果有的话)来检测接听者何时接听了拨出电话。

经过一番研究,我尝试了以下方法:

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 中发布应用程序时会出现一些问题,因此这将永远见不到光(不幸的是)。

任何帮助表示赞赏,在此先感谢您。

4

0 回答 0