moltmobile-android/app/src/main/java/com/inou/clawdnode/calls/CallScreener.kt

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)
}
}