mirror of
https://github.com/home-assistant/android
synced 2024-09-19 16:11:37 +00:00
Incoming/Outgoing phone number tracking (#978)
This commit is contained in:
parent
41bacb346a
commit
09a4c5c563
|
@ -15,6 +15,7 @@
|
||||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_CALL_LOG" />
|
||||||
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
|
||||||
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
|
<uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package io.homeassistant.companion.android.sensors
|
||||||
|
|
||||||
import android.Manifest
|
import android.Manifest
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.telephony.SubscriptionInfo
|
import android.telephony.SubscriptionInfo
|
||||||
import android.telephony.SubscriptionManager
|
import android.telephony.SubscriptionManager
|
||||||
|
@ -19,6 +20,13 @@ class PhoneStateSensorManager : SensorManager {
|
||||||
R.string.sensor_description_phone_state
|
R.string.sensor_description_phone_state
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val callNumber = SensorManager.BasicSensor(
|
||||||
|
"call_number",
|
||||||
|
"sensor",
|
||||||
|
R.string.basic_sensor_name_call_number,
|
||||||
|
R.string.sensor_description_call_number
|
||||||
|
)
|
||||||
|
|
||||||
val sim_1 = SensorManager.BasicSensor(
|
val sim_1 = SensorManager.BasicSensor(
|
||||||
"sim_1",
|
"sim_1",
|
||||||
"sensor",
|
"sensor",
|
||||||
|
@ -41,49 +49,77 @@ class PhoneStateSensorManager : SensorManager {
|
||||||
|
|
||||||
override val availableSensors: List<SensorManager.BasicSensor>
|
override val availableSensors: List<SensorManager.BasicSensor>
|
||||||
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1)
|
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1)
|
||||||
listOf(phoneState, sim_1, sim_2)
|
listOf(phoneState, callNumber, sim_1, sim_2)
|
||||||
else listOf(phoneState)
|
else listOf(phoneState, callNumber)
|
||||||
|
|
||||||
override fun requiredPermissions(sensorId: String): Array<String> {
|
override fun requiredPermissions(sensorId: String): Array<String> {
|
||||||
return arrayOf(Manifest.permission.READ_PHONE_STATE)
|
return if (sensorId == callNumber.id) {
|
||||||
|
arrayOf(Manifest.permission.READ_PHONE_STATE, Manifest.permission.READ_CALL_LOG)
|
||||||
|
} else arrayOf(Manifest.permission.READ_PHONE_STATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun requestSensorUpdate(
|
override fun requestSensorUpdate(
|
||||||
context: Context
|
context: Context
|
||||||
) {
|
) {
|
||||||
updatePhoneStateSensor(context)
|
checkPhoneState(context)
|
||||||
updateSimSensor(context, 0)
|
updateSimSensor(context, 0)
|
||||||
updateSimSensor(context, 1)
|
updateSimSensor(context, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updatePhoneStateSensor(context: Context) {
|
private fun checkPhoneState(context: Context) {
|
||||||
if (!isEnabled(context, phoneState.id))
|
if (isEnabled(context, phoneState.id) || isEnabled(context, callNumber.id)) {
|
||||||
return
|
var currentPhoneState = "unknown"
|
||||||
var currentPhoneState = "unavailable"
|
|
||||||
if (checkPermission(context, phoneState.id)) {
|
|
||||||
val telephonyManager =
|
|
||||||
(context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager)
|
|
||||||
|
|
||||||
currentPhoneState = when (telephonyManager.callState) {
|
if (checkPermission(context, phoneState.id)) {
|
||||||
0 -> "idle"
|
val telephonyManager =
|
||||||
1 -> "ringing"
|
(context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager)
|
||||||
2 -> "offhook"
|
|
||||||
else -> "unknown"
|
currentPhoneState = when (telephonyManager.callState) {
|
||||||
|
0 -> "idle"
|
||||||
|
1 -> "ringing"
|
||||||
|
2 -> "offhook"
|
||||||
|
else -> "unknown"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if (isEnabled(context, phoneState.id))
|
||||||
|
updatePhoneStateSensor(context, currentPhoneState)
|
||||||
|
|
||||||
|
if (isEnabled(context, callNumber.id) && currentPhoneState in arrayOf("idle", "unknown"))
|
||||||
|
updateCallNumberSensor(context, "none")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updatePhoneStateSensor(context: Context, state: String) {
|
||||||
var phoneIcon = "mdi:phone"
|
var phoneIcon = "mdi:phone"
|
||||||
if (currentPhoneState == "ringing" || currentPhoneState == "offhook")
|
if (state == "ringing" || state == "offhook")
|
||||||
phoneIcon += "-in-talk"
|
phoneIcon += "-in-talk"
|
||||||
|
|
||||||
onSensorUpdated(context,
|
onSensorUpdated(context,
|
||||||
phoneState,
|
phoneState,
|
||||||
currentPhoneState,
|
state,
|
||||||
phoneIcon,
|
phoneIcon,
|
||||||
mapOf()
|
mapOf()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateCallNumber(context: Context, intent: Intent) {
|
||||||
|
if (checkPermission(context, callNumber.id)) {
|
||||||
|
if (intent.hasExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)) {
|
||||||
|
val number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)
|
||||||
|
updateCallNumberSensor(context, number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateCallNumberSensor(context: Context, number: String) {
|
||||||
|
onSensorUpdated(context,
|
||||||
|
callNumber,
|
||||||
|
number,
|
||||||
|
"mdi:numeric",
|
||||||
|
mapOf()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun updateSimSensor(context: Context, slotIndex: Int) {
|
private fun updateSimSensor(context: Context, slotIndex: Int) {
|
||||||
val basicSimSensor = when (slotIndex) {
|
val basicSimSensor = when (slotIndex) {
|
||||||
0 -> sim_1
|
0 -> sim_1
|
||||||
|
@ -110,7 +146,7 @@ class PhoneStateSensorManager : SensorManager {
|
||||||
attrs["mcc"] = info.mccString.toString()
|
attrs["mcc"] = info.mccString.toString()
|
||||||
attrs["mnc"] = info.mncString.toString()
|
attrs["mnc"] = info.mncString.toString()
|
||||||
attrs["is opportunistic"] = info.isOpportunistic
|
attrs["is opportunistic"] = info.isOpportunistic
|
||||||
if (info.dataRoaming == SubscriptionManager.DATA_ROAMING_ENABLE) attrs["data roaming"] = "enable"
|
if (info.dataRoaming == SubscriptionManager.DATA_ROAMING_ENABLE) attrs["data roaming"] = "enable"
|
||||||
else attrs["data roaming"] = "disable"
|
else attrs["data roaming"] = "disable"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.bluetooth.BluetoothAdapter
|
||||||
import android.content.BroadcastReceiver
|
import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.telephony.TelephonyManager
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
|
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
|
||||||
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
import io.homeassistant.companion.android.common.data.integration.IntegrationRepository
|
||||||
|
@ -57,6 +58,13 @@ class SensorReceiver : BroadcastReceiver() {
|
||||||
Intent.ACTION_POWER_DISCONNECTED
|
Intent.ACTION_POWER_DISCONNECTED
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private val skippableActions = mapOf(
|
||||||
|
"android.app.action.NEXT_ALARM_CLOCK_CHANGED" to NextAlarmManager.nextAlarm.id,
|
||||||
|
"android.bluetooth.device.action.ACL_CONNECTED" to BluetoothSensorManager.bluetoothConnection.id,
|
||||||
|
"android.bluetooth.device.action.ACL_DISCONNECTED" to BluetoothSensorManager.bluetoothConnection.id,
|
||||||
|
BluetoothAdapter.ACTION_STATE_CHANGED to BluetoothSensorManager.bluetoothState.id
|
||||||
|
)
|
||||||
|
|
||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
|
||||||
DaggerSensorComponent.builder()
|
DaggerSensorComponent.builder()
|
||||||
|
@ -64,34 +72,20 @@ class SensorReceiver : BroadcastReceiver() {
|
||||||
.build()
|
.build()
|
||||||
.inject(this)
|
.inject(this)
|
||||||
|
|
||||||
when (intent.action) {
|
if (skippableActions.containsKey(intent.action)) {
|
||||||
"android.app.action.NEXT_ALARM_CLOCK_CHANGED" -> {
|
val sensor = skippableActions[intent.action]
|
||||||
val sensorDao = AppDatabase.getInstance(context).sensorDao()
|
if (!isSensorEnabled(context, sensor!!)) {
|
||||||
val sensor = sensorDao.get(NextAlarmManager.nextAlarm.id)
|
Log.d(TAG, String.format
|
||||||
if (sensor?.enabled != true) {
|
("Sensor %s corresponding to received event %s is disabled, skipping sensors update", sensor, intent.action))
|
||||||
Log.d(TAG, "Alarm Sensor disabled, skipping sensors update")
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"android.bluetooth.device.action.ACL_CONNECTED",
|
|
||||||
"android.bluetooth.device.action.ACL_DISCONNECTED" -> {
|
|
||||||
val sensorDao = AppDatabase.getInstance(context).sensorDao()
|
|
||||||
val sensorBtConn = sensorDao.get(BluetoothSensorManager.bluetoothConnection.id)
|
|
||||||
if (sensorBtConn?.enabled != true) {
|
|
||||||
Log.d(TAG, "Bluetooth Connection Sensor disabled, skipping sensors update")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
BluetoothAdapter.ACTION_STATE_CHANGED -> {
|
|
||||||
val sensorDao = AppDatabase.getInstance(context).sensorDao()
|
|
||||||
val sensorBtState = sensorDao.get(BluetoothSensorManager.bluetoothState.id)
|
|
||||||
if (sensorBtState?.enabled != true) {
|
|
||||||
Log.d(TAG, "Bluetooth State Sensor disabled, skipping sensors update")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (intent.action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED) &&
|
||||||
|
isSensorEnabled(context, PhoneStateSensorManager.callNumber.id)) {
|
||||||
|
PhoneStateSensorManager().updateCallNumber(context, intent)
|
||||||
|
}
|
||||||
|
|
||||||
ioScope.launch {
|
ioScope.launch {
|
||||||
updateSensors(context, integrationUseCase)
|
updateSensors(context, integrationUseCase)
|
||||||
if (chargingActions.contains(intent.action)) {
|
if (chargingActions.contains(intent.action)) {
|
||||||
|
@ -103,6 +97,10 @@ class SensorReceiver : BroadcastReceiver() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isSensorEnabled(context: Context, id: String): Boolean {
|
||||||
|
return AppDatabase.getInstance(context).sensorDao().get(id)?.enabled == true
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun updateSensors(
|
suspend fun updateSensors(
|
||||||
context: Context,
|
context: Context,
|
||||||
integrationUseCase: IntegrationRepository
|
integrationUseCase: IntegrationRepository
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
<string name="basic_sensor_name_battery_state">Battery State</string>
|
<string name="basic_sensor_name_battery_state">Battery State</string>
|
||||||
<string name="basic_sensor_name_bluetooth">Bluetooth Connection</string>
|
<string name="basic_sensor_name_bluetooth">Bluetooth Connection</string>
|
||||||
<string name="basic_sensor_name_bluetooth_state">Bluetooth State</string>
|
<string name="basic_sensor_name_bluetooth_state">Bluetooth State</string>
|
||||||
|
<string name="basic_sensor_name_call_number">Call Number</string>
|
||||||
<string name="basic_sensor_name_charger_type">Charger Type</string>
|
<string name="basic_sensor_name_charger_type">Charger Type</string>
|
||||||
<string name="basic_sensor_name_charging">Is Charging</string>
|
<string name="basic_sensor_name_charging">Is Charging</string>
|
||||||
<string name="basic_sensor_name_doze">Doze Mode</string>
|
<string name="basic_sensor_name_doze">Doze Mode</string>
|
||||||
|
@ -185,6 +186,7 @@ like to connect to:</string>
|
||||||
<string name="sensor_description_battery_state">The current charging state of the battery</string>
|
<string name="sensor_description_battery_state">The current charging state of the battery</string>
|
||||||
<string name="sensor_description_bluetooth_connection">Information about currently connected bluetooth devices</string>
|
<string name="sensor_description_bluetooth_connection">Information about currently connected bluetooth devices</string>
|
||||||
<string name="sensor_description_bluetooth_state">Whether or not bluetooth is enabled on the device</string>
|
<string name="sensor_description_bluetooth_state">Whether or not bluetooth is enabled on the device</string>
|
||||||
|
<string name="sensor_description_call_number">The cell number of the incoming or outgoing call</string>
|
||||||
<string name="sensor_description_charger_type">The type of charger plugged into the device currently</string>
|
<string name="sensor_description_charger_type">The type of charger plugged into the device currently</string>
|
||||||
<string name="sensor_description_charging">Whether or not the device is actively charging</string>
|
<string name="sensor_description_charging">Whether or not the device is actively charging</string>
|
||||||
<string name="sensor_description_detected_activity">The current activity type as computed by Googles Activity Recognition API</string>
|
<string name="sensor_description_detected_activity">The current activity type as computed by Googles Activity Recognition API</string>
|
||||||
|
|
Loading…
Reference in a new issue