From da49f310c4be0b2ad59983a2c7a51b2a7a933a94 Mon Sep 17 00:00:00 2001 From: Sky233 Date: Wed, 11 Feb 2026 18:56:40 +0800 Subject: [PATCH 1/2] =?UTF-8?q?Feat(Tile):=20=E5=88=9D=E6=AD=A5=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20TileService?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 效果类似小米的通知栏里面的米家快捷操作 --- app/build.gradle.kts | 11 +- .../2.json | 303 ++++++++++++++++++ app/src/main/AndroidManifest.xml | 6 +- .../github/miwu/logic/database/AppDatabase.kt | 5 +- .../logic/database/entity/FavoriteDevice.kt | 7 +- .../miwu/logic/repository/DeviceRepository.kt | 52 +-- .../miwu/logic/repository/LocalRepository.kt | 57 +--- .../github/miwu/logic/repository/Module.kt | 10 +- .../repository/impl/DeviceRepositoryImpl.kt | 65 ++++ .../repository/impl/LocalRepositoryImpl.kt | 93 ++++++ .../github/miwu/service/DeviceTileService.kt | 163 +++++++++- .../github/miwu/ui/device/DeviceActivity.kt | 1 + .../miwu/ui/edit/EditFavoriteActivity.kt | 14 +- .../miwu/ui/edit/EditFavoriteViewModel.kt | 9 +- .../com/github/miwu/ui/main/MainViewModel.kt | 5 +- app/src/main/res/layout/activity_device.xml | 10 +- gradle/libs.versions.toml | 7 +- .../miwu/miot/model/miot/MiotDevices.kt | 4 +- .../support/manager/MiotDeviceManagerImpl.kt | 2 +- .../src/main/java/miwu/widget/Text.kt | 2 +- 20 files changed, 689 insertions(+), 137 deletions(-) create mode 100644 app/schemas/com.github.miwu.logic.database.AppDatabase/2.json create mode 100644 app/src/main/java/com/github/miwu/logic/repository/impl/DeviceRepositoryImpl.kt create mode 100644 app/src/main/java/com/github/miwu/logic/repository/impl/LocalRepositoryImpl.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b1c1f48c..262766f4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -82,8 +82,6 @@ dependencies { implementation(libs.glide) - implementation(libs.androidx.wear.tiles.tooling.preview) - debugImplementation(libs.androidx.wear.tiles.tooling) ksp(libs.glide.compiler) @@ -121,10 +119,10 @@ dependencies { implementation(libs.androidx.wear.protolayout) implementation(libs.androidx.wear.protolayout.material) implementation(libs.androidx.wear.protolayout.expression) - debugImplementation(libs.androidx.wear.tiles.tooling) - debugImplementation(libs.androidx.wear.tiles.tooling.preview) implementation(libs.androidx.concurrent.futures) - + implementation(libs.androidx.wear.tiles.tooling.preview) + implementation(libs.androidx.wear.tooling.preview) + debugImplementation(libs.androidx.wear.tiles.tooling) implementation(libs.google.guava) implementation(libs.google.horologist.compose.tools) @@ -151,6 +149,9 @@ dependencies { implementation(libs.resultat) + implementation(libs.ktor.client.core) + + testImplementation(libs.junit) androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.espresso.core) diff --git a/app/schemas/com.github.miwu.logic.database.AppDatabase/2.json b/app/schemas/com.github.miwu.logic.database.AppDatabase/2.json new file mode 100644 index 00000000..7d456c89 --- /dev/null +++ b/app/schemas/com.github.miwu.logic.database.AppDatabase/2.json @@ -0,0 +1,303 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "0518eb9590d1368b07546d66fe61ffff", + "entities": [ + { + "tableName": "favorite_device", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`bssid` TEXT NOT NULL, `cnt` INTEGER, `comFlag` INTEGER NOT NULL, `did` TEXT NOT NULL, `freqFlag` INTEGER NOT NULL, `hideMode` INTEGER, `isOnline` INTEGER NOT NULL, `lastOnline` INTEGER, `latitude` TEXT NOT NULL, `localIp` TEXT, `longitude` TEXT NOT NULL, `mac` TEXT NOT NULL, `model` TEXT NOT NULL, `name` TEXT NOT NULL, `orderTime` INTEGER NOT NULL, `parentId` TEXT, `permitLevel` INTEGER NOT NULL, `pid` INTEGER NOT NULL, `rssi` INTEGER, `showMode` INTEGER, `specType` TEXT, `ssid` TEXT, `token` TEXT NOT NULL, `uid` INTEGER NOT NULL, `fwVersion` TEXT, `isSetPinCode` INTEGER, `isSubGroup` INTEGER, `mcuVersion` TEXT, `pinCodeType` INTEGER, `platform` TEXT, `showGroupMember` INTEGER, PRIMARY KEY(`uid`, `did`))", + "fields": [ + { + "fieldPath": "bssid", + "columnName": "bssid", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cnt", + "columnName": "cnt", + "affinity": "INTEGER" + }, + { + "fieldPath": "comFlag", + "columnName": "comFlag", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "did", + "columnName": "did", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "freqFlag", + "columnName": "freqFlag", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hideMode", + "columnName": "hideMode", + "affinity": "INTEGER" + }, + { + "fieldPath": "isOnline", + "columnName": "isOnline", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastOnline", + "columnName": "lastOnline", + "affinity": "INTEGER" + }, + { + "fieldPath": "latitude", + "columnName": "latitude", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "localIp", + "columnName": "localIp", + "affinity": "TEXT" + }, + { + "fieldPath": "longitude", + "columnName": "longitude", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "mac", + "columnName": "mac", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "model", + "columnName": "model", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "orderTime", + "columnName": "orderTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "TEXT" + }, + { + "fieldPath": "permitLevel", + "columnName": "permitLevel", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pid", + "columnName": "pid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rssi", + "columnName": "rssi", + "affinity": "INTEGER" + }, + { + "fieldPath": "showMode", + "columnName": "showMode", + "affinity": "INTEGER" + }, + { + "fieldPath": "specType", + "columnName": "specType", + "affinity": "TEXT" + }, + { + "fieldPath": "ssid", + "columnName": "ssid", + "affinity": "TEXT" + }, + { + "fieldPath": "token", + "columnName": "token", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "extra.fwVersion", + "columnName": "fwVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "extra.isSetPinCode", + "columnName": "isSetPinCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "extra.isSubGroup", + "columnName": "isSubGroup", + "affinity": "INTEGER" + }, + { + "fieldPath": "extra.mcuVersion", + "columnName": "mcuVersion", + "affinity": "TEXT" + }, + { + "fieldPath": "extra.pinCodeType", + "columnName": "pinCodeType", + "affinity": "INTEGER" + }, + { + "fieldPath": "extra.platform", + "columnName": "platform", + "affinity": "TEXT" + }, + { + "fieldPath": "extra.showGroupMember", + "columnName": "showGroupMember", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uid", + "did" + ] + }, + "indices": [ + { + "name": "index_favorite_device_uid_did", + "unique": false, + "columnNames": [ + "uid", + "did" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_favorite_device_uid_did` ON `${TABLE_NAME}` (`uid`, `did`)" + } + ] + }, + { + "tableName": "favorite_device_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `did` TEXT NOT NULL, `sort_index` INTEGER NOT NULL DEFAULT 2147483647, PRIMARY KEY(`uid`, `did`), FOREIGN KEY(`uid`, `did`) REFERENCES `favorite_device`(`uid`, `did`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "uid", + "columnName": "uid", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "did", + "columnName": "did", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sortIndex", + "columnName": "sort_index", + "affinity": "INTEGER", + "notNull": true, + "defaultValue": "2147483647" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uid", + "did" + ] + }, + "indices": [ + { + "name": "index_favorite_device_metadata_uid_did", + "unique": false, + "columnNames": [ + "uid", + "did" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_favorite_device_metadata_uid_did` ON `${TABLE_NAME}` (`uid`, `did`)" + } + ], + "foreignKeys": [ + { + "table": "favorite_device", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uid", + "did" + ], + "referencedColumns": [ + "uid", + "did" + ] + } + ] + }, + { + "tableName": "crash_item", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`errorMessage` TEXT NOT NULL, `timestamp` INTEGER NOT NULL, `path` TEXT NOT NULL, `index` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "errorMessage", + "columnName": "errorMessage", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "index", + "columnName": "index", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "index" + ] + } + } + ], + "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, '0518eb9590d1368b07546d66fe61ffff')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 462cb94a..45cd3aa8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -7,7 +7,7 @@ - + () - private val roomMap = ArrayMap() - private val _deviceMetadataHandler = MutableStateFlow(newHandler()) - val deviceMetadataHandler = _deviceMetadataHandler.asStateFlow() +interface DeviceRepository { + val deviceMetadataHandler: StateFlow - private suspend fun update() { - handlerMutex.withLock { - _deviceMetadataHandler.emit(newHandler()) - } - } + suspend fun addIcon(models: List) - suspend fun addIcon(models: List) = withContext(Dispatchers.IO) { - models - .mapNotNull { model -> - if (model in iconMap) null - else specAttrProvider.getIconUrl(model) - .getOrNull() - ?.takeIf(String::isNotEmpty) - ?.let { model to it } - } - .takeIf(List<*>::isNotEmpty) - ?.let { - iconMapMutex.withLock { - iconMap.putAll(it) - } - update() - } - } + suspend fun addRoom(input: List>) - suspend fun addRoom(input: List>) { - input.takeIf(List<*>::isNotEmpty) - ?.let { - roomMapMutex.withLock { - roomMap.putAll(it) - } - update() - } - } + fun getRoom(did: String): String - fun getRoom(did: String): String = roomMap[did] ?: "未知位置" // TODO i18n - - fun getIcon(model: String): String? = iconMap[model] - - private fun newHandler() = DeviceMetadataHandler(iconMap.toMap(), roomMap.toMap()) + fun getIcon(model: String): String? } \ No newline at end of file diff --git a/app/src/main/java/com/github/miwu/logic/repository/LocalRepository.kt b/app/src/main/java/com/github/miwu/logic/repository/LocalRepository.kt index 4aa0f4af..4be305c9 100644 --- a/app/src/main/java/com/github/miwu/logic/repository/LocalRepository.kt +++ b/app/src/main/java/com/github/miwu/logic/repository/LocalRepository.kt @@ -1,48 +1,21 @@ package com.github.miwu.logic.repository -import com.github.miwu.logic.database.AppDatabase import com.github.miwu.logic.database.entity.FavoriteDevice -import com.github.miwu.logic.database.entity.FavoriteDevice.Companion.toMiot -import com.github.miwu.logic.database.entity.FavoriteDevice.Companion.toMiwu -import com.github.miwu.logic.database.entity.FavoriteDeviceMetadata -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch import miwu.miot.model.miot.MiotDevice -import miwu.support.base.MiwuDevice -import org.koin.core.component.KoinComponent -import org.koin.core.component.inject - -class LocalRepository : KoinComponent { - private val scope: CoroutineScope by inject() - private val database: AppDatabase by inject() - private val dao get() = database.deviceDAO() - private val deviceRepository: DeviceRepository by inject() - - val deviceList: Flow> = dao.observeList() - .onEach { deviceRepository.addIcon(it.map(FavoriteDevice::model)) } - -// val deviceList: Flow> = originDeviceList -// .map(::convert) - - fun addDevice(miotDevice: MiotDevice) { - scope.launch { - dao.insert(miotDevice.toMiwu()) - dao.insertMeta(FavoriteDeviceMetadata(miotDevice.uid, miotDevice.did)) - updateSortIndices(dao.getList()) - } - } - - fun updateSortIndices(list: List) { - scope.launch { - list.mapIndexed { index, item -> - FavoriteDeviceMetadata(item.uid, item.did, index) - }.let { dao.updateSortIndices(it) } - } - } - - private fun convert(list: List) = - list.map { it.toMiot() } +import java.util.concurrent.CopyOnWriteArrayList + +interface LocalRepository { + + val deviceListFlow: Flow> + + val deviceList: List + + val iconMap: Map + + fun addDevice(miotDevice: MiotDevice) + + fun removeDevice(favoriteDevice: FavoriteDevice) + + fun updateSortIndices(list: List) } diff --git a/app/src/main/java/com/github/miwu/logic/repository/Module.kt b/app/src/main/java/com/github/miwu/logic/repository/Module.kt index b7e26441..1837040a 100644 --- a/app/src/main/java/com/github/miwu/logic/repository/Module.kt +++ b/app/src/main/java/com/github/miwu/logic/repository/Module.kt @@ -1,16 +1,18 @@ package com.github.miwu.logic.repository import com.github.miwu.logic.repository.impl.AppRepositoryImpl +import com.github.miwu.logic.repository.impl.DeviceRepositoryImpl +import com.github.miwu.logic.repository.impl.LocalRepositoryImpl import org.koin.dsl.module val repositoryModule = module { single { AppRepositoryImpl() } - single { - LocalRepository() + single { + LocalRepositoryImpl() } - single { - DeviceRepository() + single { + DeviceRepositoryImpl() } } \ No newline at end of file diff --git a/app/src/main/java/com/github/miwu/logic/repository/impl/DeviceRepositoryImpl.kt b/app/src/main/java/com/github/miwu/logic/repository/impl/DeviceRepositoryImpl.kt new file mode 100644 index 00000000..1751ffce --- /dev/null +++ b/app/src/main/java/com/github/miwu/logic/repository/impl/DeviceRepositoryImpl.kt @@ -0,0 +1,65 @@ +package com.github.miwu.logic.repository.impl + +import android.util.ArrayMap +import com.github.miwu.logic.handler.DeviceMetadataHandler +import com.github.miwu.logic.repository.DeviceRepository +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext +import miwu.miot.provider.MiotSpecAttrProvider +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import kotlin.collections.putAll + +class DeviceRepositoryImpl : KoinComponent, DeviceRepository { + private val specAttrProvider: MiotSpecAttrProvider by inject() + private val iconMapMutex = Mutex() + private val roomMapMutex = Mutex() + private val handlerMutex = Mutex() + private val iconMap = ArrayMap() + private val roomMap = ArrayMap() + override val deviceMetadataHandler = MutableStateFlow(newHandler()) + + override suspend fun addIcon(models: List): Unit = + withContext(Dispatchers.IO) { + models + .mapNotNull { model -> + if (model in iconMap) null + else specAttrProvider.getIconUrl(model) + .getOrNull() + ?.takeIf(String::isNotEmpty) + ?.let { model to it } + } + .takeIf(List<*>::isNotEmpty) + ?.let { + iconMapMutex.withLock { + iconMap.putAll(it) + } + update() + } + } + + override suspend fun addRoom(input: List>) { + input.takeIf(List<*>::isNotEmpty) + ?.let { + roomMapMutex.withLock { + roomMap.putAll(it) + } + update() + } + } + + override fun getRoom(did: String): String = roomMap[did] ?: "未知位置" // TODO i18n + + override fun getIcon(model: String): String? = iconMap[model] + + private suspend fun update() { + handlerMutex.withLock { + deviceMetadataHandler.emit(newHandler()) + } + } + + private fun newHandler() = DeviceMetadataHandler(iconMap.toMap(), roomMap.toMap()) +} \ No newline at end of file diff --git a/app/src/main/java/com/github/miwu/logic/repository/impl/LocalRepositoryImpl.kt b/app/src/main/java/com/github/miwu/logic/repository/impl/LocalRepositoryImpl.kt new file mode 100644 index 00000000..f39d047c --- /dev/null +++ b/app/src/main/java/com/github/miwu/logic/repository/impl/LocalRepositoryImpl.kt @@ -0,0 +1,93 @@ +package com.github.miwu.logic.repository.impl + +import com.github.miwu.logic.database.AppDatabase +import com.github.miwu.logic.database.entity.FavoriteDevice +import com.github.miwu.logic.database.entity.FavoriteDevice.Companion.toMiot +import com.github.miwu.logic.database.entity.FavoriteDevice.Companion.toMiwu +import com.github.miwu.logic.database.entity.FavoriteDeviceMetadata +import com.github.miwu.logic.repository.DeviceRepository +import com.github.miwu.logic.repository.LocalRepository +import com.github.miwu.service.DeviceTileService +import com.github.miwu.utils.Logger +import io.ktor.client.HttpClient +import io.ktor.client.request.get +import io.ktor.client.statement.bodyAsBytes +import kndroidx.extension.log +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import miwu.miot.model.miot.MiotDevice +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.util.concurrent.CopyOnWriteArrayList + +class LocalRepositoryImpl : KoinComponent, LocalRepository { + private val scope: CoroutineScope by inject() + private val database: AppDatabase by inject() + private val dao get() = database.deviceDAO() + private val deviceRepository: DeviceRepository by inject() + private val deviceMetadataHandler = deviceRepository.deviceMetadataHandler + private val httpClient = HttpClient() + private val logger = Logger() + + init { + deviceMetadataHandler.onEach { + refreshIcon() + }.launchIn(scope) + } + + override val deviceList = CopyOnWriteArrayList() + override val iconMap = mutableMapOf() + override val deviceListFlow: Flow> = dao.observeList() + .onEach { deviceRepository.addIcon(it.map(FavoriteDevice::model)) } + .onEach { + deviceList.clear() + deviceList.addAll(it) + refreshIcon() + DeviceTileService.refresh() + } + + override fun addDevice(miotDevice: MiotDevice) { + scope.launch { + dao.insert(miotDevice.toMiwu()) + dao.insertMeta(FavoriteDeviceMetadata(miotDevice.uid, miotDevice.did)) + updateSortIndices(dao.getList()) + } + } + + override fun removeDevice(favoriteDevice: FavoriteDevice) { + scope.launch { + dao.delete(favoriteDevice) + } + } + + override fun updateSortIndices(list: List) { + scope.launch { + list + .mapIndexed { index, item -> + FavoriteDeviceMetadata(item.uid, item.did, index) + } + .let { dao.updateSortIndices(it) } + } + } + + private suspend fun refreshIcon() { + val handler = deviceMetadataHandler.value + logger.info("refreshIcon") + deviceList.take(4).forEach { device -> + val model = device.model + logger.info("model: {}", model) + if (iconMap[model] == null) { + handler.getIcon(model) + ?.let { httpClient.get(it) } + ?.bodyAsBytes() + ?.also { iconMap[model] = it } + ?: logger.info("model: {}, load icon failure", model) + } + } + } + + private fun convert(list: List) = list.map { it.toMiot() } +} diff --git a/app/src/main/java/com/github/miwu/service/DeviceTileService.kt b/app/src/main/java/com/github/miwu/service/DeviceTileService.kt index 8bc82810..237623f6 100644 --- a/app/src/main/java/com/github/miwu/service/DeviceTileService.kt +++ b/app/src/main/java/com/github/miwu/service/DeviceTileService.kt @@ -1,34 +1,84 @@ +@file:Suppress("FunctionName") + package com.github.miwu.service import android.content.Context +import androidx.wear.protolayout.material.Typography.TYPOGRAPHY_BUTTON +import androidx.wear.tiles.EventBuilders import androidx.wear.tiles.tooling.preview.Preview import androidx.wear.tiles.tooling.preview.TilePreviewData import androidx.wear.tiles.tooling.preview.TilePreviewHelper import androidx.wear.tooling.preview.devices.WearDevices -import kndroidx.extension.toast -import kndroidx.kndroidxConfig +import com.github.miwu.R +import com.github.miwu.appModule +import com.github.miwu.logic.database.entity.FavoriteDevice.Companion.toMiot +import com.github.miwu.logic.repository.AppRepository +import com.github.miwu.logic.repository.LocalRepository +import com.github.miwu.logic.setting.AppSetting +import com.github.miwu.utils.Logger +import kndroidx.KndroidConfig +import kndroidx.KndroidX +import kndroidx.wear.tile.Clickable import kndroidx.wear.tile.Modifier -import kndroidx.wear.tile.Tile +import kndroidx.wear.tile.ShapeBackground import kndroidx.wear.tile.alignment.HorizontalAlignment import kndroidx.wear.tile.alignment.VerticalAlignment +import kndroidx.wear.tile.background +import kndroidx.wear.tile.boolean +import kndroidx.wear.tile.clickable +import kndroidx.wear.tile.color import kndroidx.wear.tile.dp +import kndroidx.wear.tile.entity.ByteImage import kndroidx.wear.tile.fillMaxSize -import kndroidx.wear.tile.id +import kndroidx.wear.tile.fillMaxWidth +import kndroidx.wear.tile.height import kndroidx.wear.tile.layout -import kndroidx.wear.tile.layout.Arc import kndroidx.wear.tile.layout.Box import kndroidx.wear.tile.layout.Column import kndroidx.wear.tile.layout.Grid -import kndroidx.wear.tile.service.TileServiceX +import kndroidx.wear.tile.padding +import kndroidx.wear.tile.service.LayoutTileService +import kndroidx.wear.tile.string +import kndroidx.wear.tile.weight +import kndroidx.wear.tile.widget.Image +import kndroidx.wear.tile.widget.Spacer import kndroidx.wear.tile.widget.Text +import kndroidx.wear.tile.width +import kndroidx.wear.tile.wrapContentHeight +import kndroidx.wear.tile.wrapContentSize +import miwu.miot.model.miot.MiotDevice +import miwu.miot.utils.json +import org.koin.android.ext.android.inject +import org.koin.android.ext.koin.androidContext +import org.koin.android.ext.koin.androidLogger +import org.koin.core.context.startKoin +import java.io.File + +class DeviceTileService : LayoutTileService() { + private val logger = Logger() + val localRepository: LocalRepository by inject() + val appRepository: AppRepository by inject() + + override val version get() = (localRepository.iconMap.hashCode() + resVersion).toString() + private val resVersion = 1 -class DeviceTileService : TileServiceX() { + init { + onResourcesRequest() + } - override val version = "1" + override fun onClick(id: String) = invokeById(id) - override fun onClick(id: String) = autoHandle(id) + override fun onTileAddEvent(requestParams: EventBuilders.TileAddEvent) { + super.onTileAddEvent(requestParams) + } - override fun onResourcesRequest() {} + override fun onResourcesRequest() { + ResImage("scene", R.drawable.ic_miwu_scene_tile) + for ((model, icon) in localRepository.iconMap) { + logger.info("res version {}", version) + ByteImage(model, icon) + } + } override fun onLayout() = layout { Box( @@ -36,14 +86,95 @@ class DeviceTileService : TileServiceX() { horizontalAlignment = HorizontalAlignment.Center, verticalAlignment = VerticalAlignment.Center ) { - Text( - text = "hello tile", + if (localRepository.deviceList.isEmpty() || appRepository.miotUser == null) { + Text(text = "hello tile") + } else { + Page(localRepository.deviceList.map { it.toMiot() }) + } + } + } + + fun Any.Page(list: List) = + Grid( + modifier = Modifier + .fillMaxWidth() + .wrapContentHeight() + .padding(horizontal = 25.dp), + rowModifier = Modifier.padding(vertical = 3.dp), + spanCount = 2 + ) { + for ((i, device) in list.withIndex()) { + if (i >= 4) break + CardItem(device) + } + } + + fun Any.CardItem(device: MiotDevice) = + Box( + modifier = Modifier + .wrapContentSize() + .weight(1f, null) + .padding(horizontal = 3.dp) + ) { + Box( modifier = Modifier - .id("text") - .onClick { - "you click it!".toast() + .wrapContentSize() + .fillMaxWidth() + .background(ShapeBackground(0xFF202020.color, 15.dp)) + .clickable( + Clickable( + device.did, + packageName = packageName, + className = "com.github.miwu.ui.device.DeviceActivity" + ) { + boolean("isFromTile", true) + string("device", json.encodeToString(device)) + string("user", json.encodeToString(appRepository.miotUser)) + } + ) + ) { + Column( + modifier = Modifier + .wrapContentHeight() + .weight(weight = 1f) + .padding(horizontal = 10.dp, vertical = 10.dp) + ) { + Image( + resId = device.model, + modifier = Modifier + .width(25.dp) + .height(25.dp) + ) + Spacer(modifier = Modifier.height(5.dp).width(0.dp)) + Text( + text = device.name, + typography = TYPOGRAPHY_BUTTON + ) { + setColor(0xFFFFFFFF.color) } - ) + } + } + } + + companion object { + + fun refresh() { + getUpdater(KndroidX.context).requestUpdate(DeviceTileService::class.java) + } + + } + + + @Preview(device = WearDevices.LARGE_ROUND) + fun tilePreview(context: Context) = TilePreviewData { request -> + KndroidConfig.context = context + startKoin { + androidLogger() + androidContext(context) + modules(appModule) } + onLayout() + .let(TilePreviewHelper::singleTimelineEntryTileBuilder) + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/github/miwu/ui/device/DeviceActivity.kt b/app/src/main/java/com/github/miwu/ui/device/DeviceActivity.kt index 11549ff9..f62bf7b5 100644 --- a/app/src/main/java/com/github/miwu/ui/device/DeviceActivity.kt +++ b/app/src/main/java/com/github/miwu/ui/device/DeviceActivity.kt @@ -48,6 +48,7 @@ class DeviceActivity : ViewActivityX(Binding::inflate), MiotDeviceManag this ) } + val isFromTile by lazy { intent.getBooleanExtra("isFromTile", false) } override fun init() { printDeviceInfo() diff --git a/app/src/main/java/com/github/miwu/ui/edit/EditFavoriteActivity.kt b/app/src/main/java/com/github/miwu/ui/edit/EditFavoriteActivity.kt index daaf2b9a..6a58b5c3 100644 --- a/app/src/main/java/com/github/miwu/ui/edit/EditFavoriteActivity.kt +++ b/app/src/main/java/com/github/miwu/ui/edit/EditFavoriteActivity.kt @@ -34,7 +34,10 @@ class EditFavoriteActivity : ViewActivityX(Binding::inflate) { recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder ): Int { - return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0) + return makeMovementFlags( + ItemTouchHelper.UP or ItemTouchHelper.DOWN, + ItemTouchHelper.START + ) } override fun onMove( @@ -73,6 +76,13 @@ class EditFavoriteActivity : ViewActivityX(Binding::inflate) { start() } - override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit + override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) { + val position = viewHolder.absoluteAdapterPosition + if (direction == ItemTouchHelper.START) { + list?.get(position)?.let(viewModel::remove) + (list as? MutableList)?.removeAt(position) + adapter?.notifyItemRemoved(position) + } + } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/miwu/ui/edit/EditFavoriteViewModel.kt b/app/src/main/java/com/github/miwu/ui/edit/EditFavoriteViewModel.kt index 0edde2ea..decdb0b6 100644 --- a/app/src/main/java/com/github/miwu/ui/edit/EditFavoriteViewModel.kt +++ b/app/src/main/java/com/github/miwu/ui/edit/EditFavoriteViewModel.kt @@ -10,7 +10,6 @@ import com.github.miwu.ui.main.state.FragmentState.Empty import com.github.miwu.ui.main.state.FragmentState.Normal import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.take -import miwu.miot.model.miot.MiotDevice class EditFavoriteViewModel( val appRepository: AppRepository, @@ -19,14 +18,18 @@ class EditFavoriteViewModel( ) : ViewModel() { val metadataHandler = deviceRepository.deviceMetadataHandler .asLiveData() - val devices = localRepository.deviceList + val devices = localRepository.deviceListFlow .take(1) .asLiveData() - val deviceState = localRepository.deviceList + val deviceState = localRepository.deviceListFlow .map { if (it.isEmpty()) Empty else Normal } .asLiveData() fun updateSortIndices(list: List) { localRepository.updateSortIndices(list) } + + fun remove(item: FavoriteDevice) { + localRepository.removeDevice(item) + } } \ No newline at end of file diff --git a/app/src/main/java/com/github/miwu/ui/main/MainViewModel.kt b/app/src/main/java/com/github/miwu/ui/main/MainViewModel.kt index cef8b6ef..80302b77 100644 --- a/app/src/main/java/com/github/miwu/ui/main/MainViewModel.kt +++ b/app/src/main/java/com/github/miwu/ui/main/MainViewModel.kt @@ -13,7 +13,6 @@ import com.github.miwu.ui.main.state.FragmentState.Normal import com.github.miwu.utils.Logger import fr.haan.resultat.fold import kotlinx.coroutines.flow.map -import org.koin.core.annotation.KoinViewModel class MainViewModel( @@ -25,9 +24,9 @@ class MainViewModel( private val logger = Logger() val metadataHandler = deviceRepository.deviceMetadataHandler .asLiveData() - val localDevices = localRepository.deviceList + val localDevices = localRepository.deviceListFlow .asLiveData() - val localDeviceState = localRepository.deviceList + val localDeviceState = localRepository.deviceListFlow .map { if (it.isEmpty()) Empty else Normal } .asLiveData() val scenes = appRepository.scenes diff --git a/app/src/main/res/layout/activity_device.xml b/app/src/main/res/layout/activity_device.xml index 3e97b6e1..20c898bd 100644 --- a/app/src/main/res/layout/activity_device.xml +++ b/app/src/main/res/layout/activity_device.xml @@ -58,13 +58,15 @@ + android:onClick="@{() -> activity.onAddButtonClick()}" + android:text="添加到卡片" + app:isVisible="@{!activity.isFromTile}" /> + android:onClick="@{() -> activity.onStarButtonClick()}" + android:text="收藏设备" + app:isVisible="@{!activity.isFromTile}" /> \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index be670dcb..a0193929 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -35,7 +35,7 @@ microsoft-appcenter = "5.0.6" glidecompiler = "5.0.5" glide = "5.0.5" gson = "2.13.2" -kndroidx = "2d113169a0" +kndroidx = "261cbda5ef" androidx-lifecycle = "2.10.0" androidx-wear-protolayout = "1.3.0" resultat = "1.0.0" @@ -51,7 +51,8 @@ androidx-cardview = "1.0.0" androidx-room-common-jvm = "2.8.4" androidx-recyclerview = "1.4.0" androidx-wear-tiles-tooling-preview = "1.5.0" -androidx-wear-tiles-tooling = "1.5.0" +wear-tooling-preview="1.0.0" +androidx-wear-tiles-tooling = "1.6.0-alpha05" #noinspection NewerVersionAvailable slf4jApi = "2.0.7" logback-android = "3.0.0" @@ -130,6 +131,7 @@ androidx-wear-tiles = { module = "androidx.wear.tiles:tiles", version.ref = "and androidx-wear-protolayout-expression = { module = "androidx.wear.protolayout:protolayout-expression", version.ref = "androidx-wear-protolayout" } androidx-wear-protolayout-material = { module = "androidx.wear.protolayout:protolayout-material", version.ref = "androidx-wear-protolayout" } androidx-wear-tiles-tooling-preview = { group = "androidx.wear.tiles", name = "tiles-tooling-preview", version.ref = "androidx-wear-tiles-tooling-preview" } +androidx-wear-tooling-preview = { group = "androidx.wear", name = "wear-tooling-preview", version.ref = "wear-tooling-preview" } androidx-wear-tiles-tooling = { group = "androidx.wear.tiles", name = "tiles-tooling", version.ref = "androidx-wear-tiles-tooling" } @@ -179,6 +181,7 @@ kotlin-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version. ktorfit = { group = "de.jensklingenberg.ktorfit", name = "ktorfit-lib", version.ref = "ktorfit" } +ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" } ktor-client-json = { module = "io.ktor:ktor-client-json", version.ref = "ktor" } ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" } ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" } diff --git a/miot-api/src/commonMain/kotlin/miwu/miot/model/miot/MiotDevices.kt b/miot-api/src/commonMain/kotlin/miwu/miot/model/miot/MiotDevices.kt index 26cdfa05..03b62045 100644 --- a/miot-api/src/commonMain/kotlin/miwu/miot/model/miot/MiotDevices.kt +++ b/miot-api/src/commonMain/kotlin/miwu/miot/model/miot/MiotDevices.kt @@ -31,7 +31,7 @@ data class MiotDevices( @SerialName("did") val did: String, @SerialName("extra") val extra: Extra, @SerialName("freqFlag") val freqFlag: Boolean, - @SerialName("hide_mode") val hideMode: Int, + @SerialName("hide_mode") val hideMode: Int? = 0, @SerialName("isOnline") val isOnline: Boolean, @SerialName("last_online") val lastOnline: Long? = null, @SerialName("latitude") val latitude: String, @@ -45,7 +45,7 @@ data class MiotDevices( @SerialName("permitLevel") val permitLevel: Int, @SerialName("pid") val pid: Int, @SerialName("rssi") val rssi: Int? = 0, - @SerialName("show_mode") val showMode: Int, + @SerialName("show_mode") val showMode: Int? = 0, @SerialName("spec_type") val specType: String? = null, @SerialName("ssid") val ssid: String? = null, @SerialName("token") val token: String, diff --git a/miwu-support/src/main/java/miwu/support/manager/MiotDeviceManagerImpl.kt b/miwu-support/src/main/java/miwu/support/manager/MiotDeviceManagerImpl.kt index 3013f274..6fa9996a 100644 --- a/miwu-support/src/main/java/miwu/support/manager/MiotDeviceManagerImpl.kt +++ b/miwu-support/src/main/java/miwu/support/manager/MiotDeviceManagerImpl.kt @@ -58,7 +58,7 @@ class MiotDeviceManagerImpl internal constructor( private val updatingInterval = 500L private val widgetHolders = CopyOnWriteArrayList() private val supportWidget = mutableSetOf() - private val deviceUrn = device.specType!! + private val deviceUrn = device.specType ?: "" private var isOutdated = false private val job = Job() private val scope = CoroutineScope(job) diff --git a/miwu-support/src/main/java/miwu/widget/Text.kt b/miwu-support/src/main/java/miwu/widget/Text.kt index 0cbcb3fe..5f7acbd0 100644 --- a/miwu-support/src/main/java/miwu/widget/Text.kt +++ b/miwu-support/src/main/java/miwu/widget/Text.kt @@ -11,4 +11,4 @@ import miwu.support.base.MiwuWidget @Bind("environment", "relative-humidity") @Bind("environment", "pm2.5-density") @Bind("gas-sensor", "gas-concentration") -class Text : MiwuWidget() +class Text : MiwuWidget() \ No newline at end of file From 158c87bd2ed857868297e6afcb3361be6daaef88 Mon Sep 17 00:00:00 2001 From: Sky233 Date: Wed, 11 Feb 2026 23:07:13 +0800 Subject: [PATCH 2/2] =?UTF-8?q?Refactor:=20=E9=A1=B9=E7=9B=AE=E6=9E=B6?= =?UTF-8?q?=E6=9E=84=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - `DeviceActivity` 将大部分工作转移到 ViewModel 中, 划分职责 --- .../github/miwu/service/DeviceTileService.kt | 3 - .../main/java/com/github/miwu/ui/ViewModel.kt | 2 +- .../github/miwu/ui/device/DeviceActivity.kt | 71 ++++++---------- .../github/miwu/ui/device/DeviceViewModel.kt | 80 ++++++++++++++++++- app/src/main/res/layout/activity_device.xml | 39 +++++++-- app/src/main/res/values/strings.xml | 2 + 6 files changed, 138 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/com/github/miwu/service/DeviceTileService.kt b/app/src/main/java/com/github/miwu/service/DeviceTileService.kt index 237623f6..a7e8589b 100644 --- a/app/src/main/java/com/github/miwu/service/DeviceTileService.kt +++ b/app/src/main/java/com/github/miwu/service/DeviceTileService.kt @@ -14,7 +14,6 @@ import com.github.miwu.appModule import com.github.miwu.logic.database.entity.FavoriteDevice.Companion.toMiot import com.github.miwu.logic.repository.AppRepository import com.github.miwu.logic.repository.LocalRepository -import com.github.miwu.logic.setting.AppSetting import com.github.miwu.utils.Logger import kndroidx.KndroidConfig import kndroidx.KndroidX @@ -28,7 +27,6 @@ import kndroidx.wear.tile.boolean import kndroidx.wear.tile.clickable import kndroidx.wear.tile.color import kndroidx.wear.tile.dp -import kndroidx.wear.tile.entity.ByteImage import kndroidx.wear.tile.fillMaxSize import kndroidx.wear.tile.fillMaxWidth import kndroidx.wear.tile.height @@ -52,7 +50,6 @@ import org.koin.android.ext.android.inject import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.core.context.startKoin -import java.io.File class DeviceTileService : LayoutTileService() { private val logger = Logger() diff --git a/app/src/main/java/com/github/miwu/ui/ViewModel.kt b/app/src/main/java/com/github/miwu/ui/ViewModel.kt index e4b8859d..337072a1 100644 --- a/app/src/main/java/com/github/miwu/ui/ViewModel.kt +++ b/app/src/main/java/com/github/miwu/ui/ViewModel.kt @@ -33,7 +33,7 @@ val viewModelModule = module { EditFavoriteViewModel(get(), get(), get()) } viewModel { - DeviceViewModel(get()) + DeviceViewModel(get(), get(), get(), get()) } viewModel { CrashViewModel() diff --git a/app/src/main/java/com/github/miwu/ui/device/DeviceActivity.kt b/app/src/main/java/com/github/miwu/ui/device/DeviceActivity.kt index f62bf7b5..26d905ad 100644 --- a/app/src/main/java/com/github/miwu/ui/device/DeviceActivity.kt +++ b/app/src/main/java/com/github/miwu/ui/device/DeviceActivity.kt @@ -4,11 +4,17 @@ import android.content.Context import android.view.ViewGroup import android.widget.LinearLayout import androidx.core.view.isVisible +import androidx.lifecycle.lifecycleScope +import com.github.miwu.ui.device.DeviceViewModel.Event.DeviceInitiated import com.github.miwu.utils.Logger import com.github.miwu.utils.MiotDeviceClient +import fr.haan.resultat.Resultat import kndroidx.activity.ViewActivityX import kndroidx.extension.start import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import miwu.android.R import miwu.android.icon.generated.icon.AndroidIcons import miwu.android.translate.AndroidTranslateHelper @@ -27,59 +33,41 @@ import org.koin.android.ext.android.inject import org.koin.androidx.viewmodel.ext.android.viewModel import com.github.miwu.databinding.ActivityDeviceBinding as Binding -class DeviceActivity : ViewActivityX(Binding::inflate), MiotDeviceManager.Callback { +class DeviceActivity : ViewActivityX(Binding::inflate) { override val viewModel: DeviceViewModel by viewModel() private val logger = Logger() - private val device by lazy { intent.getStringExtra("device")!!.to().getOrThrow() } - private val user by lazy { intent.getStringExtra("user")!!.to().getOrThrow() } - private val miotDeviceClient by lazy { MiotDeviceClient(user) } - private val specAttrProvider: MiotSpecAttrProvider by inject() private val marginBottom by lazy { resources.getDimensionPixelSize(R.dimen.device_miwu_layout_margin_bottom) } private val wrapperList = arrayListOf>() - private val manager by lazy { - MiotDeviceManager.build( - miotDeviceClient, - specAttrProvider, - device, - AndroidIcons, - AndroidCache(this), - AndroidTranslateHelper, - Dispatchers.Main, - this - ) - } - val isFromTile by lazy { intent.getBooleanExtra("isFromTile", false) } override fun init() { - printDeviceInfo() - manager.init() + with(viewModel) { + event.onEach(::onEvent) + .launchIn(lifecycleScope) + printDeviceInfo() + manager.init() + } } override fun onDestroy() { - manager.stop() + viewModel.manager.stop() super.onDestroy() } + fun onEvent(event: DeviceViewModel.Event) { + when(event) { + DeviceInitiated -> { + initDeviceLayout() + wrapperList.forEach(MiwuWrapper<*>::init) + } + } + } + fun onAddButtonClick() { } fun onStarButtonClick() { - viewModel.addFavorite(device) - } - - private fun printDeviceInfo() { - with(device) { - logger.info( - "Current miot device info: model={}, mac={}, did={}, isOnline={}, specType={}", - model, - mac, - did, - isOnline, - specType, - ) - logger.debug("Current miot all device info: {}", this) - } + viewModel.addFavorite() } private inline fun T.addWidget( @@ -104,17 +92,8 @@ class DeviceActivity : ViewActivityX(Binding::inflate), MiotDeviceManag setMargins(0, 0, 0, marginBottom) } - override fun onDeviceInitiated() { - initDeviceLayout() - wrapperList.forEach(MiwuWrapper<*>::init) - } - - override fun onDeviceAttLoaded(specAtt: SpecAtt) { - logger.info("Device {}, spec att: {}", device.name, specAtt) - } - private fun initDeviceLayout() { - with(manager.layout) { + with(viewModel.manager.layout) { with(binding) { Header { header.addWidget(it) diff --git a/app/src/main/java/com/github/miwu/ui/device/DeviceViewModel.kt b/app/src/main/java/com/github/miwu/ui/device/DeviceViewModel.kt index 86021fd6..42cc51f7 100644 --- a/app/src/main/java/com/github/miwu/ui/device/DeviceViewModel.kt +++ b/app/src/main/java/com/github/miwu/ui/device/DeviceViewModel.kt @@ -1,15 +1,91 @@ package com.github.miwu.ui.device +import android.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.github.miwu.logic.database.AppDatabase import com.github.miwu.logic.database.entity.FavoriteDevice.Companion.toMiwu import com.github.miwu.logic.repository.LocalRepository +import com.github.miwu.utils.Logger +import com.github.miwu.utils.MiotDeviceClient +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch +import miwu.android.icon.generated.icon.AndroidIcons +import miwu.android.translate.AndroidTranslateHelper +import miwu.miot.kmp.utils.to +import miwu.miot.model.MiotUser +import miwu.miot.model.att.SpecAtt import miwu.miot.model.miot.MiotDevice +import miwu.miot.provider.MiotSpecAttrProvider +import miwu.support.manager.MiotDeviceManager +import org.koin.android.ext.android.inject +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import kotlin.getValue -class DeviceViewModel(val localRepository: LocalRepository) : ViewModel() { - fun addFavorite(device: MiotDevice) { +class DeviceViewModel( + private val application: Application, + private val localRepository: LocalRepository, + private val savedStateHandle: SavedStateHandle, + private val specAttrProvider: MiotSpecAttrProvider +) : AndroidViewModel(application), MiotDeviceManager.Callback, KoinComponent { + private val logger = Logger() + private val device = savedStateHandle.get("device") + ?.to() + ?.getOrThrow() + ?: error("MiotDevice is not found") + private val user = savedStateHandle.get("user") + ?.to() + ?.getOrThrow() + ?: error("MiotUser is not found") + private val miotDeviceClient = MiotDeviceClient(user) + private val _event = MutableSharedFlow() + val event = _event.asSharedFlow() + val isFromTile = savedStateHandle.get("isFromTile") ?: false + val manager by lazy { + MiotDeviceManager.build( + miotDeviceClient, + specAttrProvider, + device, + AndroidIcons, + AndroidCache(application), + AndroidTranslateHelper, + Dispatchers.Main, + this + ) + } + + fun printDeviceInfo() { + with(device) { + logger.info( + "Current miot device info: model={}, mac={}, did={}, isOnline={}, specType={}", + model, + mac, + did, + isOnline, + specType, + ) + logger.debug("Current miot all device info: {}", this) + } + } + + fun addFavorite() { localRepository.addDevice(device) } + + override fun onDeviceInitiated() { + _event.tryEmit(Event.DeviceInitiated) + } + + override fun onDeviceAttLoaded(specAtt: SpecAtt) { + logger.info("Device {}, spec att: {}", device.name, specAtt) + } + + sealed interface Event { + object DeviceInitiated: Event + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_device.xml b/app/src/main/res/layout/activity_device.xml index 20c898bd..623072e1 100644 --- a/app/src/main/res/layout/activity_device.xml +++ b/app/src/main/res/layout/activity_device.xml @@ -8,6 +8,9 @@ + + + + + + + + + @@ -56,17 +81,17 @@ android:visibility="gone" /> - + + + + + + android:text="@string/favorite_device" + app:isVisible="@{!viewModel.isFromTile}" /> \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d294ca14..f4018c3d 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -94,6 +94,8 @@ 登录二维码 网络错误, 请检查网络后再试 登录过期, 请重新登录 + 添加到卡片 + 收藏设备