diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 026f33a61..c2925c61c 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -163,7 +163,6 @@ dependencies {
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.fragment:fragment-ktx:1.5.6")
- implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.13.5")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("com.squareup.picasso:picasso:2.8")
diff --git a/common/build.gradle.kts b/common/build.gradle.kts
index 77977fec0..1504640b0 100644
--- a/common/build.gradle.kts
+++ b/common/build.gradle.kts
@@ -68,7 +68,7 @@ dependencies {
api("androidx.work:work-runtime-ktx:2.8.1")
- implementation("com.squareup.retrofit2:retrofit:2.9.0")
+ api("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-jackson:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.10.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.10.0")
diff --git a/common/schemas/io.homeassistant.companion.android.database.AppDatabase/40.json b/common/schemas/io.homeassistant.companion.android.database.AppDatabase/40.json
new file mode 100644
index 000000000..b93d2522d
--- /dev/null
+++ b/common/schemas/io.homeassistant.companion.android.database.AppDatabase/40.json
@@ -0,0 +1,994 @@
+{
+ "formatVersion": 1,
+ "database": {
+ "version": 40,
+ "identityHash": "9ec60cb96f3febd24dc5713f23f65e50",
+ "entities": [
+ {
+ "tableName": "sensor_attributes",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, `value_type` TEXT NOT NULL, PRIMARY KEY(`sensor_id`, `name`))",
+ "fields": [
+ {
+ "fieldPath": "sensorId",
+ "columnName": "sensor_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "value",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "valueType",
+ "columnName": "value_type",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "sensor_id",
+ "name"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "authentication_list",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `username` TEXT NOT NULL, `password` TEXT NOT NULL, PRIMARY KEY(`host`))",
+ "fields": [
+ {
+ "fieldPath": "host",
+ "columnName": "host",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "username",
+ "columnName": "username",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "password",
+ "columnName": "password",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "host"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "sensors",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `enabled` INTEGER NOT NULL, `registered` INTEGER DEFAULT NULL, `state` TEXT NOT NULL, `last_sent_state` TEXT DEFAULT NULL, `last_sent_icon` TEXT DEFAULT NULL, `state_type` TEXT NOT NULL, `type` TEXT NOT NULL, `icon` TEXT NOT NULL, `name` TEXT NOT NULL, `device_class` TEXT, `unit_of_measurement` TEXT, `state_class` TEXT, `entity_category` TEXT, `core_registration` TEXT, `app_registration` TEXT, PRIMARY KEY(`id`, `server_id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "enabled",
+ "columnName": "enabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "registered",
+ "columnName": "registered",
+ "affinity": "INTEGER",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "state",
+ "columnName": "state",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastSentState",
+ "columnName": "last_sent_state",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "lastSentIcon",
+ "columnName": "last_sent_icon",
+ "affinity": "TEXT",
+ "notNull": false,
+ "defaultValue": "NULL"
+ },
+ {
+ "fieldPath": "stateType",
+ "columnName": "state_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "type",
+ "columnName": "type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "icon",
+ "columnName": "icon",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceClass",
+ "columnName": "device_class",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "unitOfMeasurement",
+ "columnName": "unit_of_measurement",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "stateClass",
+ "columnName": "state_class",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "entityCategory",
+ "columnName": "entity_category",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "coreRegistration",
+ "columnName": "core_registration",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "appRegistration",
+ "columnName": "app_registration",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id",
+ "server_id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "sensor_settings",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`sensor_id` TEXT NOT NULL, `name` TEXT NOT NULL, `value` TEXT NOT NULL, `value_type` TEXT NOT NULL, `enabled` INTEGER NOT NULL, `entries` TEXT NOT NULL, PRIMARY KEY(`sensor_id`, `name`))",
+ "fields": [
+ {
+ "fieldPath": "sensorId",
+ "columnName": "sensor_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "name",
+ "columnName": "name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "value",
+ "columnName": "value",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "valueType",
+ "columnName": "value_type",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "enabled",
+ "columnName": "enabled",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entries",
+ "columnName": "entries",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "sensor_id",
+ "name"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "button_widgets",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `icon_id` INTEGER NOT NULL, `domain` TEXT NOT NULL, `service` TEXT NOT NULL, `service_data` TEXT NOT NULL, `label` TEXT, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, `require_authentication` INTEGER NOT NULL DEFAULT 0, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "iconId",
+ "columnName": "icon_id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "domain",
+ "columnName": "domain",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "service",
+ "columnName": "service",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serviceData",
+ "columnName": "service_data",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "label",
+ "columnName": "label",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "backgroundType",
+ "columnName": "background_type",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'DAYNIGHT'"
+ },
+ {
+ "fieldPath": "textColor",
+ "columnName": "text_color",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "requireAuthentication",
+ "columnName": "require_authentication",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "camera_widgets",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `entity_id` TEXT NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "media_player_controls_widgets",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `entity_id` TEXT NOT NULL, `label` TEXT, `show_skip` INTEGER NOT NULL, `show_seek` INTEGER NOT NULL, `show_volume` INTEGER NOT NULL, `show_source` INTEGER NOT NULL DEFAULT false, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "label",
+ "columnName": "label",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "showSkip",
+ "columnName": "show_skip",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "showSeek",
+ "columnName": "show_seek",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "showVolume",
+ "columnName": "show_volume",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "showSource",
+ "columnName": "show_source",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "false"
+ },
+ {
+ "fieldPath": "backgroundType",
+ "columnName": "background_type",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'DAYNIGHT'"
+ },
+ {
+ "fieldPath": "textColor",
+ "columnName": "text_color",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "static_widget",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `entity_id` TEXT NOT NULL, `attribute_ids` TEXT, `label` TEXT, `text_size` REAL NOT NULL, `state_separator` TEXT NOT NULL, `attribute_separator` TEXT NOT NULL, `last_update` TEXT NOT NULL, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attributeIds",
+ "columnName": "attribute_ids",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "label",
+ "columnName": "label",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "textSize",
+ "columnName": "text_size",
+ "affinity": "REAL",
+ "notNull": true
+ },
+ {
+ "fieldPath": "stateSeparator",
+ "columnName": "state_separator",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "attributeSeparator",
+ "columnName": "attribute_separator",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "lastUpdate",
+ "columnName": "last_update",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "backgroundType",
+ "columnName": "background_type",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'DAYNIGHT'"
+ },
+ {
+ "fieldPath": "textColor",
+ "columnName": "text_color",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "template_widgets",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `server_id` INTEGER NOT NULL DEFAULT 0, `template` TEXT NOT NULL, `text_size` REAL NOT NULL DEFAULT 12.0, `last_update` TEXT NOT NULL, `background_type` TEXT NOT NULL DEFAULT 'DAYNIGHT', `text_color` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "template",
+ "columnName": "template",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "textSize",
+ "columnName": "text_size",
+ "affinity": "REAL",
+ "notNull": true,
+ "defaultValue": "12.0"
+ },
+ {
+ "fieldPath": "lastUpdate",
+ "columnName": "last_update",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "backgroundType",
+ "columnName": "background_type",
+ "affinity": "TEXT",
+ "notNull": true,
+ "defaultValue": "'DAYNIGHT'"
+ },
+ {
+ "fieldPath": "textColor",
+ "columnName": "text_color",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "notification_history",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `received` INTEGER NOT NULL, `message` TEXT NOT NULL, `data` TEXT NOT NULL, `source` TEXT NOT NULL, `server_id` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "received",
+ "columnName": "received",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "message",
+ "columnName": "message",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "data",
+ "columnName": "data",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "source",
+ "columnName": "source",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "qs_tiles",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `tile_id` TEXT NOT NULL, `added` INTEGER NOT NULL DEFAULT 1, `server_id` INTEGER NOT NULL DEFAULT 0, `icon_id` INTEGER, `entity_id` TEXT NOT NULL, `label` TEXT NOT NULL, `subtitle` TEXT, `should_vibrate` INTEGER NOT NULL DEFAULT 0, `auth_required` INTEGER NOT NULL DEFAULT 0)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "tileId",
+ "columnName": "tile_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "added",
+ "columnName": "added",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ },
+ {
+ "fieldPath": "serverId",
+ "columnName": "server_id",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "iconId",
+ "columnName": "icon_id",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "label",
+ "columnName": "label",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "subtitle",
+ "columnName": "subtitle",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "shouldVibrate",
+ "columnName": "should_vibrate",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ },
+ {
+ "fieldPath": "authRequired",
+ "columnName": "auth_required",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "0"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorites",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `position` INTEGER NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "position",
+ "columnName": "position",
+ "affinity": "INTEGER",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "favorite_cache",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `friendly_name` TEXT NOT NULL, `icon` TEXT, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "friendlyName",
+ "columnName": "friendly_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "icon",
+ "columnName": "icon",
+ "affinity": "TEXT",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "entity_state_complications",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `entity_id` TEXT NOT NULL, `show_title` INTEGER NOT NULL DEFAULT 1, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "entityId",
+ "columnName": "entity_id",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "showTitle",
+ "columnName": "show_title",
+ "affinity": "INTEGER",
+ "notNull": true,
+ "defaultValue": "1"
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "servers",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `_name` TEXT NOT NULL, `name_override` TEXT, `_version` TEXT, `list_order` INTEGER NOT NULL, `device_name` TEXT, `external_url` TEXT NOT NULL, `internal_url` TEXT, `cloud_url` TEXT, `webhook_id` TEXT, `secret` TEXT, `cloudhook_url` TEXT, `use_cloud` INTEGER NOT NULL, `internal_ssids` TEXT NOT NULL, `prioritize_internal` INTEGER NOT NULL, `access_token` TEXT, `refresh_token` TEXT, `token_expiration` INTEGER, `token_type` TEXT, `install_id` TEXT, `user_id` TEXT, `user_name` TEXT, `user_is_owner` INTEGER, `user_is_admin` INTEGER)",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "_name",
+ "columnName": "_name",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "nameOverride",
+ "columnName": "name_override",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "_version",
+ "columnName": "_version",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "listOrder",
+ "columnName": "list_order",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "deviceName",
+ "columnName": "device_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.externalUrl",
+ "columnName": "external_url",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "connection.internalUrl",
+ "columnName": "internal_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.cloudUrl",
+ "columnName": "cloud_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.webhookId",
+ "columnName": "webhook_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.secret",
+ "columnName": "secret",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.cloudhookUrl",
+ "columnName": "cloudhook_url",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "connection.useCloud",
+ "columnName": "use_cloud",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "connection.internalSsids",
+ "columnName": "internal_ssids",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "connection.prioritizeInternal",
+ "columnName": "prioritize_internal",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "session.accessToken",
+ "columnName": "access_token",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "session.refreshToken",
+ "columnName": "refresh_token",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "session.tokenExpiration",
+ "columnName": "token_expiration",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "session.tokenType",
+ "columnName": "token_type",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "session.installId",
+ "columnName": "install_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "user.id",
+ "columnName": "user_id",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "user.name",
+ "columnName": "user_name",
+ "affinity": "TEXT",
+ "notNull": false
+ },
+ {
+ "fieldPath": "user.isOwner",
+ "columnName": "user_is_owner",
+ "affinity": "INTEGER",
+ "notNull": false
+ },
+ {
+ "fieldPath": "user.isAdmin",
+ "columnName": "user_is_admin",
+ "affinity": "INTEGER",
+ "notNull": false
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": true,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ },
+ {
+ "tableName": "settings",
+ "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER NOT NULL, `websocket_setting` TEXT NOT NULL, `sensor_update_frequency` TEXT NOT NULL, PRIMARY KEY(`id`))",
+ "fields": [
+ {
+ "fieldPath": "id",
+ "columnName": "id",
+ "affinity": "INTEGER",
+ "notNull": true
+ },
+ {
+ "fieldPath": "websocketSetting",
+ "columnName": "websocket_setting",
+ "affinity": "TEXT",
+ "notNull": true
+ },
+ {
+ "fieldPath": "sensorUpdateFrequency",
+ "columnName": "sensor_update_frequency",
+ "affinity": "TEXT",
+ "notNull": true
+ }
+ ],
+ "primaryKey": {
+ "autoGenerate": false,
+ "columnNames": [
+ "id"
+ ]
+ },
+ "indices": [],
+ "foreignKeys": []
+ }
+ ],
+ "views": [],
+ "setupQueries": [
+ "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
+ "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9ec60cb96f3febd24dc5713f23f65e50')"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt b/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt
index d899275c0..7a557675d 100644
--- a/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt
+++ b/common/src/main/java/io/homeassistant/companion/android/database/AppDatabase.kt
@@ -87,7 +87,7 @@ import io.homeassistant.companion.android.common.R as commonR
Server::class,
Setting::class
],
- version = 39,
+ version = 40,
autoMigrations = [
AutoMigration(from = 24, to = 25),
AutoMigration(from = 25, to = 26),
@@ -103,7 +103,8 @@ import io.homeassistant.companion.android.common.R as commonR
AutoMigration(from = 35, to = 36),
AutoMigration(from = 36, to = 37, spec = AppDatabase.Companion.Migration36to37::class),
AutoMigration(from = 37, to = 38, spec = AppDatabase.Companion.Migration37to38::class),
- AutoMigration(from = 38, to = 39)
+ AutoMigration(from = 38, to = 39),
+ AutoMigration(from = 39, to = 40)
]
)
@TypeConverters(
diff --git a/common/src/main/java/io/homeassistant/companion/android/database/wear/EntityStateComplications.kt b/common/src/main/java/io/homeassistant/companion/android/database/wear/EntityStateComplications.kt
index 45b7ec22a..a90523ed8 100644
--- a/common/src/main/java/io/homeassistant/companion/android/database/wear/EntityStateComplications.kt
+++ b/common/src/main/java/io/homeassistant/companion/android/database/wear/EntityStateComplications.kt
@@ -13,5 +13,7 @@ data class EntityStateComplications(
@ColumnInfo(name = "id")
val id: Int,
@ColumnInfo(name = "entity_id")
- val entityId: String
+ val entityId: String,
+ @ColumnInfo(name = "show_title", defaultValue = "1")
+ val showTitle: Boolean
)
diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml
index f6cdc5a81..bc480066d 100644
--- a/common/src/main/res/values/strings.xml
+++ b/common/src/main/res/values/strings.xml
@@ -125,7 +125,7 @@
Invalid entity
Entity state
Entity state
- preview
+ Preview
Configuration
Configure Service Call
Widget Label
@@ -721,6 +721,7 @@
Show
Sharing logs with the Home Assistant team will help to solve issues. Please share the logs only if you have been asked to do so by a Home Assistant developer
Show and Share Logs
+ Show entity name
Sign in on phone
%1$s decreased
%1$s increased
diff --git a/wear/build.gradle.kts b/wear/build.gradle.kts
index c0789f4d9..38f4c8524 100644
--- a/wear/build.gradle.kts
+++ b/wear/build.gradle.kts
@@ -96,6 +96,7 @@ dependencies {
implementation("com.google.android.material:material:1.8.0")
implementation("androidx.wear:wear:1.2.0")
+ implementation("androidx.core:core-ktx:1.10.0")
implementation("com.google.android.gms:play-services-wearable:18.0.0")
implementation("androidx.wear:wear-input:1.2.0-alpha02")
implementation("androidx.wear:wear-remote-interactions:1.0.0")
diff --git a/wear/src/main/AndroidManifest.xml b/wear/src/main/AndroidManifest.xml
index 5de7077a3..6b67ab94f 100644
--- a/wear/src/main/AndroidManifest.xml
+++ b/wear/src/main/AndroidManifest.xml
@@ -154,7 +154,7 @@
+ android:value="SHORT_TEXT,LONG_TEXT" />
diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigActivity.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigActivity.kt
index 14f5ebb03..6a85041b0 100644
--- a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigActivity.kt
+++ b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigActivity.kt
@@ -9,6 +9,7 @@ import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
+import androidx.core.content.IntentCompat
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.EXTRA_CONFIG_COMPLICATION_ID
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.EXTRA_CONFIG_COMPLICATION_TYPE
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.EXTRA_CONFIG_DATA_SOURCE_COMPONENT
@@ -33,9 +34,11 @@ class ComplicationConfigActivity : ComponentActivity() {
val id = intent.getIntExtra(EXTRA_CONFIG_COMPLICATION_ID, -1)
val type = intent.getIntExtra(EXTRA_CONFIG_COMPLICATION_TYPE, -1)
- val component = intent.getParcelableExtra(EXTRA_CONFIG_DATA_SOURCE_COMPONENT)
+ val component = IntentCompat.getParcelableExtra(intent, EXTRA_CONFIG_DATA_SOURCE_COMPONENT, ComponentName::class.java)
Log.i(TAG, "Config for id $id of type $type for component ${component?.className}")
+ complicationConfigViewModel.setDataFromIntent(id)
+
setContent {
LoadConfigView(
complicationConfigViewModel
diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt
index 4aeca8365..4266a82bb 100644
--- a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt
+++ b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationConfigViewModel.kt
@@ -15,6 +15,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import io.homeassistant.companion.android.HomeAssistantApplication
import io.homeassistant.companion.android.common.data.integration.Entity
import io.homeassistant.companion.android.common.data.integration.domain
+import io.homeassistant.companion.android.common.data.integration.friendlyName
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.common.data.websocket.WebSocketState
import io.homeassistant.companion.android.data.SimplifiedEntity
@@ -29,8 +30,8 @@ import javax.inject.Inject
@HiltViewModel
class ComplicationConfigViewModel @Inject constructor(
application: Application,
+ favoritesDao: FavoritesDao,
private val serverManager: ServerManager,
- private val favoritesDao: FavoritesDao,
private val entityStateComplicationsDao: EntityStateComplicationsDao
) : AndroidViewModel(application) {
companion object {
@@ -55,11 +56,28 @@ class ComplicationConfigViewModel @Inject constructor(
private set
var selectedEntity: SimplifiedEntity? by mutableStateOf(null)
private set
+ var entityShowTitle by mutableStateOf(true)
+ private set
init {
loadEntities()
}
+ fun setDataFromIntent(id: Int) {
+ viewModelScope.launch {
+ if (!serverManager.isRegistered() || id <= 0) return@launch
+
+ val stored = entityStateComplicationsDao.get(id)
+ stored?.let {
+ selectedEntity = SimplifiedEntity(entityId = it.entityId)
+ entityShowTitle = it.showTitle
+ if (loadingState == LoadingState.READY) {
+ updateSelectedEntity()
+ }
+ }
+ }
+ }
+
private fun loadEntities() {
viewModelScope.launch {
if (!serverManager.isRegistered()) {
@@ -73,6 +91,7 @@ class ComplicationConfigViewModel @Inject constructor(
entities[it.entityId] = it
}
updateEntityDomains()
+ updateSelectedEntity()
// Finished initial load, update state
val webSocketState = serverManager.webSocketRepository().getConnectionState()
@@ -111,13 +130,32 @@ class ComplicationConfigViewModel @Inject constructor(
entitiesByDomainOrder.addAll(domainsList)
}
+ private fun updateSelectedEntity() {
+ if (selectedEntity == null) return
+ val fullEntity = entities[selectedEntity!!.entityId]
+
+ selectedEntity = if (fullEntity == null) {
+ null // Clear invalid value
+ } else {
+ SimplifiedEntity(
+ entityId = fullEntity.entityId,
+ friendlyName = fullEntity.friendlyName,
+ icon = (fullEntity.attributes as? Map<*, *>)?.get("icon") as? String ?: ""
+ )
+ }
+ }
+
fun setEntity(entity: SimplifiedEntity) {
selectedEntity = entity
}
+ fun setShowTitle(show: Boolean) {
+ entityShowTitle = show
+ }
+
fun addEntityStateComplication(id: Int, entity: SimplifiedEntity) {
viewModelScope.launch {
- entityStateComplicationsDao.add(EntityStateComplications(id, entity.entityId))
+ entityStateComplicationsDao.add(EntityStateComplications(id, entity.entityId, entityShowTitle))
}
}
diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt
index 6816b8c57..8aa137d30 100644
--- a/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt
+++ b/wear/src/main/java/io/homeassistant/companion/android/complications/ComplicationReceiver.kt
@@ -5,6 +5,7 @@ import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
+import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService.Companion.EXTRA_CONFIG_COMPLICATION_ID
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceUpdateRequester
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.common.data.servers.ServerManager
@@ -92,6 +93,20 @@ class ComplicationReceiver : BroadcastReceiver() {
)
}
+ fun getComplicationConfigureIntent(
+ context: Context,
+ complicationInstanceId: Int
+ ): PendingIntent {
+ return PendingIntent.getActivity(
+ context,
+ complicationInstanceId,
+ Intent(context, ComplicationConfigActivity::class.java).apply {
+ putExtra(EXTRA_CONFIG_COMPLICATION_ID, complicationInstanceId)
+ },
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ }
+
fun getAssistIntent(context: Context): PendingIntent {
return PendingIntent.getActivity(
context,
diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt
index 5c24d2cdb..839220cd9 100644
--- a/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt
+++ b/wear/src/main/java/io/homeassistant/companion/android/complications/EntityStateDataSourceService.kt
@@ -3,8 +3,10 @@ package io.homeassistant.companion.android.complications
import android.graphics.Color
import android.graphics.drawable.Icon
import android.util.Log
+import androidx.annotation.StringRes
import androidx.wear.watchface.complications.data.ComplicationData
import androidx.wear.watchface.complications.data.ComplicationType
+import androidx.wear.watchface.complications.data.LongTextComplicationData
import androidx.wear.watchface.complications.data.MonochromaticImage
import androidx.wear.watchface.complications.data.PlainComplicationText
import androidx.wear.watchface.complications.data.ShortTextComplicationData
@@ -15,9 +17,12 @@ import com.mikepenz.iconics.typeface.library.community.material.CommunityMateria
import com.mikepenz.iconics.utils.colorInt
import dagger.hilt.android.AndroidEntryPoint
import io.homeassistant.companion.android.common.R
+import io.homeassistant.companion.android.common.data.integration.friendlyName
+import io.homeassistant.companion.android.common.data.integration.friendlyState
import io.homeassistant.companion.android.common.data.integration.getIcon
import io.homeassistant.companion.android.common.data.servers.ServerManager
import io.homeassistant.companion.android.database.wear.EntityStateComplicationsDao
+import retrofit2.HttpException
import javax.inject.Inject
@AndroidEntryPoint
@@ -34,60 +39,131 @@ class EntityStateDataSourceService : SuspendingComplicationDataSourceService() {
}
override suspend fun onComplicationRequest(request: ComplicationRequest): ComplicationData? {
- if (request.complicationType != ComplicationType.SHORT_TEXT) {
+ if (request.complicationType != ComplicationType.SHORT_TEXT && request.complicationType != ComplicationType.LONG_TEXT) {
return null
}
- val id = request.complicationInstanceId
-
- val entityId = entityStateComplicationsDao.get(id)?.entityId
- ?: return ShortTextComplicationData.Builder(
- text = PlainComplicationText.Builder(getText(R.string.complication_entity_invalid)).build(),
- contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description))
- .build()
- ).build()
+ val settings = entityStateComplicationsDao.get(request.complicationInstanceId)
+ val entityId = settings?.entityId
+ ?: return getErrorComplication(request, R.string.complication_entity_invalid, true)
val entity = try {
serverManager.integrationRepository().getEntity(entityId)
- ?: return ShortTextComplicationData.Builder(
- text = PlainComplicationText.Builder(getText(R.string.state_unknown)).build(),
- contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description))
- .build()
- ).build()
+ ?: return getErrorComplication(request, R.string.state_unknown)
} catch (t: Throwable) {
Log.e(TAG, "Unable to get entity state for $entityId: ${t.message}")
- return null
+ return if (t is HttpException && t.code() == 404) {
+ getErrorComplication(request, R.string.complication_entity_invalid)
+ } else {
+ null
+ }
}
- val attributes = entity.attributes as Map<*, *>
val icon = entity.getIcon(applicationContext) ?: CommunityMaterial.Icon.cmd_bookmark
val iconBitmap = IconicsDrawable(this, icon).apply {
colorInt = Color.WHITE
}.toBitmap()
- return ShortTextComplicationData.Builder(
- text = PlainComplicationText.Builder(entity.state).build(),
- contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description))
- .build()
- )
- .setTapAction(ComplicationReceiver.getComplicationToggleIntent(this, request.complicationInstanceId))
- .setMonochromaticImage(MonochromaticImage.Builder(Icon.createWithBitmap(iconBitmap)).build())
- .setTitle(PlainComplicationText.Builder(attributes["friendly_name"] as String? ?: entity.entityId).build())
- .build()
+
+ val title = if (settings.showTitle) {
+ PlainComplicationText.Builder(entity.friendlyName).build()
+ } else {
+ null
+ }
+ val text = PlainComplicationText.Builder(entity.friendlyState(this)).build()
+ val contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description)).build()
+ val monochromaticImage = MonochromaticImage.Builder(Icon.createWithBitmap(iconBitmap)).build()
+ val tapAction = ComplicationReceiver.getComplicationToggleIntent(this, request.complicationInstanceId)
+
+ return when (request.complicationType) {
+ ComplicationType.SHORT_TEXT -> {
+ ShortTextComplicationData.Builder(
+ text = text,
+ contentDescription = contentDescription
+ )
+ .setTitle(title)
+ .setTapAction(tapAction)
+ .setMonochromaticImage(monochromaticImage)
+ .build()
+ }
+ ComplicationType.LONG_TEXT -> {
+ LongTextComplicationData.Builder(
+ text = text,
+ contentDescription = contentDescription
+ )
+ .setTitle(title)
+ .setTapAction(tapAction)
+ .setMonochromaticImage(monochromaticImage)
+ .build()
+ }
+ else -> null // Already handled at the start of the function
+ }
}
- override fun getPreviewData(type: ComplicationType): ComplicationData =
- ShortTextComplicationData.Builder(
- text = PlainComplicationText.Builder(getText(R.string.complication_entity_state_preview)).build(),
- contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description)).build()
- )
- .setMonochromaticImage(
- MonochromaticImage.Builder(
- Icon.createWithResource(
- this,
- io.homeassistant.companion.android.R.drawable.ic_lightbulb
- )
- ).build()
+ override fun getPreviewData(type: ComplicationType): ComplicationData? {
+ val text = PlainComplicationText.Builder(getText(R.string.complication_entity_state_preview)).build()
+ val contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description)).build()
+ val title = PlainComplicationText.Builder(getText(R.string.entity)).build()
+ val monochromaticImage = MonochromaticImage.Builder(
+ Icon.createWithResource(
+ this,
+ io.homeassistant.companion.android.R.drawable.ic_lightbulb
)
- .setTitle(PlainComplicationText.Builder(getText(R.string.entity)).build())
- .build()
+ ).build()
+ return when (type) {
+ ComplicationType.SHORT_TEXT -> {
+ ShortTextComplicationData.Builder(
+ text = text,
+ contentDescription = contentDescription
+ )
+ .setTitle(title)
+ .setMonochromaticImage(monochromaticImage)
+ .build()
+ }
+ ComplicationType.LONG_TEXT -> {
+ LongTextComplicationData.Builder(
+ text = text,
+ contentDescription = contentDescription
+ )
+ .setTitle(title)
+ .setMonochromaticImage(monochromaticImage)
+ .build()
+ }
+ else -> {
+ Log.w(TAG, "Preview for unsupported complication type $type requested")
+ null
+ }
+ }
+ }
+
+ /**
+ * Get a simple complication for errors with [textRes] in the text slot.
+ *
+ * @param setTapAction If tapping the complication should open configuration
+ */
+ private fun getErrorComplication(
+ request: ComplicationRequest,
+ @StringRes textRes: Int,
+ setTapAction: Boolean = false
+ ): ComplicationData {
+ val text = PlainComplicationText.Builder(
+ if (setTapAction) { "+" } else { getText(textRes) }
+ ).build()
+ val contentDescription = PlainComplicationText.Builder(getText(R.string.complication_entity_state_content_description)).build()
+ val tapAction = if (setTapAction) {
+ ComplicationReceiver.getComplicationConfigureIntent(this, request.complicationInstanceId)
+ } else {
+ null
+ }
+ return if (request.complicationType == ComplicationType.SHORT_TEXT) {
+ ShortTextComplicationData.Builder(
+ text = text,
+ contentDescription = contentDescription
+ ).setTapAction(tapAction).build()
+ } else {
+ LongTextComplicationData.Builder(
+ text = text,
+ contentDescription = contentDescription
+ ).setTapAction(tapAction).build()
+ }
+ }
}
diff --git a/wear/src/main/java/io/homeassistant/companion/android/complications/views/ComplicationConfigMainView.kt b/wear/src/main/java/io/homeassistant/companion/android/complications/views/ComplicationConfigMainView.kt
index 01f5cdf89..4bc0e2d9d 100644
--- a/wear/src/main/java/io/homeassistant/companion/android/complications/views/ComplicationConfigMainView.kt
+++ b/wear/src/main/java/io/homeassistant/companion/android/complications/views/ComplicationConfigMainView.kt
@@ -7,25 +7,29 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Devices
+import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.wear.compose.material.Button
import androidx.wear.compose.material.ButtonDefaults
import androidx.wear.compose.material.Chip
import androidx.wear.compose.material.ChipDefaults
-import androidx.wear.compose.material.ExperimentalWearMaterialApi
+import androidx.wear.compose.material.Icon
import androidx.wear.compose.material.Text
+import androidx.wear.compose.material.ToggleChip
+import androidx.wear.compose.material.ToggleChipDefaults
import androidx.wear.compose.navigation.SwipeDismissableNavHost
import androidx.wear.compose.navigation.composable
import androidx.wear.compose.navigation.rememberSwipeDismissableNavController
import com.mikepenz.iconics.compose.Image
import com.mikepenz.iconics.typeface.library.community.material.CommunityMaterial
-import io.homeassistant.companion.android.HomeAssistantApplication
import io.homeassistant.companion.android.common.R
import io.homeassistant.companion.android.complications.ComplicationConfigViewModel
import io.homeassistant.companion.android.data.SimplifiedEntity
import io.homeassistant.companion.android.theme.WearAppTheme
import io.homeassistant.companion.android.theme.wearColorPalette
import io.homeassistant.companion.android.util.getIcon
+import io.homeassistant.companion.android.util.simplifiedEntity
import io.homeassistant.companion.android.views.ChooseEntityView
import io.homeassistant.companion.android.views.ListHeader
import io.homeassistant.companion.android.views.ThemeLazyColumn
@@ -33,7 +37,6 @@ import io.homeassistant.companion.android.views.ThemeLazyColumn
private const val SCREEN_MAIN = "main"
private const val SCREEN_CHOOSE_ENTITY = "choose_entity"
-@OptIn(ExperimentalWearMaterialApi::class)
@Composable
fun LoadConfigView(
complicationConfigViewModel: ComplicationConfigViewModel,
@@ -48,15 +51,16 @@ fun LoadConfigView(
composable(SCREEN_MAIN) {
MainConfigView(
entity = complicationConfigViewModel.selectedEntity,
+ showTitle = complicationConfigViewModel.entityShowTitle,
loadingState = complicationConfigViewModel.loadingState,
onChooseEntityClicked = {
swipeDismissableNavController.navigate(SCREEN_CHOOSE_ENTITY)
},
+ onShowTitleClicked = complicationConfigViewModel::setShowTitle,
onAcceptClicked = onAcceptClicked
)
}
composable(SCREEN_CHOOSE_ENTITY) {
- val app = complicationConfigViewModel.getApplication()
ChooseEntityView(
entitiesByDomainOrder = complicationConfigViewModel.entitiesByDomainOrder,
entitiesByDomain = complicationConfigViewModel.entitiesByDomain,
@@ -76,8 +80,10 @@ fun LoadConfigView(
@Composable
fun MainConfigView(
entity: SimplifiedEntity?,
+ showTitle: Boolean,
loadingState: ComplicationConfigViewModel.LoadingState,
onChooseEntityClicked: () -> Unit,
+ onShowTitleClicked: (Boolean) -> Unit,
onAcceptClicked: () -> Unit
) {
ThemeLazyColumn {
@@ -119,6 +125,26 @@ fun MainConfigView(
onClick = onChooseEntityClicked
)
}
+ item {
+ val isChecked = !loaded || showTitle
+ ToggleChip(
+ checked = isChecked,
+ onCheckedChange = onShowTitleClicked,
+ label = { Text(stringResource(R.string.show_entity_title)) },
+ toggleControl = {
+ Icon(
+ imageVector = ToggleChipDefaults.switchIcon(isChecked),
+ contentDescription = if (isChecked) {
+ stringResource(R.string.enabled)
+ } else {
+ stringResource(R.string.disabled)
+ }
+ )
+ },
+ modifier = Modifier.fillMaxWidth(),
+ enabled = loaded && entity != null
+ )
+ }
item {
Button(
@@ -139,3 +165,16 @@ fun MainConfigView(
}
}
}
+
+@Preview(device = Devices.WEAR_OS_LARGE_ROUND)
+@Composable
+fun PreviewMainConfigView() {
+ MainConfigView(
+ entity = simplifiedEntity,
+ showTitle = true,
+ loadingState = ComplicationConfigViewModel.LoadingState.READY,
+ onChooseEntityClicked = {},
+ onShowTitleClicked = {},
+ onAcceptClicked = {}
+ )
+}