mirror of
https://github.com/home-assistant/android
synced 2024-10-15 12:32:54 +00:00
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:
parent
a8bd4da0a0
commit
d016aff47e
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
|
@ -79,7 +79,7 @@
|
|||
</activity>
|
||||
|
||||
<receiver
|
||||
android:name=".background.LocationBroadcastReceiver"
|
||||
android:name=".sensors.LocationBroadcastReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package io.homeassistant.companion.android
|
||||
|
||||
interface SensorUpdater {
|
||||
suspend fun updateSensors()
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,
|
||||
|
|
@ -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()
|
|
@ -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
|
||||
)
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ class PhoneStateReceiver(
|
|||
if (PermissionManager.checkPhoneStatePermission(context)) {
|
||||
updateJob?.cancel()
|
||||
updateJob = ioScope.launch {
|
||||
AllSensorsUpdaterImpl(integrationUseCase, context).updateSensors()
|
||||
SensorReceiver().updateSensors(context, integrationUseCase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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>>
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -37,7 +37,7 @@ class WifiStateReceiver() : BroadcastReceiver() {
|
|||
|
||||
updateJob?.cancel()
|
||||
updateJob = ioScope.launch {
|
||||
AllSensorsUpdaterImpl(integrationUseCase, context).updateSensors()
|
||||
SensorReceiver().updateSensors(context, integrationUseCase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
8
app/src/main/res/drawable/leak.xml
Normal file
8
app/src/main/res/drawable/leak.xml
Normal 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>
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
32
app/src/main/res/xml/sensor_detail.xml
Normal file
32
app/src/main/res/xml/sensor_detail.xml
Normal 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>
|
5
app/src/main/res/xml/sensors.xml
Normal file
5
app/src/main/res/xml/sensors.xml
Normal 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>
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
)
|
|
@ -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
|
||||
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -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() }
|
||||
|
|
Loading…
Reference in a new issue