Skip to content

Commit

Permalink
use call receiver for calls, set device profile to watch to auto conf…
Browse files Browse the repository at this point in the history
…irm perms
  • Loading branch information
crc-32 committed Jun 13, 2024
1 parent a51b396 commit af79a7b
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 3 deletions.
21 changes: 18 additions & 3 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
<!-- Required for running in background indefinitely -->
<uses-permission android:name="android.permission.REQUEST_COMPANION_RUN_IN_BACKGROUND" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_USE_DATA_IN_BACKGROUND" />
<uses-permission android:name="android.permission.REQUEST_COMPANION_PROFILE_WATCH" />
<uses-permission
android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"
android:maxSdkVersion="26" />
Expand All @@ -40,7 +41,10 @@

<!-- Incoming call handling -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.MANAGE_ONGOING_CALLS"
tools:ignore="ProtectedPermissions" /> <!-- This is a system permission, but we are an exception when paired -->

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

Expand Down Expand Up @@ -135,11 +139,14 @@
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
<service android:name=".notifications.InCallService"
<!--<service android:name=".notifications.InCallService"
android:permission="android.permission.BIND_INCALL_SERVICE"
android:exported="true">
<meta-data android:name="android.telecom.IN_CALL_SERVICE_UI" android:value="true" />
</service>
<intent-filter>
<action android:name="android.telecom.InCallService"/>
</intent-filter>
</service>-->

<receiver android:name=".receivers.AppStartReceiver" android:exported="true">
<intent-filter>
Expand All @@ -156,11 +163,19 @@
</intent-filter>
</receiver>

<receiver android:name=".receivers.CallReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PHONE_STATE" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>

<receiver
android:name=".bridges.background.BackgroundTimelineFlutterBridge$Receiver"
android:exported="false" />


<provider
android:name="androidx.core.content.FileProvider"
android:authorities="io.rebble.cobble.files"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ class ConnectionUiFlutterBridge @Inject constructor(

val associationRequest = AssociationRequest.Builder()
.addDeviceFilter(filter)
.setDeviceProfile(AssociationRequest.DEVICE_PROFILE_WATCH)
.setSingleDevice(true)
.build()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,9 @@ class SystemHandler @Inject constructor(

val locationManager = getSystemService(context, LocationManager::class.java)
if (locationManager?.isProviderEnabled(LocationManager.GPS_PROVIDER) == true || locationManager?.isProviderEnabled(LocationManager.NETWORK_PROVIDER) == true) platflormFlags.add(PhoneAppVersion.PlatformFlag.GPS)

//TODO: check phone and sms capabilities
platflormFlags.add(PhoneAppVersion.PlatformFlag.Telephony)

return PhoneAppVersion.AppVersionResponse(
UInt.MAX_VALUE,
Expand Down
123 changes: 123 additions & 0 deletions android/app/src/main/kotlin/io/rebble/cobble/receivers/CallReceiver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package io.rebble.cobble.receivers

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import android.provider.ContactsContract
import android.telecom.Call
import android.telephony.TelephonyManager
import io.rebble.cobble.CobbleApplication
import io.rebble.libpebblecommon.packets.PhoneControl
import io.rebble.libpebblecommon.services.PhoneControlService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.actor
import timber.log.Timber
import kotlin.random.Random
import kotlin.random.nextUInt

class CallReceiver: BroadcastReceiver() {
private var lastCookie: UInt? = null

sealed class PhoneState {
data class IncomingCall(val cookie: UInt, val number: String?, val contactName: String?): PhoneState()
data class OutgoingCall(val cookie: UInt, val number: String?, val contactName: String?): PhoneState()
data object CallReceived: PhoneState()
data object CallEnded: PhoneState()
}

lateinit var phoneControlService: PhoneControlService

private val phoneStateChangeActor = GlobalScope.actor<PhoneState> {
for (state in channel) {
Timber.d("Phone state changed: $state")
when (state) {
is PhoneState.IncomingCall -> {
// Incoming call
val cookie = state.cookie
val incomingNumber = state.number
val contactName = state.contactName
lastCookie = cookie
phoneControlService.send(
PhoneControl.IncomingCall(
cookie,
incomingNumber ?: "Unknown",
contactName ?: "",
)
)
}
is PhoneState.OutgoingCall -> {
// Outgoing call
// Needs implementing when firmware supports it
}
is PhoneState.CallReceived -> {
// Call received
lastCookie?.let {
phoneControlService.send(PhoneControl.Start(it))
}
}
is PhoneState.CallEnded -> {
// Call ended
lastCookie?.let {
phoneControlService.send(PhoneControl.End(it))
lastCookie = null
}
}
}
}
}
override fun onReceive(context: Context?, intent: Intent?) {
val injectionComponent = (context!!.applicationContext as CobbleApplication).component
phoneControlService = injectionComponent.createPhoneControlService()


when (intent?.action) {
TelephonyManager.ACTION_PHONE_STATE_CHANGED -> {
val state = intent?.getStringExtra(TelephonyManager.EXTRA_STATE)
val number = intent?.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)
val contactName = number?.let { getContactName(context, it) }

when (state) {
TelephonyManager.EXTRA_STATE_RINGING -> {
phoneStateChangeActor.trySend(PhoneState.IncomingCall(Random.nextUInt(), number, contactName))
}
TelephonyManager.EXTRA_STATE_OFFHOOK -> {
phoneStateChangeActor.trySend(PhoneState.CallReceived)
}
TelephonyManager.EXTRA_STATE_IDLE -> {
phoneStateChangeActor.trySend(PhoneState.CallEnded)
}
}
}
Intent.ACTION_NEW_OUTGOING_CALL -> {
val number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER)
val contactName = number?.let { getContactName(context, it) }
phoneStateChangeActor.trySend(PhoneState.OutgoingCall(Random.nextUInt(), number, contactName))
}
}

}

private fun getContactName(context: Context, number: String): String? {
val cursor = context.contentResolver.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
arrayOf(
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER
),
ContactsContract.CommonDataKinds.Phone.NUMBER + " = ?",
arrayOf(number),
null
)
val name = cursor?.use {
if (it.moveToFirst()) {
it.getString(it.getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME))
} else {
null
}
}
return name
}
}

0 comments on commit af79a7b

Please sign in to comment.