From 09a4c5c563edf1f3e73e84a8792f712bebe05089 Mon Sep 17 00:00:00 2001 From: Oleksandr Kapshuk Date: Fri, 2 Oct 2020 00:51:22 +0300 Subject: [PATCH] Incoming/Outgoing phone number tracking (#978) --- app/src/main/AndroidManifest.xml | 1 + .../sensors/PhoneStateSensorManager.kt | 76 ++++++++++++++----- .../android/sensors/SensorReceiver.kt | 48 ++++++------ app/src/main/res/values/strings.xml | 2 + 4 files changed, 82 insertions(+), 45 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ae30f332c..bbf50fb14 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,6 +15,7 @@ + diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/PhoneStateSensorManager.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/PhoneStateSensorManager.kt index 19095c3d4..8e65c0b24 100644 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/PhoneStateSensorManager.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/PhoneStateSensorManager.kt @@ -2,6 +2,7 @@ package io.homeassistant.companion.android.sensors import android.Manifest import android.content.Context +import android.content.Intent import android.os.Build import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager @@ -19,6 +20,13 @@ class PhoneStateSensorManager : SensorManager { 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( "sim_1", "sensor", @@ -41,49 +49,77 @@ class PhoneStateSensorManager : SensorManager { override val availableSensors: List get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) - listOf(phoneState, sim_1, sim_2) - else listOf(phoneState) + listOf(phoneState, callNumber, sim_1, sim_2) + else listOf(phoneState, callNumber) override fun requiredPermissions(sensorId: String): Array { - 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( context: Context ) { - updatePhoneStateSensor(context) + checkPhoneState(context) updateSimSensor(context, 0) updateSimSensor(context, 1) } - private fun updatePhoneStateSensor(context: Context) { - if (!isEnabled(context, phoneState.id)) - return - var currentPhoneState = "unavailable" - if (checkPermission(context, phoneState.id)) { - val telephonyManager = - (context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager) + private fun checkPhoneState(context: Context) { + if (isEnabled(context, phoneState.id) || isEnabled(context, callNumber.id)) { + var currentPhoneState = "unknown" - currentPhoneState = when (telephonyManager.callState) { - 0 -> "idle" - 1 -> "ringing" - 2 -> "offhook" - else -> "unknown" + if (checkPermission(context, phoneState.id)) { + val telephonyManager = + (context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager) + + 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" - if (currentPhoneState == "ringing" || currentPhoneState == "offhook") + if (state == "ringing" || state == "offhook") phoneIcon += "-in-talk" onSensorUpdated(context, phoneState, - currentPhoneState, + state, phoneIcon, 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) { val basicSimSensor = when (slotIndex) { 0 -> sim_1 @@ -110,7 +146,7 @@ class PhoneStateSensorManager : SensorManager { attrs["mcc"] = info.mccString.toString() attrs["mnc"] = info.mncString.toString() 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" } } diff --git a/app/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt b/app/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt index 9fcea0f6d..902dfc72d 100644 --- a/app/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt +++ b/app/src/main/java/io/homeassistant/companion/android/sensors/SensorReceiver.kt @@ -4,6 +4,7 @@ import android.bluetooth.BluetoothAdapter import android.content.BroadcastReceiver import android.content.Context import android.content.Intent +import android.telephony.TelephonyManager import android.util.Log import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor import io.homeassistant.companion.android.common.data.integration.IntegrationRepository @@ -57,6 +58,13 @@ class SensorReceiver : BroadcastReceiver() { 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) { DaggerSensorComponent.builder() @@ -64,34 +72,20 @@ class SensorReceiver : BroadcastReceiver() { .build() .inject(this) - when (intent.action) { - "android.app.action.NEXT_ALARM_CLOCK_CHANGED" -> { - val sensorDao = AppDatabase.getInstance(context).sensorDao() - val sensor = sensorDao.get(NextAlarmManager.nextAlarm.id) - if (sensor?.enabled != true) { - Log.d(TAG, "Alarm Sensor disabled, skipping sensors update") - 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 (skippableActions.containsKey(intent.action)) { + val sensor = skippableActions[intent.action] + if (!isSensorEnabled(context, sensor!!)) { + Log.d(TAG, String.format + ("Sensor %s corresponding to received event %s is disabled, skipping sensors update", sensor, intent.action)) + return } } + if (intent.action.equals(TelephonyManager.ACTION_PHONE_STATE_CHANGED) && + isSensorEnabled(context, PhoneStateSensorManager.callNumber.id)) { + PhoneStateSensorManager().updateCallNumber(context, intent) + } + ioScope.launch { updateSensors(context, integrationUseCase) 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( context: Context, integrationUseCase: IntegrationRepository diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f8efa9be1..fa8c049b9 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -28,6 +28,7 @@ Battery State Bluetooth Connection Bluetooth State + Call Number Charger Type Is Charging Doze Mode @@ -185,6 +186,7 @@ like to connect to: The current charging state of the battery Information about currently connected bluetooth devices Whether or not bluetooth is enabled on the device + The cell number of the incoming or outgoing call The type of charger plugged into the device currently Whether or not the device is actively charging The current activity type as computed by Googles Activity Recognition API