108 lines
3.2 KiB
Kotlin
108 lines
3.2 KiB
Kotlin
package com.inou.clawdnode.calls
|
|
|
|
import android.content.ComponentName
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.content.ServiceConnection
|
|
import android.os.IBinder
|
|
import android.telecom.Call
|
|
import android.telecom.CallScreeningService
|
|
import android.util.Log
|
|
import com.inou.clawdnode.ClawdNodeApp
|
|
import com.inou.clawdnode.protocol.CallIncomingEvent
|
|
import com.inou.clawdnode.service.NodeService
|
|
|
|
/**
|
|
* Screens incoming calls before they ring.
|
|
* Sends call info to Gateway for Claude to decide: answer, reject, or let ring.
|
|
*/
|
|
class CallScreener : CallScreeningService() {
|
|
|
|
private val tag = "CallScreener"
|
|
|
|
private var nodeService: NodeService? = null
|
|
private val serviceConnection = object : ServiceConnection {
|
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
|
nodeService = (service as NodeService.LocalBinder).getService()
|
|
}
|
|
override fun onServiceDisconnected(name: ComponentName?) {
|
|
nodeService = null
|
|
}
|
|
}
|
|
|
|
override fun onCreate() {
|
|
super.onCreate()
|
|
Log.i(tag, "CallScreener created")
|
|
|
|
Intent(this, NodeService::class.java).also { intent ->
|
|
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
|
|
}
|
|
}
|
|
|
|
override fun onDestroy() {
|
|
unbindService(serviceConnection)
|
|
super.onDestroy()
|
|
}
|
|
|
|
override fun onScreenCall(callDetails: Call.Details) {
|
|
val number = callDetails.handle?.schemeSpecificPart ?: "Unknown"
|
|
val callId = callDetails.handle?.toString() ?: System.currentTimeMillis().toString()
|
|
|
|
Log.i(tag, "Screening call from: $number")
|
|
|
|
// Look up contact name
|
|
val contactName = lookupContact(number)
|
|
|
|
// Store call details for later action
|
|
ActiveCalls.add(callId, callDetails)
|
|
|
|
// Send event to Gateway
|
|
val event = CallIncomingEvent(
|
|
callId = callId,
|
|
number = number,
|
|
contact = contactName
|
|
)
|
|
nodeService?.sendEvent(event)
|
|
|
|
ClawdNodeApp.instance.auditLog.logCall(
|
|
"CALL_INCOMING",
|
|
number,
|
|
contactName,
|
|
"screening"
|
|
)
|
|
|
|
// Default: let the call ring through
|
|
// Gateway can send call_reject or call_silence command
|
|
// For v0.1, we allow all calls but notify Gateway
|
|
respondToCall(callDetails, CallResponse.Builder()
|
|
.setDisallowCall(false)
|
|
.setSkipCallLog(false)
|
|
.setSkipNotification(false)
|
|
.build()
|
|
)
|
|
}
|
|
|
|
private fun lookupContact(number: String): String? {
|
|
// TODO: Look up in contacts
|
|
// For now, return null (unknown caller)
|
|
return null
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tracks active calls for command handling.
|
|
*/
|
|
object ActiveCalls {
|
|
private val calls = mutableMapOf<String, Call.Details>()
|
|
|
|
fun add(callId: String, details: Call.Details) {
|
|
calls[callId] = details
|
|
}
|
|
|
|
fun get(callId: String): Call.Details? = calls[callId]
|
|
|
|
fun remove(callId: String) {
|
|
calls.remove(callId)
|
|
}
|
|
}
|