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 = {} + ) +}