Sensor Screen (#742)

* Initial work on settings UI and getting them all to return.

* Basic views + flow done

* No icons in sensor detail.

* Refactor room stuff to make more sense.

* Add permission checking and DB entries for sensors!

* Make sure we have sensors sorted.

* Move LocationBroadcastReceiver.kt into sensors package.

* Move SensorUpdater.kt into sensors package.

* Sensors can now be enabled and disabled and displays data in UI.

* Added Location "Sensors".

* Start a single receiver to handle all sensor based stuff.

* Fix minimal flavor.

* Rework onboarding flow to use Daos

* Add categories to sensors
extract strings
add icon.

* linting

* Fix tests and minimal.

* Fix merge issues.

* Remove unused files.

* Bring phone sensors inline.
This commit is contained in:
Justin Bassett 2020-08-12 21:34:15 -04:00 committed by GitHub
parent a8bd4da0a0
commit d016aff47e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 888 additions and 734 deletions

View file

@ -19,13 +19,12 @@ import androidx.core.text.HtmlCompat
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.background.LocationBroadcastReceiver
import io.homeassistant.companion.android.background.LocationBroadcastReceiverBase
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.domain.authentication.AuthenticationUseCase
import io.homeassistant.companion.android.domain.authentication.SessionState
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.homeassistant.companion.android.domain.url.UrlUseCase
import io.homeassistant.companion.android.sensors.LocationBroadcastReceiver
import io.homeassistant.companion.android.util.UrlHandler
import io.homeassistant.companion.android.util.cancel
import io.homeassistant.companion.android.util.cancelGroupIfNeeded
@ -109,7 +108,7 @@ class MessagingService : FirebaseMessagingService() {
private fun requestAccurateLocationUpdate() {
val intent = Intent(this, LocationBroadcastReceiver::class.java)
intent.action = LocationBroadcastReceiverBase.ACTION_REQUEST_ACCURATE_LOCATION_UPDATE
intent.action = LocationBroadcastReceiver.ACTION_REQUEST_ACCURATE_LOCATION_UPDATE
sendBroadcast(intent)
}
@ -270,7 +269,7 @@ class MessagingService : FirebaseMessagingService() {
data: Map<String, String>
): NotificationCompat.Builder {
var groupNotificationBuilder = NotificationCompat.Builder(this, channelId)
val groupNotificationBuilder = NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_stat_ic_notification)
.setStyle(
NotificationCompat.BigTextStyle()

View file

@ -1,30 +0,0 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.homeassistant.companion.android.util.PermissionManager
class AllSensorsUpdaterImpl(
integrationUseCase: IntegrationUseCase,
appContext: Context
) :
AllSensorsUpdater(integrationUseCase, appContext) {
override suspend fun getManagers(): List<SensorManager> {
val sensorManagers = mutableListOf(
BatterySensorManager(),
NetworkSensorManager(),
NextAlarmManager()
)
if (integrationUseCase.isBackgroundTrackingEnabled() && PermissionManager.checkLocationPermission(appContext)) {
sensorManagers.add(GeocodeSensorManager())
}
if (integrationUseCase.isCallTrackingEnabled() && PermissionManager.checkPhoneStatePermission(appContext)) {
sensorManagers.add(PhoneStateSensorManager())
}
return sensorManagers
}
}

View file

@ -1,12 +1,11 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import android.location.Address
import android.location.Geocoder
import android.util.Log
import com.google.android.gms.location.LocationServices
import com.google.android.gms.tasks.Tasks
import io.homeassistant.companion.android.background.LocationBroadcastReceiverBase
import io.homeassistant.companion.android.domain.integration.Sensor
import io.homeassistant.companion.android.domain.integration.SensorRegistration
import io.homeassistant.companion.android.util.PermissionManager
@ -16,67 +15,56 @@ class GeocodeSensorManager : SensorManager {
private const val TAG = "GeocodeSM"
}
override val name: String
get() = "Geolocation Sensors"
override fun requiredPermissions(): Array<String> {
return PermissionManager.getLocationPermissionArray()
}
override fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>> {
val sensor = getGeocodedLocation(context)
if (sensor != null) {
return listOf(
SensorRegistration(
sensor,
"Geocoded Location"
)
)
}
return emptyList()
return listOf(getGeocodedLocation(context))
}
override fun getSensors(context: Context): List<Sensor<Any>> {
val geocodedSensor = getGeocodedLocation(context)
if (geocodedSensor != null) {
return listOf(geocodedSensor)
}
return emptyList()
}
private fun getGeocodedLocation(context: Context): Sensor<Any>? {
if (!PermissionManager.checkLocationPermission(context)) {
Log.w(TAG, "Tried getting gecoded location without permission.")
return null
}
try {
val locApi = LocationServices.getFusedLocationProviderClient(context)
Tasks.await(locApi.lastLocation)?.let {
if (it.accuracy > LocationBroadcastReceiverBase.MINIMUM_ACCURACY)
return null
Geocoder(context)
.getFromLocation(it.latitude, it.longitude, 1)
.firstOrNull()?.let { address ->
return Sensor(
"geocoded_location",
if (address.maxAddressLineIndex >= 0) address.getAddressLine(0) else "Unknown",
"sensor",
"mdi:map",
mapOf(
"Administrative Area" to address.adminArea,
"Country" to address.countryName,
"ISO Country Code" to address.countryCode,
"Locality" to address.locality,
"Location" to listOf(address.latitude, address.longitude),
"Postal Code" to address.postalCode,
"Sub Administrative Area" to address.subAdminArea,
"Sub Locality" to address.subLocality,
"Sub Thoroughfare" to address.subThoroughfare,
"Thoroughfare" to address.thoroughfare
)
)
}
private fun getGeocodedLocation(context: Context): SensorRegistration<Any> {
var address: Address? = null
if (PermissionManager.checkLocationPermission(context)) {
try {
val locApi = LocationServices.getFusedLocationProviderClient(context)
Tasks.await(locApi.lastLocation)?.let {
if (it.accuracy <= LocationBroadcastReceiver.MINIMUM_ACCURACY)
address = Geocoder(context)
.getFromLocation(it.latitude, it.longitude, 1)
.firstOrNull()
}
} catch (e: Exception) {
// We don't want to crash if the device cannot get a geocoded location
Log.e(TAG, "Issue getting geocoded location ", e)
}
} catch (e: Exception) {
// We don't want to crash if the device cannot get a geocoded location
Log.e(TAG, "Issue getting geocoded location ", e)
}
return null
val attributes = address?.let {
mapOf(
"Administrative Area" to it.adminArea,
"Country" to it.countryName,
"ISO Country Code" to it.countryCode,
"Locality" to it.locality,
"Location" to listOf(it.latitude, it.longitude),
"Postal Code" to it.postalCode,
"Sub Administrative Area" to it.subAdminArea,
"Sub Locality" to it.subLocality,
"Sub Thoroughfare" to it.subThoroughfare,
"Thoroughfare" to it.thoroughfare
)
}.orEmpty()
return SensorRegistration(
"geocoded_location",
address?.getAddressLine(0) ?: "Unknown",
"sensor",
"mdi:map",
attributes,
"Geocoded Location"
)
}
}

View file

@ -1,6 +1,7 @@
package io.homeassistant.companion.android.background
package io.homeassistant.companion.android.sensors
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.location.Location
@ -13,26 +14,82 @@ import com.google.android.gms.location.LocationCallback
import com.google.android.gms.location.LocationRequest
import com.google.android.gms.location.LocationResult
import com.google.android.gms.location.LocationServices
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.database.AppDatabase
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.homeassistant.companion.android.domain.integration.SensorRegistration
import io.homeassistant.companion.android.domain.integration.UpdateLocation
import io.homeassistant.companion.android.util.PermissionManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
class LocationBroadcastReceiver : LocationBroadcastReceiverBase() {
class LocationBroadcastReceiver : BroadcastReceiver(), SensorManager {
override fun setupLocationTracking(context: Context) {
companion object {
const val MINIMUM_ACCURACY = 200
const val ACTION_REQUEST_LOCATION_UPDATES =
"io.homeassistant.companion.android.background.REQUEST_UPDATES"
const val ACTION_REQUEST_ACCURATE_LOCATION_UPDATE =
"io.homeassistant.companion.android.background.REQUEST_ACCURATE_UPDATE"
const val ACTION_PROCESS_LOCATION =
"io.homeassistant.companion.android.background.PROCESS_UPDATES"
const val ACTION_PROCESS_GEO =
"io.homeassistant.companion.android.background.PROCESS_GEOFENCE"
const val ID_BACKGROUND_LOCATION = "location_background"
const val ID_ZONE_LOCATION = "location_zone"
internal const val TAG = "LocBroadcastReceiver"
}
@Inject
lateinit var integrationUseCase: IntegrationUseCase
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO)
override fun onReceive(context: Context, intent: Intent) {
ensureInjected(context)
when (intent.action) {
Intent.ACTION_BOOT_COMPLETED,
ACTION_REQUEST_LOCATION_UPDATES -> setupLocationTracking(context)
ACTION_PROCESS_LOCATION -> handleLocationUpdate(intent)
ACTION_PROCESS_GEO -> handleGeoUpdate(context, intent)
ACTION_REQUEST_ACCURATE_LOCATION_UPDATE -> requestSingleAccurateLocation(context)
else -> Log.w(TAG, "Unknown intent action: ${intent.action}!")
}
}
private fun ensureInjected(context: Context) {
if (context.applicationContext is GraphComponentAccessor) {
DaggerSensorComponent.builder()
.appComponent((context.applicationContext as GraphComponentAccessor).appComponent)
.build()
.inject(this)
} else {
throw Exception("Application Context passed is not of our application!")
}
}
private fun setupLocationTracking(context: Context) {
if (!PermissionManager.checkLocationPermission(context)) {
Log.w(TAG, "Not starting location reporting because of permissions.")
return
}
mainScope.launch {
val sensorDao = AppDatabase.getInstance(context).sensorDao()
ioScope.launch {
try {
removeAllLocationUpdateRequests(context)
if (integrationUseCase.isBackgroundTrackingEnabled())
if (sensorDao.get(ID_BACKGROUND_LOCATION)?.enabled == true)
requestLocationUpdates(context)
if (integrationUseCase.isZoneTrackingEnabled())
if (sensorDao.get(ID_ZONE_LOCATION)?.enabled == true)
requestZoneUpdates(context)
} catch (e: Exception) {
Log.e(TAG, "Issue setting up location tracking", e)
@ -89,7 +146,7 @@ class LocationBroadcastReceiver : LocationBroadcastReceiverBase() {
}
}
override fun handleLocationUpdate(intent: Intent) {
private fun handleLocationUpdate(intent: Intent) {
Log.d(TAG, "Received location update.")
LocationResult.extractResult(intent)?.lastLocation?.let {
if (it.accuracy > MINIMUM_ACCURACY) {
@ -100,7 +157,7 @@ class LocationBroadcastReceiver : LocationBroadcastReceiverBase() {
}
}
override fun handleGeoUpdate(context: Context, intent: Intent) {
private fun handleGeoUpdate(context: Context, intent: Intent) {
Log.d(TAG, "Received geofence update.")
val geofencingEvent = GeofencingEvent.fromIntent(intent)
if (geofencingEvent.hasError()) {
@ -136,7 +193,7 @@ class LocationBroadcastReceiver : LocationBroadcastReceiverBase() {
if (Build.VERSION.SDK_INT >= 26) location.verticalAccuracyMeters.toInt() else 0
)
mainScope.launch {
ioScope.launch {
try {
integrationUseCase.updateLocation(updateLocation)
} catch (e: Exception) {
@ -182,7 +239,7 @@ class LocationBroadcastReceiver : LocationBroadcastReceiverBase() {
return geofencingRequestBuilder.build()
}
override fun requestSingleAccurateLocation(context: Context) {
private fun requestSingleAccurateLocation(context: Context) {
if (!PermissionManager.checkLocationPermission(context)) {
Log.w(TAG, "Not getting single accurate location because of permissions.")
return
@ -224,4 +281,32 @@ class LocationBroadcastReceiver : LocationBroadcastReceiverBase() {
null
)
}
override val name: String
get() = "Location Sensors"
override fun requiredPermissions(): Array<String> {
return PermissionManager.getLocationPermissionArray()
}
override fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>> {
return listOf<SensorRegistration<Any>>(
SensorRegistration(
ID_BACKGROUND_LOCATION,
"",
"",
"mdi:map",
mapOf(),
"Background Location"
),
SensorRegistration(
ID_ZONE_LOCATION,
"",
"",
"mdi:map",
mapOf(),
"Zone Based Location"
)
)
}
}

View file

@ -79,7 +79,7 @@
</activity>
<receiver
android:name=".background.LocationBroadcastReceiver"
android:name=".sensors.LocationBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>

View file

@ -7,6 +7,7 @@ import io.homeassistant.companion.android.onboarding.authentication.Authenticati
import io.homeassistant.companion.android.onboarding.discovery.DiscoveryFragment
import io.homeassistant.companion.android.onboarding.integration.MobileAppIntegrationFragment
import io.homeassistant.companion.android.onboarding.manual.ManualSetupFragment
import io.homeassistant.companion.android.sensors.SensorsSettingsFragment
import io.homeassistant.companion.android.settings.SettingsActivity
import io.homeassistant.companion.android.settings.SettingsFragment
import io.homeassistant.companion.android.settings.shortcuts.ShortcutsFragment
@ -35,4 +36,6 @@ interface PresenterComponent {
fun inject(activity: WebViewActivity)
fun inject(dialog: SsidDialogFragment)
fun inject(fragment: SensorsSettingsFragment)
}

View file

@ -1,5 +0,0 @@
package io.homeassistant.companion.android
interface SensorUpdater {
suspend fun updateSensors()
}

View file

@ -1,64 +0,0 @@
package io.homeassistant.companion.android.background
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
abstract class LocationBroadcastReceiverBase : BroadcastReceiver() {
companion object {
const val MINIMUM_ACCURACY = 200
const val ACTION_REQUEST_LOCATION_UPDATES =
"io.homeassistant.companion.android.background.REQUEST_UPDATES"
const val ACTION_REQUEST_ACCURATE_LOCATION_UPDATE =
"io.homeassistant.companion.android.background.REQUEST_ACCURATE_UPDATE"
const val ACTION_PROCESS_LOCATION =
"io.homeassistant.companion.android.background.PROCESS_UPDATES"
const val ACTION_PROCESS_GEO =
"io.homeassistant.companion.android.background.PROCESS_GEOFENCE"
internal const val TAG = "LocBroadcastReceiver"
}
@Inject
lateinit var integrationUseCase: IntegrationUseCase
internal val mainScope: CoroutineScope = CoroutineScope(Dispatchers.Main + Job())
override fun onReceive(context: Context, intent: Intent) {
ensureInjected(context)
when (intent.action) {
Intent.ACTION_BOOT_COMPLETED,
ACTION_REQUEST_LOCATION_UPDATES -> setupLocationTracking(context)
ACTION_PROCESS_LOCATION -> handleLocationUpdate(intent)
ACTION_PROCESS_GEO -> handleGeoUpdate(context, intent)
ACTION_REQUEST_ACCURATE_LOCATION_UPDATE -> requestSingleAccurateLocation(context)
else -> Log.w(TAG, "Unknown intent action: ${intent.action}!")
}
}
private fun ensureInjected(context: Context) {
if (context.applicationContext is GraphComponentAccessor) {
DaggerReceiverComponent.builder()
.appComponent((context.applicationContext as GraphComponentAccessor).appComponent)
.build()
.inject(this)
} else {
throw Exception("Application Context passed is not of our application!")
}
}
internal abstract fun setupLocationTracking(context: Context)
internal abstract fun handleLocationUpdate(intent: Intent)
internal abstract fun handleGeoUpdate(context: Context, intent: Intent)
internal abstract fun requestSingleAccurateLocation(context: Context)
}

View file

@ -1,10 +0,0 @@
package io.homeassistant.companion.android.background
import dagger.Component
import io.homeassistant.companion.android.common.dagger.AppComponent
@Component(dependencies = [AppComponent::class])
interface ReceiverComponent {
fun inject(receiver: LocationBroadcastReceiverBase)
}

View file

@ -1,9 +0,0 @@
package io.homeassistant.companion.android.database
import androidx.room.Database
import androidx.room.RoomDatabase
@Database(entities = [AuthenticationList::class], version = 1)
abstract class AppDataBase : RoomDatabase() {
abstract fun authenticationDatabaseDao(): AuthenticationDataBaseDao
}

View file

@ -0,0 +1,39 @@
package io.homeassistant.companion.android.database
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import io.homeassistant.companion.android.database.authentication.Authentication
import io.homeassistant.companion.android.database.authentication.AuthenticationDao
import io.homeassistant.companion.android.database.sensor.Sensor
import io.homeassistant.companion.android.database.sensor.SensorDao
@Database(
entities = [
Authentication::class,
Sensor::class
],
version = 1
)
abstract class AppDatabase : RoomDatabase() {
abstract fun authenticationDao(): AuthenticationDao
abstract fun sensorDao(): SensorDao
companion object {
private const val DATABASE_NAME = "HomeAssistantDB"
@Volatile private var instance: AppDatabase? = null
fun getInstance(context: Context): AppDatabase {
return instance ?: synchronized(this) {
instance ?: buildDatabase(context).also { instance = it }
}
}
private fun buildDatabase(context: Context): AppDatabase {
return Room
.databaseBuilder(context, AppDatabase::class.java, DATABASE_NAME)
.allowMainThreadQueries()
.build()
}
}
}

View file

@ -1,11 +1,11 @@
package io.homeassistant.companion.android.database
package io.homeassistant.companion.android.database.authentication
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "Authentication_List")
data class AuthenticationList(
data class Authentication(
@PrimaryKey
var host: String,

View file

@ -1,4 +1,4 @@
package io.homeassistant.companion.android.database
package io.homeassistant.companion.android.database.authentication
import androidx.room.Dao
import androidx.room.Insert
@ -6,16 +6,16 @@ import androidx.room.Query
import androidx.room.Update
@Dao
interface AuthenticationDataBaseDao {
interface AuthenticationDao {
@Insert
fun insert(authentication: AuthenticationList)
fun insert(authentication: Authentication)
@Update
fun update(authentication: AuthenticationList)
fun update(authentication: Authentication)
@Query("SELECT * from Authentication_List WHERE Host = :key")
fun get(key: String): AuthenticationList?
fun get(key: String): Authentication?
@Query("DELETE FROM Authentication_List")
fun clear()

View file

@ -0,0 +1,17 @@
package io.homeassistant.companion.android.database.sensor
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "sensors")
data class Sensor(
@PrimaryKey
var unique_id: String,
@ColumnInfo(name = "enabled")
var enabled: Boolean,
@ColumnInfo(name = "registered")
var registered: Boolean,
@ColumnInfo(name = "state")
var state: String
)

View file

@ -0,0 +1,19 @@
package io.homeassistant.companion.android.database.sensor
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
@Dao
interface SensorDao {
@Query("SELECT * FROM Sensors WHERE unique_id = :uniqueId")
fun get(uniqueId: String): Sensor?
@Insert
fun add(sensor: Sensor)
@Update
fun update(sensor: Sensor)
}

View file

@ -21,6 +21,10 @@ import io.homeassistant.companion.android.DaggerPresenterComponent
import io.homeassistant.companion.android.PresenterModule
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.database.AppDatabase
import io.homeassistant.companion.android.database.sensor.Sensor
import io.homeassistant.companion.android.sensors.LocationBroadcastReceiver
import io.homeassistant.companion.android.sensors.PhoneStateSensorManager
import io.homeassistant.companion.android.util.PermissionManager
import javax.inject.Inject
import kotlinx.android.synthetic.main.fragment_mobile_app_integration.*
@ -82,7 +86,7 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
zoneTracking = findViewById<SwitchCompat>(R.id.location_zone).apply {
setOnCheckedChangeListener { _, isChecked ->
presenter.onToggleZoneTracking(isChecked)
updateSensorDao(LocationBroadcastReceiver.ID_ZONE_LOCATION, isChecked)
}
isEnabled = hasLocationPermission
isChecked = hasLocationPermission
@ -92,7 +96,7 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
backgroundTracking = findViewById<SwitchCompat>(R.id.location_background).apply {
setOnCheckedChangeListener { _, isChecked ->
presenter.onToggleBackgroundTracking(isChecked)
updateSensorDao(LocationBroadcastReceiver.ID_BACKGROUND_LOCATION, isChecked)
}
isEnabled = hasLocationPermission
isChecked = hasLocationPermission && isIgnoringBatteryOptimizations()
@ -111,7 +115,7 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
callTracking = findViewById<SwitchCompat>(R.id.call_tracking).apply {
setOnCheckedChangeListener { _, isChecked ->
presenter.onToggleCallTracking(isChecked)
updateSensorDao(PhoneStateSensorManager.ID_PHONE, isChecked)
}
isEnabled = hasPhoneStatePermission
isChecked = hasPhoneStatePermission
@ -175,7 +179,7 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
zoneTracking.isEnabled = true
zoneTrackingSummary.isEnabled = true
zoneTracking.isChecked = true
presenter.onToggleZoneTracking(true)
updateSensorDao(LocationBroadcastReceiver.ID_ZONE_LOCATION, true)
backgroundTracking.isEnabled = true
backgroundTrackingSummary.isEnabled = true
@ -194,7 +198,7 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
callTracking.isEnabled = true
callTracking.isChecked = true
callTrackingSummary.isEnabled = true
presenter.onToggleCallTracking(true)
updateSensorDao(PhoneStateSensorManager.ID_PHONE, true)
} else {
callTracking.isEnabled = false
callTrackingSummary.isEnabled = false
@ -205,7 +209,7 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == BACKGROUND_REQUEST && isIgnoringBatteryOptimizations()) {
zoneTracking.isChecked = true
presenter.onToggleBackgroundTracking(true)
updateSensorDao(LocationBroadcastReceiver.ID_BACKGROUND_LOCATION, true)
}
}
@ -227,4 +231,21 @@ class MobileAppIntegrationFragment : Fragment(), MobileAppIntegrationView {
?.isIgnoringBatteryOptimizations(activity?.packageName ?: "")
?: false
}
private fun updateSensorDao(uniqueId: String, isChecked: Boolean) {
val sensorDao = AppDatabase.getInstance(requireContext()).sensorDao()
var sensor = sensorDao.get(uniqueId)
if (sensor == null) {
sensor = Sensor(
uniqueId,
isChecked,
false,
""
)
sensorDao.add(sensor)
} else {
sensor.enabled = isChecked
sensorDao.update(sensor)
}
}
}

View file

@ -2,8 +2,5 @@ package io.homeassistant.companion.android.onboarding.integration
interface MobileAppIntegrationPresenter {
fun onRegistrationAttempt(simple: Boolean)
fun onToggleZoneTracking(enabled: Boolean)
fun onToggleBackgroundTracking(enabled: Boolean)
fun onToggleCallTracking(enabled: Boolean)
fun onFinish()
}

View file

@ -51,24 +51,6 @@ open class MobileAppIntegrationPresenterBase constructor(
}
}
override fun onToggleZoneTracking(enabled: Boolean) {
mainScope.launch {
integrationUseCase.setZoneTrackingEnabled(enabled)
}
}
override fun onToggleBackgroundTracking(enabled: Boolean) {
mainScope.launch {
integrationUseCase.setBackgroundTrackingEnabled(enabled)
}
}
override fun onToggleCallTracking(enabled: Boolean) {
mainScope.launch {
integrationUseCase.setCallTrackingEnabled(enabled)
}
}
override fun onFinish() {
mainScope.cancel()
}

View file

@ -1,60 +0,0 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import android.content.Intent
import android.util.Log
import io.homeassistant.companion.android.SensorUpdater
import io.homeassistant.companion.android.background.LocationBroadcastReceiver
import io.homeassistant.companion.android.background.LocationBroadcastReceiverBase
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
abstract class AllSensorsUpdater(
internal val integrationUseCase: IntegrationUseCase,
internal val appContext: Context
) : SensorUpdater {
companion object {
internal const val TAG = "AllSensorsUpdaterImpl"
}
abstract suspend fun getManagers(): List<SensorManager>
override suspend fun updateSensors() {
// When we update the sensors make sure to request an accurate location.
val intent = Intent(appContext, LocationBroadcastReceiver::class.java)
intent.action = LocationBroadcastReceiverBase.ACTION_REQUEST_ACCURATE_LOCATION_UPDATE
appContext.sendBroadcast(intent)
val sensorManagers = getManagers()
registerSensors(sensorManagers)
var success = false
try {
success = integrationUseCase.updateSensors(
sensorManagers.flatMap { it.getSensors(appContext) }.toTypedArray()
)
} catch (e: Exception) {
Log.e(TAG, "Exception while updating sensors.", e)
}
// We failed to update a sensor, we should register all the sensors again.
if (!success) {
registerSensors(sensorManagers)
}
}
private suspend fun registerSensors(sensorManagers: List<SensorManager>) {
sensorManagers.flatMap {
it.getSensorRegistrations(appContext)
}.forEach {
// I want to call this async but because of the way we need to store the
// fact we have registered it we can't
try {
integrationUseCase.registerSensor(it)
} catch (e: Exception) {
Log.e(TAG, "Issue registering sensor: ${it.uniqueId}", e)
}
}
}
}

View file

@ -5,7 +5,6 @@ import android.content.Intent
import android.content.IntentFilter
import android.os.BatteryManager
import android.util.Log
import io.homeassistant.companion.android.domain.integration.Sensor
import io.homeassistant.companion.android.domain.integration.SensorRegistration
class BatterySensorManager : SensorManager {
@ -14,51 +13,29 @@ class BatterySensorManager : SensorManager {
const val TAG = "BatterySensor"
}
override val name: String
get() = "Battery Sensors"
override fun requiredPermissions(): Array<String> {
return emptyArray()
}
override fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>> {
return context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))?.let {
val retVal = ArrayList<SensorRegistration<Any>>()
getBatteryLevelSensor(it)?.let { sensor ->
retVal.add(
SensorRegistration(
sensor,
"Battery Level",
"battery",
"%"
)
)
retVal.add(sensor)
}
getBatteryStateSensor(it)?.let { sensor ->
retVal.add(
SensorRegistration(
sensor,
"Battery State",
"battery"
)
)
retVal.add(sensor)
}
return@let retVal
} ?: listOf()
}
override fun getSensors(context: Context): List<Sensor<Any>> {
val retVal = ArrayList<Sensor<Any>>()
context.registerReceiver(null, IntentFilter(Intent.ACTION_BATTERY_CHANGED))?.let {
getBatteryLevelSensor(it)?.let { sensor ->
retVal.add(sensor)
}
getBatteryStateSensor(it)?.let { sensor ->
retVal.add(sensor)
}
}
return retVal
}
private fun getBatteryPercentage(level: Int, scale: Int): Int {
return (level.toFloat() / scale.toFloat() * 100.0f).toInt()
}
@ -88,7 +65,7 @@ class BatterySensorManager : SensorManager {
return batteryIcon
}
private fun getBatteryLevelSensor(intent: Intent): Sensor<Any>? {
private fun getBatteryLevelSensor(intent: Intent): SensorRegistration<Any>? {
val level: Int = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale: Int = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
@ -103,16 +80,19 @@ class BatterySensorManager : SensorManager {
val chargerType = getChargerType(intent)
val chargingStatus = getChargingStatus(intent)
return Sensor(
return SensorRegistration(
"battery_level",
percentage,
"sensor",
getBatteryIcon(percentage, isCharging, chargerType, chargingStatus),
mapOf()
mapOf(),
"Battery Level",
"battery",
"%"
)
}
private fun getBatteryStateSensor(intent: Intent): Sensor<Any>? {
private fun getBatteryStateSensor(intent: Intent): SensorRegistration<Any>? {
val level: Int = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1)
val scale: Int = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1)
val status: Int = intent.getIntExtra(BatteryManager.EXTRA_STATUS, -1)
@ -129,7 +109,7 @@ class BatterySensorManager : SensorManager {
val percentage: Int = getBatteryPercentage(level, scale)
return Sensor(
return SensorRegistration(
"battery_state",
chargingStatus,
"sensor",
@ -138,7 +118,9 @@ class BatterySensorManager : SensorManager {
"is_charging" to isCharging,
"charger_type" to chargerType,
"battery_health" to batteryHealth
)
),
"Battery State",
"battery"
)
}

View file

@ -18,11 +18,11 @@ class ChargingBroadcastReceiver(
override fun onReceive(context: Context, intent: Intent?) {
updateJob?.cancel()
updateJob = ioScope.launch {
AllSensorsUpdaterImpl(integrationUseCase, context).updateSensors()
SensorReceiver().updateSensors(context, integrationUseCase)
// Add a 5 second delay to perform another update so charging state updates completely.
// This is necessary as the system needs a few seconds to verify the charger.
delay(5000L)
AllSensorsUpdaterImpl(integrationUseCase, context).updateSensors()
SensorReceiver().updateSensors(context, integrationUseCase)
}
}
}

View file

@ -1,9 +1,8 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import android.net.wifi.WifiInfo
import android.net.wifi.WifiManager
import android.util.Log
import io.homeassistant.companion.android.domain.integration.Sensor
import io.homeassistant.companion.android.domain.integration.SensorRegistration
import io.homeassistant.companion.android.util.PermissionManager
@ -12,52 +11,41 @@ class NetworkSensorManager : SensorManager {
private const val TAG = "NetworkSM"
}
override val name: String
get() = "Network Sensors"
override fun requiredPermissions(): Array<String> {
return PermissionManager.getLocationPermissionArray()
}
override fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>> {
val sensorRegistrations = mutableListOf<SensorRegistration<Any>>()
getWifiConnectionSensor(context)?.let {
sensorRegistrations.add(
SensorRegistration(
it,
"Wifi Connection"
)
)
}
return sensorRegistrations
return listOf(getWifiConnectionSensor(context))
}
override fun getSensors(context: Context): List<Sensor<Any>> {
val sensors = mutableListOf<Sensor<Any>>()
private fun getWifiConnectionSensor(context: Context): SensorRegistration<Any> {
var conInfo: WifiInfo? = null
var ssid = "Unknown"
var lastScanStrength = -1
var wifiEnabled = false
getWifiConnectionSensor(context)?.let {
sensors.add(it)
if (PermissionManager.checkLocationPermission(context)) {
val wifiManager =
(context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager)
conInfo = wifiManager.connectionInfo
wifiEnabled = wifiManager.isWifiEnabled
ssid = if (conInfo.networkId == -1) {
"<not connected>"
} else {
conInfo.ssid.removePrefix("\"").removeSuffix("\"")
}
lastScanStrength = wifiManager.scanResults.firstOrNull {
it.BSSID == conInfo.bssid
}?.level ?: -1
}
return sensors
}
private fun getWifiConnectionSensor(context: Context): Sensor<Any>? {
if (!PermissionManager.checkLocationPermission(context)) {
Log.w(TAG, "Tried getting wifi info without permission.")
return null
}
val wifiManager =
(context.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager)
val conInfo = wifiManager.connectionInfo
val wifiEnabled = wifiManager.isWifiEnabled
val ssid = if (conInfo.networkId == -1) {
"<not connected>"
} else {
conInfo.ssid.removePrefix("\"").removeSuffix("\"")
}
val lastScanStrength = wifiManager.scanResults.firstOrNull {
it.BSSID == conInfo.bssid
}?.level ?: -1
var signalStrength = -1
if (lastScanStrength != -1) {
signalStrength = WifiManager.calculateSignalLevel(lastScanStrength, 4)
@ -69,11 +57,7 @@ class NetworkSensorManager : SensorManager {
else -> signalStrength
}
return Sensor(
"wifi_connection",
ssid,
"sensor",
icon,
val attributes = conInfo?.let {
mapOf(
"bssid" to conInfo.bssid,
"ip_address" to getIpAddress(conInfo.ipAddress),
@ -83,6 +67,15 @@ class NetworkSensorManager : SensorManager {
"frequency" to conInfo.frequency,
"signal_level" to lastScanStrength
)
}.orEmpty()
return SensorRegistration(
"wifi_connection",
ssid,
"sensor",
icon,
attributes,
"Wifi Connection"
)
}

View file

@ -3,7 +3,6 @@ package io.homeassistant.companion.android.sensors
import android.app.AlarmManager
import android.content.Context
import android.util.Log
import io.homeassistant.companion.android.domain.integration.Sensor
import io.homeassistant.companion.android.domain.integration.SensorRegistration
import java.text.SimpleDateFormat
import java.util.Calendar
@ -17,33 +16,18 @@ class NextAlarmManager : SensorManager {
private const val TAG = "NextAlarm"
}
override val name: String
get() = "Alarm Sensors"
override fun requiredPermissions(): Array<String> {
return emptyArray()
}
override fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>> {
val sensorRegistrations = mutableListOf<SensorRegistration<Any>>()
getNextAlarm(context)?.let {
sensorRegistrations.add(
SensorRegistration(
it,
"Next Alarm",
"timestamp"
)
)
}
return sensorRegistrations
return listOf(getNextAlarm(context))
}
override fun getSensors(context: Context): List<Sensor<Any>> {
val sensors = mutableListOf<Sensor<Any>>()
getNextAlarm(context)?.let {
sensors.add(it)
}
return sensors
}
private fun getNextAlarm(context: Context): Sensor<Any>? {
private fun getNextAlarm(context: Context): SensorRegistration<Any> {
var triggerTime = 0L
var local = ""
@ -74,7 +58,7 @@ class NextAlarmManager : SensorManager {
val icon = "mdi:alarm"
return Sensor(
return SensorRegistration(
"next_alarm",
utc,
"sensor",
@ -83,7 +67,9 @@ class NextAlarmManager : SensorManager {
"Local Time" to local,
"Time in Milliseconds" to triggerTime,
"Package" to pendingIntent
)
),
"Next Alarm",
"timestamp"
)
}
}

View file

@ -12,12 +12,12 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
class NextAlarmReceiver() : BroadcastReceiver() {
class NextAlarmReceiver : BroadcastReceiver() {
@Inject
lateinit var integrationUseCase: IntegrationUseCase
private val ioScope = CoroutineScope(Dispatchers.IO)
var updateJob: Job? = null
private var updateJob: Job? = null
override fun onReceive(context: Context, intent: Intent) {
val isBootIntent = Intent.ACTION_BOOT_COMPLETED.equals(intent.action, ignoreCase = true)
val isNextAlarmIntent =
@ -34,7 +34,7 @@ class NextAlarmReceiver() : BroadcastReceiver() {
updateJob?.cancel()
updateJob = ioScope.launch {
AllSensorsUpdaterImpl(integrationUseCase, context).updateSensors()
SensorReceiver().updateSensors(context, integrationUseCase)
}
}
}

View file

@ -19,7 +19,7 @@ class PhoneStateReceiver(
if (PermissionManager.checkPhoneStatePermission(context)) {
updateJob?.cancel()
updateJob = ioScope.launch {
AllSensorsUpdaterImpl(integrationUseCase, context).updateSensors()
SensorReceiver().updateSensors(context, integrationUseCase)
}
}
}

View file

@ -2,68 +2,52 @@ package io.homeassistant.companion.android.sensors
import android.content.Context
import android.telephony.TelephonyManager
import android.util.Log
import io.homeassistant.companion.android.domain.integration.Sensor
import io.homeassistant.companion.android.domain.integration.SensorRegistration
import io.homeassistant.companion.android.util.PermissionManager
class PhoneStateSensorManager : SensorManager {
companion object {
const val ID_PHONE = "phone_state"
private const val TAG = "PhoneStateSM"
}
override val name: String
get() = "Phone Sensors"
override fun requiredPermissions(): Array<String> {
return PermissionManager.getPhonePermissionArray()
}
override fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>> {
val sensorRegistrations = mutableListOf<SensorRegistration<Any>>()
getPhoneStateSensor(context)?.let {
sensorRegistrations.add(
SensorRegistration(
it,
"Phone State"
)
)
}
return sensorRegistrations
return listOf(getPhoneStateSensor(context))
}
override fun getSensors(context: Context): List<Sensor<Any>> {
val sensors = mutableListOf<Sensor<Any>>()
private fun getPhoneStateSensor(context: Context): SensorRegistration<Any> {
var phoneState = "unavailable"
if (PermissionManager.checkPhoneStatePermission(context)) {
val telephonyManager =
(context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager)
getPhoneStateSensor(context)?.let {
sensors.add(it)
}
return sensors
}
private fun getPhoneStateSensor(context: Context): Sensor<Any>? {
if (!PermissionManager.checkPhoneStatePermission(context)) {
Log.w(TAG, "Tried getting phone state without permission.")
return null
}
val telephonyManager = (context.applicationContext.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager)
val phoneState: String = when (telephonyManager.callState) {
0 -> "idle"
1 -> "ringing"
2 -> "offhook"
else -> "unknown"
phoneState = when (telephonyManager.callState) {
0 -> "idle"
1 -> "ringing"
2 -> "offhook"
else -> "unknown"
}
}
var phoneIcon = "mdi:phone"
if (phoneState == "ringing" || phoneState == "offhook")
phoneIcon += "-in-talk"
return Sensor(
"phone_state",
return SensorRegistration(
ID_PHONE,
phoneState,
"sensor",
phoneIcon,
mapOf()
mapOf(),
"Phone State"
)
}
}

View file

@ -7,8 +7,16 @@ import io.homeassistant.companion.android.common.dagger.AppComponent
@Component(dependencies = [AppComponent::class], modules = [PresenterModule::class])
interface SensorComponent {
fun inject(locationBroadcastReceiver: LocationBroadcastReceiver)
fun inject(worker: SensorWorker)
fun inject(sensorReceiver: SensorReceiver)
fun inject(sensorsSettingsFragment: SensorsSettingsFragment)
fun inject(sensorDetailFragment: SensorDetailFragment)
fun inject(receiver: NextAlarmReceiver)
fun inject(receiver: WifiStateReceiver)

View file

@ -0,0 +1,144 @@
package io.homeassistant.companion.android.sensors
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.database.AppDatabase
import io.homeassistant.companion.android.database.sensor.Sensor
import io.homeassistant.companion.android.database.sensor.SensorDao
import io.homeassistant.companion.android.domain.integration.SensorRegistration
import io.homeassistant.companion.android.util.PermissionManager
class SensorDetailFragment(
private val sensorRegistration: SensorRegistration<Any>,
private val permissions: Array<String>
) :
PreferenceFragmentCompat() {
companion object {
fun newInstance(
sensorRegistration: SensorRegistration<Any>,
permissions: Array<String>
): SensorDetailFragment {
return SensorDetailFragment(sensorRegistration, permissions)
}
}
private lateinit var sensorDao: SensorDao
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
DaggerSensorComponent
.builder()
.appComponent((activity?.application as GraphComponentAccessor).appComponent)
.build()
.inject(this)
sensorDao = AppDatabase.getInstance(requireContext()).sensorDao()
addPreferencesFromResource(R.xml.sensor_detail)
findPreference<SwitchPreference>("enabled")?.let {
val dao = sensorDao.get(sensorRegistration.uniqueId)
val perm = havePermission()
if (dao == null) {
updateSensorEntity(perm)
it.isChecked = perm
} else {
it.isChecked = dao.enabled
}
it.setOnPreferenceChangeListener { _, newState ->
val isEnabled = newState as Boolean
if (isEnabled && !havePermission()) {
requestPermissions(permissions, 0)
return@setOnPreferenceChangeListener false
}
updateSensorEntity(isEnabled)
return@setOnPreferenceChangeListener true
}
}
refreshSensorData()
}
private fun refreshSensorData() {
findPreference<Preference>("unique_id")?.let {
it.summary = sensorRegistration.uniqueId
}
findPreference<Preference>("state")?.let {
if (sensorRegistration.unitOfMeasurement.isNullOrBlank())
it.summary = sensorRegistration.state.toString()
else
it.summary =
sensorRegistration.state.toString() + " " + sensorRegistration.unitOfMeasurement
}
findPreference<Preference>("device_class")?.let {
it.summary = sensorRegistration.deviceClass
}
findPreference<Preference>("icon")?.let {
it.summary = sensorRegistration.icon
}
findPreference<PreferenceCategory>("attributes")?.let {
if (sensorRegistration.attributes.isEmpty())
it.isVisible = false
else {
sensorRegistration.attributes.keys.forEach { key ->
val pref = Preference(requireContext())
pref.title = key
pref.summary = sensorRegistration.attributes[key]?.toString() ?: ""
pref.isIconSpaceReserved = false
it.addPreference(pref)
}
}
}
}
private fun updateSensorEntity(
isEnabled: Boolean
) {
var sensorEntity = sensorDao.get(sensorRegistration.uniqueId)
if (sensorEntity != null) {
sensorEntity.enabled = isEnabled
sensorEntity.state = sensorRegistration.state.toString()
sensorDao.update(sensorEntity)
} else {
sensorEntity = Sensor(
sensorRegistration.uniqueId,
isEnabled,
false,
sensorRegistration.state.toString()
)
sensorDao.add(sensorEntity)
}
}
private fun havePermission(): Boolean {
if (!permissions.isNullOrEmpty()) {
return permissions.all { PermissionManager.hasPermission(requireContext(), it) }
}
return true
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
findPreference<SwitchPreference>("enabled")?.run {
isChecked = grantResults.all { it == PackageManager.PERMISSION_GRANTED }
updateSensorEntity(isChecked)
}
}
}

View file

@ -1,12 +1,13 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import io.homeassistant.companion.android.domain.integration.Sensor
import io.homeassistant.companion.android.domain.integration.SensorRegistration
interface SensorManager {
fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>>
val name: String
fun getSensors(context: Context): List<Sensor<Any>>
fun requiredPermissions(): Array<String>
fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>>
}

View file

@ -0,0 +1,116 @@
package io.homeassistant.companion.android.sensors
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.database.AppDatabase
import io.homeassistant.companion.android.database.sensor.Sensor
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.homeassistant.companion.android.domain.integration.SensorRegistration
import io.homeassistant.companion.android.util.PermissionManager
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
class SensorReceiver : BroadcastReceiver() {
companion object {
const val TAG = "SensorReceiver"
val MANAGERS = listOf(
BatterySensorManager(),
NetworkSensorManager(),
GeocodeSensorManager(),
NextAlarmManager(),
PhoneStateSensorManager()
)
}
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.IO + Job())
@Inject
lateinit var integrationUseCase: IntegrationUseCase
override fun onReceive(context: Context, intent: Intent) {
DaggerSensorComponent.builder()
.appComponent((context as GraphComponentAccessor).appComponent)
.build()
.inject(this)
ioScope.launch {
updateSensors(context, integrationUseCase)
}
}
suspend fun updateSensors(
context: Context,
integrationUseCase: IntegrationUseCase
) {
val sensorDao = AppDatabase.getInstance(context).sensorDao()
// When we update the sensors make sure to request an accurate location.
val intent = Intent(context, LocationBroadcastReceiver::class.java)
intent.action = LocationBroadcastReceiver.ACTION_REQUEST_ACCURATE_LOCATION_UPDATE
context.sendBroadcast(intent)
val enabledRegistrations = mutableListOf<SensorRegistration<Any>>()
MANAGERS.forEach { manager ->
manager.getSensorRegistrations(context).forEach { registration ->
// Ensure dao is up to date
var sensor = sensorDao.get(registration.uniqueId)
var hasPermission = true
manager.requiredPermissions().forEach {
hasPermission =
hasPermission && PermissionManager.hasPermission(context, it)
}
if (sensor == null) {
sensor = Sensor(
registration.uniqueId, hasPermission, false,
registration.state.toString()
)
sensorDao.add(sensor)
} else {
sensor.enabled = sensor.enabled && hasPermission
sensor.state = registration.state.toString()
}
// Register Sensors
if (!sensor.registered) {
try {
integrationUseCase.registerSensor(registration)
sensor.registered = true
} catch (e: Exception) {
Log.e(TAG, "Issue registering sensor: ${registration.uniqueId}", e)
}
}
sensorDao.update(sensor)
if (sensor.enabled && sensor.registered) {
enabledRegistrations.add(registration)
}
}
}
var success = false
try {
success = integrationUseCase.updateSensors(enabledRegistrations.toTypedArray())
} catch (e: Exception) {
Log.e(TAG, "Exception while updating sensors.", e)
}
// We failed to update a sensor, we should re register next time
if (!success) {
enabledRegistrations.forEach {
val sensor = sensorDao.get(it.uniqueId)
if (sensor != null) {
sensor.registered = false
sensorDao.update(sensor)
}
}
}
}
}

View file

@ -1,6 +1,7 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import android.util.Log
import androidx.work.Constraints
import androidx.work.CoroutineWorker
import androidx.work.ExistingPeriodicWorkPolicy
@ -8,7 +9,6 @@ import androidx.work.NetworkType
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import androidx.work.WorkerParameters
import io.homeassistant.companion.android.SensorUpdater
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import java.util.concurrent.TimeUnit
@ -17,7 +17,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
class SensorWorker(
appContext: Context,
private val appContext: Context,
workerParams: WorkerParameters
) : CoroutineWorker(appContext, workerParams) {
companion object {
@ -38,18 +38,16 @@ class SensorWorker(
@Inject
lateinit var integrationUseCase: IntegrationUseCase
val allSensorUpdater: SensorUpdater
init {
DaggerSensorComponent.builder()
.appComponent((appContext as GraphComponentAccessor).appComponent)
.build()
.inject(this)
allSensorUpdater = AllSensorsUpdaterImpl(integrationUseCase, applicationContext)
}
override suspend fun doWork(): Result = withContext(Dispatchers.IO) {
allSensorUpdater.updateSensors()
Log.d(TAG, "Updating all Sensors.")
SensorReceiver().updateSensors(appContext, integrationUseCase)
Result.success()
}
}

View file

@ -0,0 +1,76 @@
package io.homeassistant.companion.android.sensors
import android.os.Bundle
import androidx.preference.Preference
import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class SensorsSettingsFragment : PreferenceFragmentCompat() {
@Inject
lateinit var integrationUseCase: IntegrationUseCase
private val ioScope: CoroutineScope = CoroutineScope(Dispatchers.Main)
companion object {
fun newInstance(): SensorsSettingsFragment {
return SensorsSettingsFragment()
}
}
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
DaggerSensorComponent
.builder()
.appComponent((activity?.application as GraphComponentAccessor).appComponent)
.build()
.inject(this)
setPreferencesFromResource(R.xml.sensors, rootKey)
ioScope.launch {
val managers = SensorReceiver.MANAGERS.plus(LocationBroadcastReceiver())
managers.sortedBy { it.name }.forEach { manager ->
val prefCategory = PreferenceCategory(preferenceScreen.context)
prefCategory.title = manager.name
preferenceScreen.addPreference(prefCategory)
manager.getSensorRegistrations(requireContext()).sortedBy { it.name }
.forEach { sensor ->
val pref = Preference(preferenceScreen.context)
pref.title = sensor.name
if (sensor.unitOfMeasurement.isNullOrBlank())
pref.summary = sensor.state.toString()
else
pref.summary = sensor.state.toString() + " " + sensor.unitOfMeasurement
// TODO: Add the icon from mdi:icon?
pref.setOnPreferenceClickListener {
parentFragmentManager
.beginTransaction()
.replace(
R.id.content,
SensorDetailFragment.newInstance(
sensor,
manager.requiredPermissions()
)
)
.addToBackStack("Sensor Detail")
.commit()
return@setOnPreferenceClickListener true
}
prefCategory.addPreference(pref)
}
}
}
}
}

View file

@ -37,7 +37,7 @@ class WifiStateReceiver() : BroadcastReceiver() {
updateJob?.cancel()
updateJob = ioScope.launch {
AllSensorsUpdaterImpl(integrationUseCase, context).updateSensors()
SensorReceiver().updateSensors(context, integrationUseCase)
}
}
}

View file

@ -15,6 +15,7 @@ import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.authenticator.Authenticator
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.nfc.NfcSetupActivity
import io.homeassistant.companion.android.sensors.SensorsSettingsFragment
import io.homeassistant.companion.android.settings.shortcuts.ShortcutsFragment
import io.homeassistant.companion.android.settings.ssid.SsidDialogFragment
import io.homeassistant.companion.android.settings.ssid.SsidPreference
@ -114,6 +115,15 @@ class SettingsFragment : PreferenceFragmentCompat(), SettingsView {
shortcuts?.isVisible = false
}
findPreference<Preference>("sensors")?.setOnPreferenceClickListener {
parentFragmentManager
.beginTransaction()
.replace(R.id.content, SensorsSettingsFragment.newInstance())
.addToBackStack(getString(R.string.sensors))
.commit()
return@setOnPreferenceClickListener true
}
findPreference<Preference>("version")?.let {
it.summary = BuildConfig.VERSION_NAME
}

View file

@ -30,9 +30,6 @@ class SettingsPresenterImpl @Inject constructor(
override fun getBoolean(key: String, defValue: Boolean): Boolean {
return runBlocking {
return@runBlocking when (key) {
"location_zone" -> integrationUseCase.isZoneTrackingEnabled()
"location_background" -> integrationUseCase.isBackgroundTrackingEnabled()
"call_state" -> integrationUseCase.isCallTrackingEnabled()
"fullscreen" -> integrationUseCase.isFullScreenEnabled()
"app_lock" -> authenticationUseCase.isLockEnabled()
else -> throw IllegalArgumentException("No boolean found by this key: $key")
@ -43,9 +40,6 @@ class SettingsPresenterImpl @Inject constructor(
override fun putBoolean(key: String, value: Boolean) {
mainScope.launch {
when (key) {
"location_zone" -> integrationUseCase.setZoneTrackingEnabled(value)
"location_background" -> integrationUseCase.setBackgroundTrackingEnabled(value)
"call_state" -> integrationUseCase.setCallTrackingEnabled(value)
"fullscreen" -> integrationUseCase.setFullScreenEnabled(value)
"app_lock" -> authenticationUseCase.setLockEnabled(value)
else -> throw IllegalArgumentException("No boolean found by this key: $key")
@ -153,7 +147,7 @@ class SettingsPresenterImpl @Inject constructor(
try {
panels = integrationUseCase.getPanels()
} catch (e: Exception) {
Log.e(SettingsPresenterImpl.TAG, "Issue getting panels.", e)
Log.e(TAG, "Issue getting panels.", e)
}
panels
}

View file

@ -7,8 +7,7 @@ import android.content.pm.PackageManager
import android.os.Build
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import io.homeassistant.companion.android.background.LocationBroadcastReceiver
import io.homeassistant.companion.android.background.LocationBroadcastReceiverBase
import io.homeassistant.companion.android.sensors.LocationBroadcastReceiver
class PermissionManager {
@ -67,13 +66,17 @@ class PermissionManager {
fun restartLocationTracking(context: Context) {
val intent = Intent(context, LocationBroadcastReceiver::class.java)
intent.action = LocationBroadcastReceiverBase.ACTION_REQUEST_LOCATION_UPDATES
intent.action = LocationBroadcastReceiver.ACTION_REQUEST_LOCATION_UPDATES
context.sendBroadcast(intent)
}
fun getPhonePermissionArray(): Array<String> {
return arrayOf(Manifest.permission.READ_PHONE_STATE)
}
fun requestPhoneStatePermissions(fragment: Fragment) {
fragment.requestPermissions(arrayOf(Manifest.permission.READ_PHONE_STATE), PHONE_STATE_REQUEST_CODE)
fragment.requestPermissions(getPhonePermissionArray(), PHONE_STATE_REQUEST_CODE)
}
fun checkPhoneStatePermission(context: Context): Boolean {

View file

@ -37,7 +37,6 @@ import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.graphics.ColorUtils
import androidx.room.Room
import androidx.webkit.WebSettingsCompat
import androidx.webkit.WebViewFeature
import eightbitlab.com.blurview.RenderScriptBlur
@ -46,12 +45,11 @@ import io.homeassistant.companion.android.DaggerPresenterComponent
import io.homeassistant.companion.android.PresenterModule
import io.homeassistant.companion.android.R
import io.homeassistant.companion.android.authenticator.Authenticator
import io.homeassistant.companion.android.background.LocationBroadcastReceiver
import io.homeassistant.companion.android.background.LocationBroadcastReceiverBase
import io.homeassistant.companion.android.common.dagger.GraphComponentAccessor
import io.homeassistant.companion.android.database.AppDataBase
import io.homeassistant.companion.android.database.AuthenticationList
import io.homeassistant.companion.android.database.AppDatabase
import io.homeassistant.companion.android.database.authentication.Authentication
import io.homeassistant.companion.android.onboarding.OnboardingActivity
import io.homeassistant.companion.android.sensors.LocationBroadcastReceiver
import io.homeassistant.companion.android.sensors.SensorWorker
import io.homeassistant.companion.android.settings.SettingsActivity
import io.homeassistant.companion.android.util.PermissionManager
@ -109,7 +107,7 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
SensorWorker.start(this)
val intent = Intent(this, LocationBroadcastReceiver::class.java)
intent.action = LocationBroadcastReceiverBase.ACTION_REQUEST_LOCATION_UPDATES
intent.action = LocationBroadcastReceiver.ACTION_REQUEST_LOCATION_UPDATES
sendBroadcast(intent)
if (BuildConfig.DEBUG) {
@ -544,12 +542,7 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
@SuppressLint("InflateParams")
fun authenticationDialog(handler: HttpAuthHandler, host: String, realm: String, authError: Boolean) {
val db = Room.databaseBuilder(
applicationContext,
AppDataBase::class.java, "HomeAssistantDB")
.allowMainThreadQueries()
.build()
val authenticationDao = db.authenticationDatabaseDao()
val authenticationDao = AppDatabase.getInstance(applicationContext).authenticationDao()
val httpAuth = authenticationDao.get((resourceURL + realm))
val inflater = layoutInflater
@ -595,10 +588,21 @@ class WebViewActivity : AppCompatActivity(), io.homeassistant.companion.android.
if (username.text.toString() != "" && password.text.toString() != "") {
if (remember.isChecked) {
if (authError)
authenticationDao.update(AuthenticationList((resourceURL + realm), username.text.toString(), password.text.toString()))
authenticationDao.update(
Authentication(
(resourceURL + realm),
username.text.toString(),
password.text.toString()
)
)
else
authenticationDao.insert(AuthenticationList((resourceURL + realm), username.text.toString(), password.text.toString()))
db.close()
authenticationDao.insert(
Authentication(
(resourceURL + realm),
username.text.toString(),
password.text.toString()
)
)
}
handler.proceed(username.text.toString(), password.text.toString())
} else AlertDialog.Builder(this)

View file

@ -0,0 +1,8 @@
<!-- drawable/leak.xml -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="@color/colorAccent" android:pathData="M6,3H3V6A3,3 0 0,0 6,3M14,3H12A9,9 0 0,1 3,12V14C9.08,14 14,9.07 14,3M10,3H8A5,5 0 0,1 3,8V10A7,7 0 0,0 10,3M10,21H12A9,9 0 0,1 21,12V10A11,11 0 0,0 10,21M18,21H21V18A3,3 0 0,0 18,21M14,21H16A5,5 0 0,1 21,16V14A7,7 0 0,0 14,21Z" />
</vector>

View file

@ -153,4 +153,15 @@ like to connect to:</string>
<string name="permission_explanation_calls">In order to track incoming and outgoing call\'s occurrence we need access to your phone state. No phone numbers or other call details will be stored.</string>
<string name="pref_call_tracking_title">Calls Tracking</string>
<string name="pref_call_tracking_summary">Allow application to detect call occurrence and notify server about it.</string>
<string name="sensors">Sensors</string>
<string name="security">Security</string>
<string name="sensor_title">Manage Sensors</string>
<string name="sensor_summary">Use this to mange what sensors are enabled/disabled.</string>
<string name="enabled_title">Enabled</string>
<string name="enabled_summary">When enabled values will be sent to Home Assistant</string>
<string name="unique_id">Unique Id</string>
<string name="state">State</string>
<string name="device_class">Device Class</string>
<string name="icon">Icon</string>
<string name="attributes">Attributes</string>
</resources>

View file

@ -28,30 +28,15 @@
app:useSimpleSummaryProvider="true"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/location">
<SwitchPreference
android:key="location_zone"
android:icon="@drawable/map_marker_radius"
android:title="@string/pref_location_zone_title"
android:summary="@string/pref_location_zone_summary"/>
<SwitchPreference
android:key="location_background"
android:icon="@drawable/map_marker"
android:title="@string/pref_location_background_title"
android:summary="@string/pref_location_background_summary"/>
android:title="@string/sensors">
<Preference
android:key="sensors"
android:icon="@drawable/leak"
android:title="@string/sensor_title"
android:summary="@string/sensor_summary"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/other_settings">
<SwitchPreference
android:key="call_state"
android:icon="@drawable/ic_phone"
android:title="@string/pref_call_tracking_title"
android:summary="@string/pref_call_tracking_summary"/>
<SwitchPreference
android:key="fullscreen"
android:icon="@drawable/ic_fullscreen"
android:title="@string/fullscreen"
android:summary="@string/fullscreen_def"/>
android:title="@string/security">
<SwitchPreference
android:key="app_lock"
android:icon="@drawable/ic_lock"
@ -62,6 +47,14 @@
android:icon="@drawable/ic_timeout"
android:title="@string/session_timeout_title"
app:useSimpleSummaryProvider="true"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/other_settings">
<SwitchPreference
android:key="fullscreen"
android:icon="@drawable/ic_fullscreen"
android:title="@string/fullscreen"
android:summary="@string/fullscreen_def"/>
<Preference
android:key="shortcuts"
android:icon="@drawable/ic_plus"

View file

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen
xmlns:app="http://schemas.android.com/apk/res-auto"
app:iconSpaceReserved="false">
<androidx.preference.SwitchPreference
app:key="enabled"
app:title="@string/enabled_title"
app:summary="@string/enabled_summary"
app:iconSpaceReserved="false"/>
<Preference
app:key="unique_id"
app:title="@string/unique_id"
app:iconSpaceReserved="false"/>
<Preference
app:key="state"
app:title="@string/state"
app:iconSpaceReserved="false"/>
<Preference
app:key="device_class"
app:title="@string/device_class"
app:iconSpaceReserved="false"/>
<Preference
app:key="icon"
app:title="@string/icon"
app:iconSpaceReserved="false"/>
<androidx.preference.PreferenceCategory
app:key="attributes"
app:title="@string/attributes"
app:iconSpaceReserved="false"/>
</androidx.preference.PreferenceScreen>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.preference.PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android">
</androidx.preference.PreferenceScreen>

View file

@ -1,22 +0,0 @@
package io.homeassistant.companion.android.background
import android.content.Context
import android.content.Intent
class LocationBroadcastReceiver : LocationBroadcastReceiverBase() {
override fun setupLocationTracking(context: Context) {
// No op
}
override fun handleLocationUpdate(intent: Intent) {
// No op
}
override fun handleGeoUpdate(context: Context, intent: Intent) {
// No op
}
override fun requestSingleAccurateLocation(context: Context) {
// No op
}
}

View file

@ -1,25 +0,0 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import io.homeassistant.companion.android.domain.integration.IntegrationUseCase
import io.homeassistant.companion.android.util.PermissionManager
class AllSensorsUpdaterImpl(
integrationUseCase: IntegrationUseCase,
appContext: Context
) : AllSensorsUpdater(integrationUseCase, appContext) {
override suspend fun getManagers(): List<SensorManager> {
val sensorManagers = mutableListOf(
BatterySensorManager(),
NetworkSensorManager(),
NextAlarmManager()
)
if (integrationUseCase.isCallTrackingEnabled() && PermissionManager.checkPhoneStatePermission(appContext)) {
sensorManagers.add(PhoneStateSensorManager())
}
return sensorManagers
}
}

View file

@ -0,0 +1,17 @@
package io.homeassistant.companion.android.sensors
import android.content.Context
import io.homeassistant.companion.android.domain.integration.SensorRegistration
class GeocodeSensorManager : SensorManager {
override val name: String
get() = "Geocode Sensors"
override fun requiredPermissions(): Array<String> {
return emptyArray()
}
override fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>> {
return emptyList()
}
}

View file

@ -0,0 +1,44 @@
package io.homeassistant.companion.android.sensors
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import io.homeassistant.companion.android.domain.integration.SensorRegistration
class LocationBroadcastReceiver : BroadcastReceiver(), SensorManager {
companion object {
const val MINIMUM_ACCURACY = 200
const val ACTION_REQUEST_LOCATION_UPDATES =
"io.homeassistant.companion.android.background.REQUEST_UPDATES"
const val ACTION_REQUEST_ACCURATE_LOCATION_UPDATE =
"io.homeassistant.companion.android.background.REQUEST_ACCURATE_UPDATE"
const val ACTION_PROCESS_LOCATION =
"io.homeassistant.companion.android.background.PROCESS_UPDATES"
const val ACTION_PROCESS_GEO =
"io.homeassistant.companion.android.background.PROCESS_GEOFENCE"
const val ID_BACKGROUND_LOCATION = "location_background"
const val ID_ZONE_LOCATION = "location_zone"
internal const val TAG = "LocBroadcastReceiver"
}
override fun onReceive(context: Context, intent: Intent) {
// Noop
}
override val name: String
get() = "Location Sensors"
override fun requiredPermissions(): Array<String> {
// Noop
return emptyArray()
}
override fun getSensorRegistrations(context: Context): List<SensorRegistration<Any>> {
// Noop
return emptyList()
}
}

View file

@ -14,7 +14,6 @@ import io.homeassistant.companion.android.domain.integration.DeviceRegistration
import io.homeassistant.companion.android.domain.integration.Entity
import io.homeassistant.companion.android.domain.integration.IntegrationRepository
import io.homeassistant.companion.android.domain.integration.Panel
import io.homeassistant.companion.android.domain.integration.Sensor
import io.homeassistant.companion.android.domain.integration.SensorRegistration
import io.homeassistant.companion.android.domain.integration.Service
import io.homeassistant.companion.android.domain.integration.UpdateLocation
@ -250,30 +249,6 @@ class IntegrationRepositoryImpl @Inject constructor(
throw IntegrationException()
}
override suspend fun setZoneTrackingEnabled(enabled: Boolean) {
localStorage.putBoolean(PREF_ZONE_ENABLED, enabled)
}
override suspend fun isZoneTrackingEnabled(): Boolean {
return localStorage.getBoolean(PREF_ZONE_ENABLED)
}
override suspend fun setBackgroundTrackingEnabled(enabled: Boolean) {
localStorage.putBoolean(PREF_BACKGROUND_ENABLED, enabled)
}
override suspend fun isBackgroundTrackingEnabled(): Boolean {
return localStorage.getBoolean(PREF_BACKGROUND_ENABLED)
}
override suspend fun setCallTrackingEnabled(enabled: Boolean) {
localStorage.putBoolean(PREF_CALL_ENABLED, enabled)
}
override suspend fun isCallTrackingEnabled(): Boolean {
return localStorage.getBoolean(PREF_CALL_ENABLED)
}
override suspend fun setFullScreenEnabled(enabled: Boolean) {
localStorage.putBoolean(PREF_FULLSCREEN_ENABLED, enabled)
}
@ -387,7 +362,7 @@ class IntegrationRepositoryImpl @Inject constructor(
throw IntegrationException()
}
override suspend fun updateSensors(sensors: Array<Sensor<Any>>): Boolean {
override suspend fun updateSensors(sensors: Array<SensorRegistration<Any>>): Boolean {
val integrationRequest = IntegrationRequest(
"update_sensor_states",
sensors.map {

View file

@ -821,51 +821,5 @@ object IntegrationRepositoryImplSpec : Spek({
}
}
}
describe("location settings") {
describe("isZoneTrackingEnabled") {
var isZoneTrackingEnabled by Delegates.notNull<Boolean>()
beforeEachTest {
coEvery { localStorage.getBoolean("zone_enabled") } returns true
runBlocking { isZoneTrackingEnabled = repository.isZoneTrackingEnabled() }
}
it("should return what is stored") {
assertThat(isZoneTrackingEnabled).isTrue()
}
}
describe("setZoneTrackingEnabled") {
beforeEachTest {
runBlocking { repository.setZoneTrackingEnabled(true) }
}
it("should return what is stored") {
coVerify {
localStorage.putBoolean("zone_enabled", true)
}
}
}
describe("isBackgroundTrackingEnabled") {
var isBackgroundTrackingEnabled by Delegates.notNull<Boolean>()
beforeEachTest {
coEvery { localStorage.getBoolean("background_enabled") } returns true
runBlocking { isBackgroundTrackingEnabled = repository.isBackgroundTrackingEnabled() }
}
it("should return what is stored") {
assertThat(isBackgroundTrackingEnabled).isTrue()
}
}
describe("setBackgroundTrackingEnabled") {
beforeEachTest {
runBlocking { repository.setBackgroundTrackingEnabled(true) }
}
it("should return what is stored") {
coVerify {
localStorage.putBoolean("background_enabled", true)
}
}
}
}
}
})

View file

@ -12,15 +12,6 @@ interface IntegrationRepository {
suspend fun getZones(): Array<Entity<ZoneAttributes>>
suspend fun setZoneTrackingEnabled(enabled: Boolean)
suspend fun isZoneTrackingEnabled(): Boolean
suspend fun setBackgroundTrackingEnabled(enabled: Boolean)
suspend fun isBackgroundTrackingEnabled(): Boolean
suspend fun setCallTrackingEnabled(enabled: Boolean)
suspend fun isCallTrackingEnabled(): Boolean
suspend fun setFullScreenEnabled(enabled: Boolean)
suspend fun isFullScreenEnabled(): Boolean
@ -45,5 +36,5 @@ interface IntegrationRepository {
suspend fun fireEvent(eventType: String, eventData: Map<String, Any>)
suspend fun registerSensor(sensorRegistration: SensorRegistration<Any>)
suspend fun updateSensors(sensors: Array<Sensor<Any>>): Boolean
suspend fun updateSensors(sensors: Array<SensorRegistration<Any>>): Boolean
}

View file

@ -27,15 +27,6 @@ interface IntegrationUseCase {
suspend fun getZones(): Array<Entity<ZoneAttributes>>
suspend fun setZoneTrackingEnabled(enabled: Boolean)
suspend fun isZoneTrackingEnabled(): Boolean
suspend fun setBackgroundTrackingEnabled(enabled: Boolean)
suspend fun isBackgroundTrackingEnabled(): Boolean
suspend fun setCallTrackingEnabled(enabled: Boolean)
suspend fun isCallTrackingEnabled(): Boolean
suspend fun setFullScreenEnabled(enabled: Boolean)
suspend fun isFullScreenEnabled(): Boolean
@ -55,5 +46,5 @@ interface IntegrationUseCase {
suspend fun registerSensor(sensorRegistration: SensorRegistration<Any>)
suspend fun updateSensors(sensors: Array<Sensor<Any>>): Boolean
suspend fun updateSensors(sensors: Array<SensorRegistration<Any>>): Boolean
}

View file

@ -56,30 +56,6 @@ class IntegrationUseCaseImpl @Inject constructor(
return integrationRepository.getZones()
}
override suspend fun setZoneTrackingEnabled(enabled: Boolean) {
return integrationRepository.setZoneTrackingEnabled(enabled)
}
override suspend fun isZoneTrackingEnabled(): Boolean {
return integrationRepository.isZoneTrackingEnabled()
}
override suspend fun setBackgroundTrackingEnabled(enabled: Boolean) {
return integrationRepository.setBackgroundTrackingEnabled(enabled)
}
override suspend fun isBackgroundTrackingEnabled(): Boolean {
return integrationRepository.isBackgroundTrackingEnabled()
}
override suspend fun setCallTrackingEnabled(enabled: Boolean) {
return integrationRepository.setCallTrackingEnabled(enabled)
}
override suspend fun isCallTrackingEnabled(): Boolean {
return integrationRepository.isCallTrackingEnabled()
}
override suspend fun setFullScreenEnabled(enabled: Boolean) {
return integrationRepository.setFullScreenEnabled(enabled)
}
@ -124,7 +100,7 @@ class IntegrationUseCaseImpl @Inject constructor(
return integrationRepository.registerSensor(sensorRegistration)
}
override suspend fun updateSensors(sensors: Array<Sensor<Any>>): Boolean {
override suspend fun updateSensors(sensors: Array<SensorRegistration<Any>>): Boolean {
return integrationRepository.updateSensors(sensors)
}
}

View file

@ -1,10 +0,0 @@
package io.homeassistant.companion.android.domain.integration
data class Sensor<T>(
val uniqueId: String,
val state: T,
val type: String,
val icon: String,
val attributes: Map<String, Any>
)

View file

@ -10,21 +10,4 @@ data class SensorRegistration<T>(
val deviceClass: String? = null,
val unitOfMeasurement: String? = null
) {
constructor(
sensor: Sensor<T>,
name: String,
deviceClass: String? = null,
unitOfMeasurement: String? = null
) : this(
sensor.uniqueId,
sensor.state,
sensor.type,
sensor.icon,
sensor.attributes,
name,
deviceClass,
unitOfMeasurement
)
}
)

View file

@ -103,46 +103,6 @@ object IntegrationUseCaseImplSpec : Spek({
}
}
describe("setZoneTrackingEnabled") {
beforeEachTest {
runBlocking { useCase.setZoneTrackingEnabled(true) }
}
it("should call the repository") {
coVerify { integrationRepository.setZoneTrackingEnabled(true) }
}
}
describe("isZoneTrackingEnabled") {
beforeEachTest {
runBlocking { useCase.isZoneTrackingEnabled() }
}
it("should call the repository") {
coVerify { integrationRepository.isZoneTrackingEnabled() }
}
}
describe("setBackgroundTrackingEnabled") {
beforeEachTest {
runBlocking { useCase.setBackgroundTrackingEnabled(true) }
}
it("should call the repository") {
coVerify { integrationRepository.setBackgroundTrackingEnabled(true) }
}
}
describe("isBackgroundTrackingEnabled") {
beforeEachTest {
runBlocking { useCase.isBackgroundTrackingEnabled() }
}
it("should call the repository") {
coVerify { integrationRepository.isBackgroundTrackingEnabled() }
}
}
describe("getThemeColor") {
beforeEachTest {
runBlocking { useCase.getThemeColor() }