diff --git a/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/Protocol.kt b/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/Protocol.kt index 7dd2cb35..837d80be 100644 --- a/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/Protocol.kt +++ b/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/Protocol.kt @@ -24,6 +24,7 @@ object Protocol { object Method { const val RegisterDevice = "registerDevice" + const val AppIcon = "appIcon" } } @@ -118,6 +119,14 @@ object Protocol { } } + object Device { + const val Plugin = "device" + + object Method { + const val GetAppIcon = "getAppIcon" + } + } + object Files { const val Plugin = "files" diff --git a/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/client/FloconClientImpl.kt b/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/client/FloconClientImpl.kt index 29d880a5..c61545e3 100644 --- a/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/client/FloconClientImpl.kt +++ b/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/client/FloconClientImpl.kt @@ -50,7 +50,7 @@ internal class FloconClientImpl( override val tablePlugin = FloconTablePluginImpl(sender = this) override val deeplinksPlugin = FloconDeeplinksPluginImpl(sender = this) override val analyticsPlugin = FloconAnalyticsPluginImpl(sender = this) - override val devicePlugin = FloconDevicePluginImpl(sender = this) + override val devicePlugin = FloconDevicePluginImpl(sender = this, context = appContext) override val networkPlugin = FloconNetworkPluginImpl( context = appContext, sender = this, @@ -113,6 +113,13 @@ internal class FloconClientImpl( ) } + Protocol.ToDevice.Device.Plugin -> { + devicePlugin.onMessageReceived( + messageFromServer = messageFromServer, + sender = this@FloconClientImpl, + ) + } + Protocol.ToDevice.Dashboard.Plugin -> { dashboardPlugin.onMessageReceived( messageFromServer = messageFromServer, diff --git a/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt b/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt index bd9e9e4c..b669b904 100644 --- a/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt +++ b/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/plugins/device/FloconDevicePluginImpl.kt @@ -1,5 +1,6 @@ package io.github.openflocon.flocon.plugins.device +import android.content.Context import io.github.openflocon.flocon.Protocol import io.github.openflocon.flocon.core.FloconMessageSender import io.github.openflocon.flocon.model.FloconMessageFromServer @@ -7,6 +8,7 @@ import io.github.openflocon.flocon.plugins.device.model.fromdevice.RegisterDevic class FloconDevicePluginImpl( private var sender: FloconMessageSender, + private val context: Context, ) : FloconDevicePlugin { override fun registerWithSerial(serial: String) { @@ -21,7 +23,18 @@ class FloconDevicePluginImpl( messageFromServer: FloconMessageFromServer, sender: FloconMessageSender ) { - // no op + when (messageFromServer.method) { + Protocol.ToDevice.Device.Method.GetAppIcon -> { + val icon = getAppIconBase64(context) + if (icon != null) { + sender.send( + plugin = Protocol.FromDevice.Device.Plugin, + method = Protocol.FromDevice.Device.Method.AppIcon, + body = icon, + ) + } + } + } } override fun onConnectedToServer(sender: FloconMessageSender) { diff --git a/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.kt b/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.kt new file mode 100644 index 00000000..2dd09c48 --- /dev/null +++ b/FloconAndroid/flocon/src/main/java/io/github/openflocon/flocon/plugins/device/GetAppIconUtils.kt @@ -0,0 +1,63 @@ +package io.github.openflocon.flocon.plugins.device + +import android.content.Context +import android.graphics.Bitmap +import android.graphics.Canvas +import android.graphics.drawable.Drawable +import android.util.Base64 +import io.github.openflocon.flocon.FloconLogger +import java.io.ByteArrayOutputStream + +internal fun getAppIconBase64(context: Context): String? { + return try { + val bitmap = getAppIcon(context) + val resizedBitmap = resizeAppIcon(bitmap, maxSize = 300) + encodeToBase64(resizedBitmap) + } catch (t: Throwable) { + FloconLogger.logError( + text = "Error while getting app icon", + throwable = t, + ) + null + } +} + +private fun getAppIcon(context: Context): Bitmap { + // 1. Récupération de l'icône en bitmap + val packageManager = context.packageManager + val packageName = context.packageName + val iconDrawable: Drawable = packageManager.getApplicationIcon(packageName) + + val bitmap = Bitmap.createBitmap( + iconDrawable.intrinsicWidth, + iconDrawable.intrinsicHeight, + Bitmap.Config.ARGB_8888 + ) + val canvas = Canvas(bitmap) + iconDrawable.setBounds(0, 0, canvas.width, canvas.height) + iconDrawable.draw(canvas) + + return bitmap +} + +private fun encodeToBase64(resizedBitmap: Bitmap): String { + // 3. Conversion en Base64 + val outputStream = ByteArrayOutputStream() + resizedBitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream) + val byteArray = outputStream.toByteArray() + + return Base64.encodeToString(byteArray, Base64.NO_WRAP) // NO_WRAP pour éviter les \n +} + +private fun resizeAppIcon(bitmap: Bitmap, maxSize: Int): Bitmap { + val width = bitmap.width + val height = bitmap.height + val scale = minOf(maxSize.toFloat() / width, maxSize.toFloat() / height, 1f) + + return Bitmap.createScaledBitmap( + bitmap, + (width * scale).toInt(), + (height * scale).toInt(), + true + ) +} diff --git a/FloconAndroid/gradle.properties b/FloconAndroid/gradle.properties index aa3d78d2..49915de5 100644 --- a/FloconAndroid/gradle.properties +++ b/FloconAndroid/gradle.properties @@ -24,5 +24,5 @@ android.nonTransitiveRClass=true floconGroupId=io.github.openflocon -floconVersion=1.0.5 +floconVersion=1.1.2 floconDescription=A powerful desktop companion for Android apps, inspect, debug, and control your app in real time. \ No newline at end of file diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/44.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/44.json new file mode 100644 index 00000000..a0133ce3 --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/44.json @@ -0,0 +1,1076 @@ +{ + "formatVersion": 1, + "database": { + "version": 44, + "identityHash": "709eea740fda569b945b16d91e67d5c4", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `type` TEXT NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `response_durationMs` REAL, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseError` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`))", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`))", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardSectionEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `sectionOrder` INTEGER NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sectionOrder", + "columnName": "sectionOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardSectionEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardSectionEntity_dashboardId_sectionOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "sectionOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId_sectionOrder` ON `${TABLE_NAME}` (`dashboardId`, `sectionOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sectionId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `button_text` TEXT, `button_actionId` TEXT, `text_label` TEXT, `text_value` TEXT, `text_color` INTEGER, `plainText_label` TEXT, `plainText_value` TEXT, `plainText_type` TEXT, `textField_actionId` TEXT, `textField_label` TEXT, `textField_value` TEXT, `textField_placeHolder` TEXT, `checkBox_actionId` TEXT, `checkBox_label` TEXT, `checkBox_value` INTEGER, FOREIGN KEY(`sectionId`) REFERENCES `DashboardSectionEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sectionId", + "columnName": "sectionId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "button.text", + "columnName": "button_text", + "affinity": "TEXT" + }, + { + "fieldPath": "button.actionId", + "columnName": "button_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "text.label", + "columnName": "text_label", + "affinity": "TEXT" + }, + { + "fieldPath": "text.value", + "columnName": "text_value", + "affinity": "TEXT" + }, + { + "fieldPath": "text.color", + "columnName": "text_color", + "affinity": "INTEGER" + }, + { + "fieldPath": "plainText.label", + "columnName": "plainText_label", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.value", + "columnName": "plainText_value", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.type", + "columnName": "plainText_type", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.actionId", + "columnName": "textField_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.label", + "columnName": "textField_label", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.value", + "columnName": "textField_value", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.placeHolder", + "columnName": "textField_placeHolder", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.actionId", + "columnName": "checkBox_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.label", + "columnName": "checkBox_label", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.value", + "columnName": "checkBox_value", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_sectionId", + "unique": false, + "columnNames": [ + "sectionId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId` ON `${TABLE_NAME}` (`sectionId`)" + }, + { + "name": "index_DashboardElementEntity_sectionId_elementOrder", + "unique": true, + "columnNames": [ + "sectionId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId_elementOrder` ON `${TABLE_NAME}` (`sectionId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardSectionEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "sectionId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `url`, `time`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "url", + "time" + ] + } + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`))", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ] + }, + { + "tableName": "network_filter", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + } + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`))", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + } + ], + "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, '709eea740fda569b945b16d91e67d5c4')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/45.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/45.json new file mode 100644 index 00000000..82b0ebed --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/45.json @@ -0,0 +1,1188 @@ +{ + "formatVersion": 1, + "database": { + "version": 45, + "identityHash": "4fe6cb3fc9787e4632ab16e469900beb", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `type` TEXT NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `response_durationMs` REAL, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseError` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`))", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`))", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardSectionEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `sectionOrder` INTEGER NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sectionOrder", + "columnName": "sectionOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardSectionEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardSectionEntity_dashboardId_sectionOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "sectionOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId_sectionOrder` ON `${TABLE_NAME}` (`dashboardId`, `sectionOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sectionId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `button_text` TEXT, `button_actionId` TEXT, `text_label` TEXT, `text_value` TEXT, `text_color` INTEGER, `plainText_label` TEXT, `plainText_value` TEXT, `plainText_type` TEXT, `textField_actionId` TEXT, `textField_label` TEXT, `textField_value` TEXT, `textField_placeHolder` TEXT, `checkBox_actionId` TEXT, `checkBox_label` TEXT, `checkBox_value` INTEGER, FOREIGN KEY(`sectionId`) REFERENCES `DashboardSectionEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sectionId", + "columnName": "sectionId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "button.text", + "columnName": "button_text", + "affinity": "TEXT" + }, + { + "fieldPath": "button.actionId", + "columnName": "button_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "text.label", + "columnName": "text_label", + "affinity": "TEXT" + }, + { + "fieldPath": "text.value", + "columnName": "text_value", + "affinity": "TEXT" + }, + { + "fieldPath": "text.color", + "columnName": "text_color", + "affinity": "INTEGER" + }, + { + "fieldPath": "plainText.label", + "columnName": "plainText_label", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.value", + "columnName": "plainText_value", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.type", + "columnName": "plainText_type", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.actionId", + "columnName": "textField_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.label", + "columnName": "textField_label", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.value", + "columnName": "textField_value", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.placeHolder", + "columnName": "textField_placeHolder", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.actionId", + "columnName": "checkBox_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.label", + "columnName": "checkBox_label", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.value", + "columnName": "checkBox_value", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_sectionId", + "unique": false, + "columnNames": [ + "sectionId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId` ON `${TABLE_NAME}` (`sectionId`)" + }, + { + "name": "index_DashboardElementEntity_sectionId_elementOrder", + "unique": true, + "columnNames": [ + "sectionId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId_elementOrder` ON `${TABLE_NAME}` (`sectionId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardSectionEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "sectionId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `url`, `time`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "url", + "time" + ] + } + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`))", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ] + }, + { + "tableName": "network_filter", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + } + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`))", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`parentDeviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`parentDeviceId`, `packageName`), FOREIGN KEY(`parentDeviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "parentDeviceId", + "columnName": "parentDeviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentDeviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "parentDeviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "DeviceAppIconEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `appPackageName` TEXT NOT NULL, `iconEncoded` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `appPackageName`), FOREIGN KEY(`appPackageName`) REFERENCES `DeviceAppEntity`(`packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appPackageName", + "columnName": "appPackageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "appPackageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "appPackageName" + ], + "referencedColumns": [ + "packageName" + ] + } + ] + } + ], + "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, '4fe6cb3fc9787e4632ab16e469900beb')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/46.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/46.json new file mode 100644 index 00000000..6063d4f8 --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/46.json @@ -0,0 +1,1190 @@ +{ + "formatVersion": 1, + "database": { + "version": 46, + "identityHash": "3212e7b0c9d5ed1ffee43064f73307dc", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `type` TEXT NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `response_durationMs` REAL, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseError` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`))", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`))", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardSectionEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `sectionOrder` INTEGER NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sectionOrder", + "columnName": "sectionOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardSectionEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardSectionEntity_dashboardId_sectionOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "sectionOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId_sectionOrder` ON `${TABLE_NAME}` (`dashboardId`, `sectionOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sectionId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `button_text` TEXT, `button_actionId` TEXT, `text_label` TEXT, `text_value` TEXT, `text_color` INTEGER, `plainText_label` TEXT, `plainText_value` TEXT, `plainText_type` TEXT, `textField_actionId` TEXT, `textField_label` TEXT, `textField_value` TEXT, `textField_placeHolder` TEXT, `checkBox_actionId` TEXT, `checkBox_label` TEXT, `checkBox_value` INTEGER, FOREIGN KEY(`sectionId`) REFERENCES `DashboardSectionEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sectionId", + "columnName": "sectionId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "button.text", + "columnName": "button_text", + "affinity": "TEXT" + }, + { + "fieldPath": "button.actionId", + "columnName": "button_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "text.label", + "columnName": "text_label", + "affinity": "TEXT" + }, + { + "fieldPath": "text.value", + "columnName": "text_value", + "affinity": "TEXT" + }, + { + "fieldPath": "text.color", + "columnName": "text_color", + "affinity": "INTEGER" + }, + { + "fieldPath": "plainText.label", + "columnName": "plainText_label", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.value", + "columnName": "plainText_value", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.type", + "columnName": "plainText_type", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.actionId", + "columnName": "textField_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.label", + "columnName": "textField_label", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.value", + "columnName": "textField_value", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.placeHolder", + "columnName": "textField_placeHolder", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.actionId", + "columnName": "checkBox_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.label", + "columnName": "checkBox_label", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.value", + "columnName": "checkBox_value", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_sectionId", + "unique": false, + "columnNames": [ + "sectionId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId` ON `${TABLE_NAME}` (`sectionId`)" + }, + { + "name": "index_DashboardElementEntity_sectionId_elementOrder", + "unique": true, + "columnNames": [ + "sectionId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId_elementOrder` ON `${TABLE_NAME}` (`sectionId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardSectionEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "sectionId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `url`, `time`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "url", + "time" + ] + } + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`))", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ] + }, + { + "tableName": "network_filter", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + } + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`))", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`parentDeviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`parentDeviceId`, `packageName`), FOREIGN KEY(`parentDeviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "parentDeviceId", + "columnName": "parentDeviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentDeviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "parentDeviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + }, + { + "tableName": "DeviceAppIconEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `appPackageName` TEXT NOT NULL, `iconEncoded` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `appPackageName`), FOREIGN KEY(`deviceId`, `appPackageName`) REFERENCES `DeviceAppEntity`(`parentDeviceId`, `packageName`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "appPackageName", + "columnName": "appPackageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "appPackageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceAppEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "deviceId", + "appPackageName" + ], + "referencedColumns": [ + "parentDeviceId", + "packageName" + ] + } + ] + } + ], + "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, '3212e7b0c9d5ed1ffee43064f73307dc')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/47.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/47.json new file mode 100644 index 00000000..94abe1fb --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/47.json @@ -0,0 +1,1149 @@ +{ + "formatVersion": 1, + "database": { + "version": 47, + "identityHash": "9b5790bee49d7d85b888b5f721f8d6ea", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `type` TEXT NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `response_durationMs` REAL, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseError` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`))", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`))", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardSectionEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `sectionOrder` INTEGER NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sectionOrder", + "columnName": "sectionOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardSectionEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardSectionEntity_dashboardId_sectionOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "sectionOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId_sectionOrder` ON `${TABLE_NAME}` (`dashboardId`, `sectionOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sectionId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `button_text` TEXT, `button_actionId` TEXT, `text_label` TEXT, `text_value` TEXT, `text_color` INTEGER, `plainText_label` TEXT, `plainText_value` TEXT, `plainText_type` TEXT, `textField_actionId` TEXT, `textField_label` TEXT, `textField_value` TEXT, `textField_placeHolder` TEXT, `checkBox_actionId` TEXT, `checkBox_label` TEXT, `checkBox_value` INTEGER, FOREIGN KEY(`sectionId`) REFERENCES `DashboardSectionEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sectionId", + "columnName": "sectionId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "button.text", + "columnName": "button_text", + "affinity": "TEXT" + }, + { + "fieldPath": "button.actionId", + "columnName": "button_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "text.label", + "columnName": "text_label", + "affinity": "TEXT" + }, + { + "fieldPath": "text.value", + "columnName": "text_value", + "affinity": "TEXT" + }, + { + "fieldPath": "text.color", + "columnName": "text_color", + "affinity": "INTEGER" + }, + { + "fieldPath": "plainText.label", + "columnName": "plainText_label", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.value", + "columnName": "plainText_value", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.type", + "columnName": "plainText_type", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.actionId", + "columnName": "textField_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.label", + "columnName": "textField_label", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.value", + "columnName": "textField_value", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.placeHolder", + "columnName": "textField_placeHolder", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.actionId", + "columnName": "checkBox_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.label", + "columnName": "checkBox_label", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.value", + "columnName": "checkBox_value", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_sectionId", + "unique": false, + "columnNames": [ + "sectionId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId` ON `${TABLE_NAME}` (`sectionId`)" + }, + { + "name": "index_DashboardElementEntity_sectionId_elementOrder", + "unique": true, + "columnNames": [ + "sectionId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId_elementOrder` ON `${TABLE_NAME}` (`sectionId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardSectionEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "sectionId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `url`, `time`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "url", + "time" + ] + } + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`))", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ] + }, + { + "tableName": "network_filter", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + } + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`))", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`parentDeviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `iconEncoded` TEXT, PRIMARY KEY(`parentDeviceId`, `packageName`), FOREIGN KEY(`parentDeviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "parentDeviceId", + "columnName": "parentDeviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentDeviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "parentDeviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + } + ], + "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, '9b5790bee49d7d85b888b5f721f8d6ea')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/48.json b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/48.json new file mode 100644 index 00000000..b5c6dd41 --- /dev/null +++ b/FloconDesktop/composeApp/schemas/io.github.openflocon.flocondesktop.common.db.AppDatabase/48.json @@ -0,0 +1,1149 @@ +{ + "formatVersion": 1, + "database": { + "version": 48, + "identityHash": "9b5790bee49d7d85b888b5f721f8d6ea", + "entities": [ + { + "tableName": "FloconNetworkCallEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`callId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `type` TEXT NOT NULL, `request_url` TEXT NOT NULL, `request_method` TEXT NOT NULL, `request_startTime` INTEGER NOT NULL, `request_requestHeaders` TEXT NOT NULL, `request_requestBody` TEXT, `request_requestByteSize` INTEGER NOT NULL, `request_isMocked` INTEGER NOT NULL, `request_graphql_query` TEXT, `request_graphql_operationType` TEXT, `response_durationMs` REAL, `response_responseContentType` TEXT, `response_responseBody` TEXT, `response_responseHeaders` TEXT, `response_responseByteSize` INTEGER, `response_responseError` TEXT, `response_graphql_isSuccess` INTEGER, `response_graphql_responseHttpCode` INTEGER, `response_http_responseHttpCode` INTEGER, `response_grpc_responseStatus` TEXT, PRIMARY KEY(`callId`))", + "fields": [ + { + "fieldPath": "callId", + "columnName": "callId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.url", + "columnName": "request_url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.method", + "columnName": "request_method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.startTime", + "columnName": "request_startTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.requestHeaders", + "columnName": "request_requestHeaders", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "request.requestBody", + "columnName": "request_requestBody", + "affinity": "TEXT" + }, + { + "fieldPath": "request.requestByteSize", + "columnName": "request_requestByteSize", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.isMocked", + "columnName": "request_isMocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "request.graphql.query", + "columnName": "request_graphql_query", + "affinity": "TEXT" + }, + { + "fieldPath": "request.graphql.operationType", + "columnName": "request_graphql_operationType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.durationMs", + "columnName": "response_durationMs", + "affinity": "REAL" + }, + { + "fieldPath": "response.responseContentType", + "columnName": "response_responseContentType", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseBody", + "columnName": "response_responseBody", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseHeaders", + "columnName": "response_responseHeaders", + "affinity": "TEXT" + }, + { + "fieldPath": "response.responseByteSize", + "columnName": "response_responseByteSize", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.responseError", + "columnName": "response_responseError", + "affinity": "TEXT" + }, + { + "fieldPath": "response.graphql.isSuccess", + "columnName": "response_graphql_isSuccess", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.graphql.responseHttpCode", + "columnName": "response_graphql_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.http.responseHttpCode", + "columnName": "response_http_responseHttpCode", + "affinity": "INTEGER" + }, + { + "fieldPath": "response.grpc.responseStatus", + "columnName": "response_grpc_responseStatus", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "callId" + ] + }, + "indices": [ + { + "name": "index_FloconNetworkCallEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FloconNetworkCallEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "FileEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL, `isDirectory` INTEGER NOT NULL, `path` TEXT NOT NULL, `parentPath` TEXT NOT NULL, `size` INTEGER NOT NULL, `lastModifiedTimestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirectory", + "columnName": "isDirectory", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "path", + "columnName": "path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentPath", + "columnName": "parentPath", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "lastModifiedTimestamp", + "columnName": "lastModifiedTimestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_FileEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_FileEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`dashboardId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, PRIMARY KEY(`dashboardId`))", + "fields": [ + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "dashboardId" + ] + }, + "indices": [ + { + "name": "index_DashboardEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DashboardSectionEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `dashboardId` TEXT NOT NULL, `sectionOrder` INTEGER NOT NULL, `name` TEXT NOT NULL, FOREIGN KEY(`dashboardId`) REFERENCES `DashboardEntity`(`dashboardId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "dashboardId", + "columnName": "dashboardId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "sectionOrder", + "columnName": "sectionOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardSectionEntity_dashboardId", + "unique": false, + "columnNames": [ + "dashboardId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId` ON `${TABLE_NAME}` (`dashboardId`)" + }, + { + "name": "index_DashboardSectionEntity_dashboardId_sectionOrder", + "unique": true, + "columnNames": [ + "dashboardId", + "sectionOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardSectionEntity_dashboardId_sectionOrder` ON `${TABLE_NAME}` (`dashboardId`, `sectionOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "dashboardId" + ], + "referencedColumns": [ + "dashboardId" + ] + } + ] + }, + { + "tableName": "DashboardElementEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `sectionId` INTEGER NOT NULL, `elementOrder` INTEGER NOT NULL, `button_text` TEXT, `button_actionId` TEXT, `text_label` TEXT, `text_value` TEXT, `text_color` INTEGER, `plainText_label` TEXT, `plainText_value` TEXT, `plainText_type` TEXT, `textField_actionId` TEXT, `textField_label` TEXT, `textField_value` TEXT, `textField_placeHolder` TEXT, `checkBox_actionId` TEXT, `checkBox_label` TEXT, `checkBox_value` INTEGER, FOREIGN KEY(`sectionId`) REFERENCES `DashboardSectionEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "sectionId", + "columnName": "sectionId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "elementOrder", + "columnName": "elementOrder", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "button.text", + "columnName": "button_text", + "affinity": "TEXT" + }, + { + "fieldPath": "button.actionId", + "columnName": "button_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "text.label", + "columnName": "text_label", + "affinity": "TEXT" + }, + { + "fieldPath": "text.value", + "columnName": "text_value", + "affinity": "TEXT" + }, + { + "fieldPath": "text.color", + "columnName": "text_color", + "affinity": "INTEGER" + }, + { + "fieldPath": "plainText.label", + "columnName": "plainText_label", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.value", + "columnName": "plainText_value", + "affinity": "TEXT" + }, + { + "fieldPath": "plainText.type", + "columnName": "plainText_type", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.actionId", + "columnName": "textField_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.label", + "columnName": "textField_label", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.value", + "columnName": "textField_value", + "affinity": "TEXT" + }, + { + "fieldPath": "textField.placeHolder", + "columnName": "textField_placeHolder", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.actionId", + "columnName": "checkBox_actionId", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.label", + "columnName": "checkBox_label", + "affinity": "TEXT" + }, + { + "fieldPath": "checkBox.value", + "columnName": "checkBox_value", + "affinity": "INTEGER" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DashboardElementEntity_sectionId", + "unique": false, + "columnNames": [ + "sectionId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId` ON `${TABLE_NAME}` (`sectionId`)" + }, + { + "name": "index_DashboardElementEntity_sectionId_elementOrder", + "unique": true, + "columnNames": [ + "sectionId", + "elementOrder" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DashboardElementEntity_sectionId_elementOrder` ON `${TABLE_NAME}` (`sectionId`, `elementOrder`)" + } + ], + "foreignKeys": [ + { + "table": "DashboardSectionEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "sectionId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "TableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `name` TEXT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_TableEntity_deviceId_packageName_name", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_TableEntity_deviceId_packageName_name` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `name`)" + } + ] + }, + { + "tableName": "TableItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `tableId` INTEGER NOT NULL, `createdAt` INTEGER NOT NULL, `columnsNames` TEXT NOT NULL, `values` TEXT NOT NULL, PRIMARY KEY(`itemId`), FOREIGN KEY(`tableId`) REFERENCES `TableEntity`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "tableId", + "columnName": "tableId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "columnsNames", + "columnName": "columnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "values", + "columnName": "values", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_TableItemEntity_tableId", + "unique": false, + "columnNames": [ + "tableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_TableItemEntity_tableId` ON `${TABLE_NAME}` (`tableId`)" + } + ], + "foreignKeys": [ + { + "table": "TableEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "tableId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "DeviceImageEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `url` TEXT NOT NULL, `time` INTEGER NOT NULL, PRIMARY KEY(`deviceId`, `url`, `time`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "time", + "columnName": "time", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "url", + "time" + ] + } + }, + { + "tableName": "SuccessQueryEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `databaseId` TEXT NOT NULL, `queryString` TEXT NOT NULL, `timestamp` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "databaseId", + "columnName": "databaseId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "queryString", + "columnName": "queryString", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "timestamp", + "columnName": "timestamp", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString", + "unique": true, + "columnNames": [ + "deviceId", + "packageName", + "databaseId", + "queryString" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_SuccessQueryEntity_deviceId_packageName_databaseId_queryString` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `databaseId`, `queryString`)" + }, + { + "name": "index_SuccessQueryEntity_databaseId", + "unique": false, + "columnNames": [ + "databaseId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_SuccessQueryEntity_databaseId` ON `${TABLE_NAME}` (`databaseId`)" + } + ] + }, + { + "tableName": "DeeplinkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `link` TEXT NOT NULL, `label` TEXT, `description` TEXT)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "link", + "columnName": "link", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "label", + "columnName": "label", + "affinity": "TEXT" + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_DeeplinkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + }, + { + "name": "index_DeeplinkEntity_deviceId_link", + "unique": true, + "columnNames": [ + "deviceId", + "link" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_DeeplinkEntity_deviceId_link` ON `${TABLE_NAME}` (`deviceId`, `link`)" + } + ] + }, + { + "tableName": "AnalyticsItemEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`itemId` TEXT NOT NULL, `analyticsTableId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `eventName` TEXT NOT NULL, `propertiesColumnsNames` TEXT NOT NULL, `propertiesValues` TEXT NOT NULL, PRIMARY KEY(`itemId`))", + "fields": [ + { + "fieldPath": "itemId", + "columnName": "itemId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "analyticsTableId", + "columnName": "analyticsTableId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "eventName", + "columnName": "eventName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesColumnsNames", + "columnName": "propertiesColumnsNames", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "propertiesValues", + "columnName": "propertiesValues", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "itemId" + ] + }, + "indices": [ + { + "name": "index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId", + "unique": false, + "columnNames": [ + "deviceId", + "packageName", + "analyticsTableId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_AnalyticsItemEntity_deviceId_packageName_analyticsTableId` ON `${TABLE_NAME}` (`deviceId`, `packageName`, `analyticsTableId`)" + } + ] + }, + { + "tableName": "network_filter", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `columnName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `itemsAsJson` TEXT NOT NULL, PRIMARY KEY(`deviceId`, `columnName`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "columnName", + "columnName": "columnName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "itemsAsJson", + "columnName": "itemsAsJson", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId", + "columnName" + ] + } + }, + { + "tableName": "MockNetworkEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`mockId` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `response` TEXT NOT NULL, `expectation_urlPattern` TEXT NOT NULL, `expectation_method` TEXT NOT NULL, PRIMARY KEY(`mockId`))", + "fields": [ + { + "fieldPath": "mockId", + "columnName": "mockId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "response", + "columnName": "response", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.urlPattern", + "columnName": "expectation_urlPattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "expectation.method", + "columnName": "expectation_method", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "mockId" + ] + }, + "indices": [ + { + "name": "index_MockNetworkEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_MockNetworkEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DeviceWithSerialEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `serial` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "serial", + "columnName": "serial", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "BadQualityConfigEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `name` TEXT NOT NULL, `deviceId` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, `packageName` TEXT NOT NULL, `isEnabled` INTEGER NOT NULL, `errorProbability` REAL NOT NULL, `errors` TEXT NOT NULL, `triggerProbability` REAL NOT NULL, `minLatencyMs` INTEGER NOT NULL, `maxLatencyMs` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isEnabled", + "columnName": "isEnabled", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "errorProbability", + "columnName": "errorProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "errors", + "columnName": "errors", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "latency.triggerProbability", + "columnName": "triggerProbability", + "affinity": "REAL", + "notNull": true + }, + { + "fieldPath": "latency.minLatencyMs", + "columnName": "minLatencyMs", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "latency.maxLatencyMs", + "columnName": "maxLatencyMs", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_BadQualityConfigEntity_deviceId_packageName", + "unique": false, + "columnNames": [ + "deviceId", + "packageName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_BadQualityConfigEntity_deviceId_packageName` ON `${TABLE_NAME}` (`deviceId`, `packageName`)" + } + ] + }, + { + "tableName": "DeviceEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`deviceId` TEXT NOT NULL, `deviceName` TEXT NOT NULL, PRIMARY KEY(`deviceId`))", + "fields": [ + { + "fieldPath": "deviceId", + "columnName": "deviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "deviceName", + "columnName": "deviceName", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "deviceId" + ] + } + }, + { + "tableName": "DeviceAppEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`parentDeviceId` TEXT NOT NULL, `name` TEXT NOT NULL, `packageName` TEXT NOT NULL, `iconEncoded` TEXT, PRIMARY KEY(`parentDeviceId`, `packageName`), FOREIGN KEY(`parentDeviceId`) REFERENCES `DeviceEntity`(`deviceId`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "parentDeviceId", + "columnName": "parentDeviceId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "packageName", + "columnName": "packageName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "iconEncoded", + "columnName": "iconEncoded", + "affinity": "TEXT" + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentDeviceId", + "packageName" + ] + }, + "foreignKeys": [ + { + "table": "DeviceEntity", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "parentDeviceId" + ], + "referencedColumns": [ + "deviceId" + ] + } + ] + } + ], + "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, '9b5790bee49d7d85b888b5f721f8d6ea')" + ] + } +} \ No newline at end of file diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/Fakes.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/Fakes.kt deleted file mode 100644 index f00b7d79..00000000 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/Fakes.kt +++ /dev/null @@ -1,9 +0,0 @@ -package io.github.openflocon.flocondesktop.common - -object Fakes { - val Enabled = false - - val FakeDeviceId = "deviceId" - - val FakePackageName = "packageName" -} diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt index 2ace8e97..b3632713 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/AppDatabase.kt @@ -16,6 +16,9 @@ import io.github.openflocon.data.local.database.dao.QueryDao import io.github.openflocon.data.local.database.models.SuccessQueryEntity import io.github.openflocon.data.local.deeplink.dao.FloconDeeplinkDao import io.github.openflocon.data.local.deeplink.models.DeeplinkEntity +import io.github.openflocon.data.local.device.datasource.dao.DevicesDao +import io.github.openflocon.data.local.device.datasource.model.DeviceAppEntity +import io.github.openflocon.data.local.device.datasource.model.DeviceEntity import io.github.openflocon.data.local.files.dao.FloconFileDao import io.github.openflocon.data.local.files.models.FileEntity import io.github.openflocon.data.local.images.dao.FloconImageDao @@ -36,7 +39,7 @@ import io.github.openflocon.flocondesktop.common.db.converters.MapStringsConvert import kotlinx.coroutines.Dispatchers @Database( - version = 44, + version = 48, entities = [ FloconNetworkCallEntity::class, FileEntity::class, @@ -53,6 +56,8 @@ import kotlinx.coroutines.Dispatchers MockNetworkEntity::class, DeviceWithSerialEntity::class, BadQualityConfigEntity::class, + DeviceEntity::class, + DeviceAppEntity::class, ], ) @TypeConverters( @@ -72,6 +77,7 @@ abstract class AppDatabase : RoomDatabase() { abstract val networkMocksDao: NetworkMocksDao abstract val adbDevicesDao: AdbDevicesDao abstract val networkBadQualityConfigDao: NetworkBadQualityConfigDao + abstract val devicesDao: DevicesDao } fun getRoomDatabase(): AppDatabase = getDatabaseBuilder() diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/RoomModule.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/RoomModule.kt index 8ea6cea0..14d8a96b 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/RoomModule.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/common/db/RoomModule.kt @@ -43,4 +43,7 @@ val roomModule = single { get().networkBadQualityConfigDao } + single { + get().devicesDao + } } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/core/data/device/DevicesRepositoryImpl.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/core/data/device/DevicesRepositoryImpl.kt index fee1a2f6..8bce917e 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/core/data/device/DevicesRepositoryImpl.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/core/data/device/DevicesRepositoryImpl.kt @@ -1,20 +1,23 @@ package io.github.openflocon.flocondesktop.core.data.device +import io.github.openflocon.data.core.device.datasource.local.LocalCurrentDeviceDataSource +import io.github.openflocon.data.core.device.datasource.local.LocalDevicesDataSource +import io.github.openflocon.data.core.device.datasource.local.model.InsertResult import io.github.openflocon.data.core.device.datasource.remote.RemoteDeviceDataSource import io.github.openflocon.domain.Protocol import io.github.openflocon.domain.adb.repository.AdbRepository import io.github.openflocon.domain.common.DispatcherProvider import io.github.openflocon.domain.device.models.DeviceAppDomainModel import io.github.openflocon.domain.device.models.DeviceDomainModel +import io.github.openflocon.domain.device.models.DeviceId import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel +import io.github.openflocon.domain.device.models.HandleDeviceResultDomainModel +import io.github.openflocon.domain.device.models.RegisterDeviceWithAppDomainModel import io.github.openflocon.domain.device.repository.DevicesRepository import io.github.openflocon.domain.messages.models.FloconIncomingMessageDomainModel import io.github.openflocon.domain.messages.repository.MessagesReceiverRepository -import io.github.openflocon.flocondesktop.common.Fakes import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext @@ -23,100 +26,154 @@ class DevicesRepositoryImpl( private val dispatcherProvider: DispatcherProvider, private val adbRepository: AdbRepository, private val remoteDeviceDataSource: RemoteDeviceDataSource, + private val localDevicesDataSource: LocalDevicesDataSource, + private val localCurrentDeviceDataSource: LocalCurrentDeviceDataSource, ) : DevicesRepository, MessagesReceiverRepository { - private val _devices = MutableStateFlow(defaultDevicesValue()) - override val devices = _devices.asStateFlow() - - private val _currentDevice = MutableStateFlow(defaultCurrentDeviceValue()) - override val currentDevice = _currentDevice.asStateFlow() - - private val _currentDeviceApp = MutableStateFlow(null) - override val currentDeviceApp: Flow = _currentDeviceApp.asStateFlow() - - override fun getCurrentDevice(): DeviceDomainModel? = _currentDevice.value - - override fun getCurrentDeviceApp(): DeviceAppDomainModel? = _currentDeviceApp.value private val devicesMutex = Mutex() - // returns new if new device - override suspend fun register(device: DeviceDomainModel): Boolean = withContext(dispatcherProvider.data) { - devicesMutex.withLock { - val existingDevice = _devices.value.find { it.deviceId == device.deviceId } - val isNewDevice = existingDevice == null + override val devices = localDevicesDataSource.devices + .flowOn(dispatcherProvider.data) - _devices.update { currentDevices -> - val updatedDevice = device.copy( - apps = device.apps.plus(existingDevice?.apps.orEmpty()) - .distinctBy(DeviceAppDomainModel::packageName), - ) + override val currentDeviceId: Flow = localCurrentDeviceDataSource.currentDeviceId - if (_currentDevice.value?.deviceId == device.deviceId) { - _currentDevice.update { updatedDevice } - } + override suspend fun getCurrentDeviceId(): DeviceId? = + localCurrentDeviceDataSource.getCurrentDeviceId() - val newDevicesList = if (isNewDevice) { - currentDevices + updatedDevice - } else { - currentDevices.map { - if (it.deviceId == device.deviceId) updatedDevice else it - } - } - newDevicesList.distinct() - } - - isNewDevice + override suspend fun getCurrentDevice(): DeviceDomainModel? = + localCurrentDeviceDataSource.getCurrentDeviceId()?.let { + localDevicesDataSource.getDeviceById(it) } - } - override suspend fun unregister(device: DeviceDomainModel) { + // returns new if new device + override suspend fun register(registerDeviceWithApp: RegisterDeviceWithAppDomainModel): HandleDeviceResultDomainModel = withContext(dispatcherProvider.data) { devicesMutex.withLock { - _devices.update { it - device } + val isKnownDevice = localCurrentDeviceDataSource.isKnownDeviceForThisSession( + registerDeviceWithApp.device.deviceId + ) + val isKnownApp = localCurrentDeviceDataSource.isKnownAppForThisSession( + deviceId = registerDeviceWithApp.device.deviceId, + packageName = registerDeviceWithApp.app.packageName + ) + if (isKnownDevice && isKnownApp) { + HandleDeviceResultDomainModel( + deviceId = registerDeviceWithApp.device.deviceId, + justConnectedForThisSession = false, + isNewDevice = false, + isNewApp = false, + ) + } else { + val isNewDevice = when (localDevicesDataSource.insertDevice( + registerDeviceWithApp.device + )) { + InsertResult.New -> true + InsertResult.Exists -> false + } + localCurrentDeviceDataSource.addNewDeviceConnectedForThisSession( + registerDeviceWithApp.device.deviceId + ) + + val isNewApp = when(localDevicesDataSource.insertDeviceApp( + deviceId = registerDeviceWithApp.device.deviceId, + app = registerDeviceWithApp.app, + )) { + InsertResult.New -> true + InsertResult.Exists -> false + } + + localCurrentDeviceDataSource.addNewDeviceAppConnectedForThisSession( + deviceId = registerDeviceWithApp.device.deviceId, + packageName = registerDeviceWithApp.app.packageName + ) + + HandleDeviceResultDomainModel( + deviceId = registerDeviceWithApp.device.deviceId, + justConnectedForThisSession = true, + isNewDevice = isNewDevice, + isNewApp = isNewApp, + ) + } } } - } override suspend fun clear() { withContext(dispatcherProvider.data) { devicesMutex.withLock { - _devices.update { emptyList() } + localDevicesDataSource.clear() } - _currentDevice.update { null } - _currentDeviceApp.update { null } } } - override suspend fun selectApp(app: DeviceAppDomainModel) { + override suspend fun selectApp(deviceId: DeviceId, app: DeviceAppDomainModel) { withContext(dispatcherProvider.data) { - _currentDeviceApp.update { app } + localCurrentDeviceDataSource.selectApp(deviceId = deviceId, app = app) } } - override suspend fun selectDevice(device: DeviceDomainModel) { + override suspend fun selectDevice(deviceId: DeviceId) { withContext(dispatcherProvider.data) { - _currentDevice.update { device } + localCurrentDeviceDataSource.selectDevice(deviceId) } } - private fun defaultCurrentDeviceValue(): DeviceDomainModel = DeviceDomainModel( - deviceId = Fakes.FakeDeviceId, - deviceName = "deviceName", - apps = listOf( - DeviceAppDomainModel( - name = "name", - packageName = "com.package.name", - ), - ), - ) - - private fun defaultDevicesValue(): List = if (Fakes.Enabled) { - listOf(defaultCurrentDeviceValue()) - } else { - emptyList() + // region apps + override fun observeDeviceApps(deviceId: DeviceId): Flow> { + return localDevicesDataSource.observeDeviceApps(deviceId) + .flowOn(dispatcherProvider.data) } + override suspend fun getDeviceSelectedApp(deviceId: DeviceId): DeviceAppDomainModel? { + return withContext(dispatcherProvider.data) { + localCurrentDeviceDataSource.getDeviceSelectedApp(deviceId) + } + } + + override fun observeDeviceSelectedApp(deviceId: DeviceId): Flow { + return localCurrentDeviceDataSource.observeDeviceSelectedApp(deviceId) + .flowOn(dispatcherProvider.data) + } + + override suspend fun getDeviceAppByPackage( + deviceId: DeviceId, + appPackageName: String + ): DeviceAppDomainModel? { + return withContext(dispatcherProvider.data) { + localDevicesDataSource.getDeviceAppByPackage(deviceId, appPackageName) + } + } + + override suspend fun saveAppIcon( + deviceId: DeviceId, + appPackageName: String, + iconEncoded: String + ) { + return localDevicesDataSource.saveAppIcon( + deviceId = deviceId, + appPackageName = appPackageName, + iconEncoded = iconEncoded + ) + } + + override suspend fun hasAppIcon( + deviceId: DeviceId, + appPackageName: String + ): Boolean { + return localDevicesDataSource.hasAppIcon( + deviceId = deviceId, + appPackageName = appPackageName + ) + } + + override suspend fun askForDeviceAppIcon(deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel) { + withContext(dispatcherProvider.data) { + remoteDeviceDataSource.askForDeviceAppIcon(deviceIdAndPackageName) + } + } + + // endregion + override val pluginName = listOf(Protocol.FromDevice.Device.Plugin) override suspend fun onMessageReceived( @@ -132,6 +189,13 @@ class DevicesRepositoryImpl( ) } } + Protocol.FromDevice.Device.Method.AppIcon -> { + localDevicesDataSource.saveAppIcon( + deviceId = message.deviceId, + appPackageName = message.appPackageName, + iconEncoded = message.body, // here we receive the image directly as base64 + ) + } } } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/delegate/DatabaseSelectorDelegate.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/delegate/DatabaseSelectorDelegate.kt index c8baf9f5..3a37d68e 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/delegate/DatabaseSelectorDelegate.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/database/delegate/DatabaseSelectorDelegate.kt @@ -6,7 +6,7 @@ import io.github.openflocon.domain.database.usecase.AskForDeviceDatabasesUseCase import io.github.openflocon.domain.database.usecase.ObserveCurrentDeviceSelectedDatabaseUseCase import io.github.openflocon.domain.database.usecase.ObserveDeviceDatabaseUseCase import io.github.openflocon.domain.database.usecase.SelectCurrentDeviceDatabaseUseCase -import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceUseCase +import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdUseCase import io.github.openflocon.flocondesktop.common.coroutines.closeable.CloseableDelegate import io.github.openflocon.flocondesktop.common.coroutines.closeable.CloseableScoped import io.github.openflocon.flocondesktop.features.database.model.DatabasesStateUiModel @@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch class DatabaseSelectorDelegate( - private val observeCurrentDeviceUseCase: ObserveCurrentDeviceUseCase, + private val observeCurrentDeviceIdUseCase: ObserveCurrentDeviceIdUseCase, private val observeDeviceDatabaseUseCase: ObserveDeviceDatabaseUseCase, private val observeCurrentDeviceSelectedDatabaseUseCase: ObserveCurrentDeviceSelectedDatabaseUseCase, private val closeableDelegate: CloseableDelegate, @@ -66,7 +66,7 @@ class DatabaseSelectorDelegate( askForDeviceDatabasesJob = coroutineScope.launch(dispatcherProvider.viewModel) { // if we change the device, we should ask again - observeCurrentDeviceUseCase() + observeCurrentDeviceIdUseCase() .distinctUntilChanged() .collect { askForDeviceDatabasesUseCase() diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/list/view/NetworkScreen.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/list/view/NetworkScreen.kt index bde0f84e..8e197bca 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/list/view/NetworkScreen.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/network/list/view/NetworkScreen.kt @@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.foundation.rememberScrollbarAdapter import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -44,6 +43,7 @@ import io.github.openflocon.library.designsystem.FloconTheme import io.github.openflocon.library.designsystem.components.FloconPanel import io.github.openflocon.library.designsystem.components.FloconSurface import io.github.openflocon.library.designsystem.components.FloconVerticalScrollbar +import io.github.openflocon.library.designsystem.components.rememberFloconScrollbarAdapter import org.jetbrains.compose.ui.tooling.preview.Preview import org.koin.compose.viewmodel.koinViewModel @@ -66,7 +66,7 @@ fun NetworkScreen( modifier: Modifier = Modifier, ) { val lazyListState = rememberLazyListState() - val scrollAdapter = rememberScrollbarAdapter(lazyListState) + val scrollAdapter = rememberFloconScrollbarAdapter(lazyListState) val columnWidths: NetworkItemColumnWidths = remember { NetworkItemColumnWidths() } // Default widths provided diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/sharedpreferences/delegate/SharedPrefSelectorDelegate.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/sharedpreferences/delegate/SharedPrefSelectorDelegate.kt index 68a3faec..c565b124 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/sharedpreferences/delegate/SharedPrefSelectorDelegate.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/features/sharedpreferences/delegate/SharedPrefSelectorDelegate.kt @@ -1,7 +1,7 @@ package io.github.openflocon.flocondesktop.features.sharedpreferences.delegate import io.github.openflocon.domain.common.DispatcherProvider -import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceUseCase +import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdUseCase import io.github.openflocon.domain.sharedpreference.models.DeviceSharedPreferenceDomainModel import io.github.openflocon.domain.sharedpreference.usecase.AskForDeviceSharedPreferencesUseCase import io.github.openflocon.domain.sharedpreference.usecase.GetCurrentDeviceSharedPreferenceValuesUseCase @@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch class SharedPrefSelectorDelegate( - private val observeCurrentDeviceUseCase: ObserveCurrentDeviceUseCase, + private val observeCurrentDeviceIdUseCase: ObserveCurrentDeviceIdUseCase, private val observeDeviceSharedPrefUseCase: ObserveDeviceSharedPreferencesUseCase, private val observeCurrentDeviceSelectedSharedPrefUseCase: ObserveCurrentDeviceSelectedSharedPreferenceUseCase, private val closeableDelegate: CloseableDelegate, @@ -76,7 +76,7 @@ class SharedPrefSelectorDelegate( askForSharedPrefsJob = coroutineScope.launch(dispatcherProvider.viewModel) { // if we change the device, we should ask again - observeCurrentDeviceUseCase() + observeCurrentDeviceIdUseCase() .distinctUntilChanged() .collect { askForDeviceSharedPrefsUseCase() diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/MainScreen.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/MainScreen.kt index e196bcb4..b4bf798e 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/MainScreen.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/MainScreen.kt @@ -41,6 +41,7 @@ import io.github.openflocon.flocondesktop.features.images.view.ImagesScreen import io.github.openflocon.flocondesktop.features.network.list.view.NetworkScreen import io.github.openflocon.flocondesktop.features.sharedpreferences.view.SharedPreferencesScreen import io.github.openflocon.flocondesktop.features.table.view.TableScreen +import io.github.openflocon.flocondesktop.main.ui.model.AppsStateUiModel import io.github.openflocon.flocondesktop.main.ui.model.DeviceAppUiModel import io.github.openflocon.flocondesktop.main.ui.model.DeviceItemUiModel import io.github.openflocon.flocondesktop.main.ui.model.DevicesStateUiModel @@ -63,12 +64,14 @@ fun MainScreen( val leftPanelState by viewModel.leftPanelState.collectAsStateWithLifecycle() val subScreen by viewModel.subScreen.collectAsStateWithLifecycle() val devicesState by viewModel.devicesState.collectAsStateWithLifecycle() + val appsState by viewModel.appsState.collectAsStateWithLifecycle() Box(modifier = modifier) { MainScreen( subScreen = subScreen, modifier = Modifier.fillMaxSize(), devicesState = devicesState, + appsState = appsState, onDeviceSelected = viewModel::onDeviceSelected, onAppSelected = viewModel::onAppSelected, leftPanelState = leftPanelState, @@ -81,6 +84,7 @@ fun MainScreen( private fun MainScreen( subScreen: SubScreen, devicesState: DevicesStateUiModel, + appsState: AppsStateUiModel, onDeviceSelected: (DeviceItemUiModel) -> Unit, onAppSelected: (DeviceAppUiModel) -> Unit, leftPanelState: LeftPanelState, @@ -112,6 +116,7 @@ private fun MainScreen( onClickItem = onClickLeftPanelItem, state = leftPanelState, devicesState = devicesState, + appsState = appsState, onDeviceSelected = onDeviceSelected, onAppSelected = onAppSelected, ) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/MainViewModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/MainViewModel.kt index 20ee279e..2975a997 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/MainViewModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/MainViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.viewModelScope import io.github.openflocon.domain.common.DispatcherProvider import io.github.openflocon.flocondesktop.app.InitialSetupStateHolder import io.github.openflocon.flocondesktop.main.ui.delegates.DevicesDelegate +import io.github.openflocon.flocondesktop.main.ui.model.AppsStateUiModel import io.github.openflocon.flocondesktop.main.ui.model.DeviceAppUiModel import io.github.openflocon.flocondesktop.main.ui.model.DeviceItemUiModel import io.github.openflocon.flocondesktop.main.ui.model.DevicesStateUiModel @@ -54,6 +55,7 @@ class MainViewModel( ) val devicesState: StateFlow = devicesDelegate.devicesState + val appsState: StateFlow = devicesDelegate.appsState fun onDeviceSelected(device: DeviceItemUiModel) { viewModelScope.launch(dispatcherProvider.viewModel) { diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/delegates/DevicesDelegate.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/delegates/DevicesDelegate.kt index 7f8e0b11..dad48b17 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/delegates/DevicesDelegate.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/delegates/DevicesDelegate.kt @@ -1,16 +1,14 @@ package io.github.openflocon.flocondesktop.main.ui.delegates -import io.github.openflocon.domain.device.models.DeviceAppDomainModel -import io.github.openflocon.domain.device.models.DeviceDomainModel -import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceAppUseCase -import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceUseCase +import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceAppsUseCase +import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdAndPackageNameUseCase +import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdUseCase import io.github.openflocon.domain.device.usecase.ObserveDevicesUseCase import io.github.openflocon.domain.device.usecase.SelectDeviceAppUseCase import io.github.openflocon.domain.device.usecase.SelectDeviceUseCase import io.github.openflocon.flocondesktop.common.coroutines.closeable.CloseableDelegate import io.github.openflocon.flocondesktop.common.coroutines.closeable.CloseableScoped -import io.github.openflocon.flocondesktop.main.ui.model.DeviceAppUiModel -import io.github.openflocon.flocondesktop.main.ui.model.DeviceItemUiModel +import io.github.openflocon.flocondesktop.main.ui.model.AppsStateUiModel import io.github.openflocon.flocondesktop.main.ui.model.DevicesStateUiModel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow @@ -21,42 +19,69 @@ class DevicesDelegate( private val selectDeviceUseCase: SelectDeviceUseCase, private val selectDeviceAppUseCase: SelectDeviceAppUseCase, observeDevicesUseCase: ObserveDevicesUseCase, - observeCurrentDeviceUseCase: ObserveCurrentDeviceUseCase, - observeCurrentDeviceAppUseCase: ObserveCurrentDeviceAppUseCase, + observeCurrentDeviceIdUseCase: ObserveCurrentDeviceIdUseCase, + observeCurrentDeviceAppsUseCase: ObserveCurrentDeviceAppsUseCase, + observeCurrentDeviceIdAndPackageNameUseCase: ObserveCurrentDeviceIdAndPackageNameUseCase, private val closeableDelegate: CloseableDelegate, ) : CloseableScoped by closeableDelegate { val devicesState: StateFlow = combine( observeDevicesUseCase(), - observeCurrentDeviceUseCase(), - observeCurrentDeviceAppUseCase(), - ) { devices, current, currentApp -> + observeCurrentDeviceIdUseCase(), + ) { devices, currentDeviceId -> if (devices.isEmpty()) { DevicesStateUiModel.Empty } else { - if (current == null || current.deviceId !in devices.map { it.deviceId }) { + val current = devices.firstOrNull { it.deviceId == currentDeviceId } + if (current == null) { val firstDevice = devices.first() select(firstDevice.deviceId) DevicesStateUiModel.WithDevices( devices = mapToUi(devices), - deviceSelected = mapToUi(firstDevice), - appSelected = defaultApp(firstDevice, currentApp), + deviceSelected = firstDevice.mapToUi(), ) } else { DevicesStateUiModel.WithDevices( devices = mapToUi(devices), - deviceSelected = mapToUi(current), - appSelected = defaultApp(current, currentApp), + deviceSelected = current.mapToUi(), ) } } + }.stateIn( + scope = coroutineScope, + SharingStarted.WhileSubscribed(5_000), + DevicesStateUiModel.Loading, + ) + + val appsState: StateFlow = combine( + observeCurrentDeviceAppsUseCase(), + observeCurrentDeviceIdAndPackageNameUseCase(), + ) { apps, currentApp -> + if (apps.isEmpty()) { + AppsStateUiModel.Empty + } else { + val current = apps.firstOrNull { it.packageName == currentApp?.packageName } + if (current == null) { + val firstApp = apps.first() + selectApp(firstApp.packageName) + AppsStateUiModel.WithApps( + apps = mapAppsToUi(apps), + appSelected = null, + ) + } else { + AppsStateUiModel.WithApps( + apps = mapAppsToUi(apps), + appSelected = current.mapToUi(), + ) + } } - .stateIn( - scope = coroutineScope, - SharingStarted.WhileSubscribed(5_000), - DevicesStateUiModel.Loading, - ) + } + .stateIn( + scope = coroutineScope, + SharingStarted.WhileSubscribed(5_000), + AppsStateUiModel.Loading, + ) suspend fun select(deviceId: String) { selectDeviceUseCase(deviceId) @@ -65,31 +90,4 @@ class DevicesDelegate( suspend fun selectApp(packageName: String) { selectDeviceAppUseCase(packageName) } - - private suspend fun defaultApp(device: DeviceDomainModel, currentApp: DeviceAppDomainModel?): DeviceAppUiModel? = if (currentApp == null || currentApp.packageName !in device.apps.map(DeviceAppDomainModel::packageName)) { - device.apps - .firstOrNull() - ?.let { - selectApp(it.packageName) - mapToUi(it) - } - } else { - mapToUi(currentApp) - } - - private fun mapToUi(devices: List): List = devices.map { - mapToUi(it) - } - - private fun mapToUi(device: DeviceDomainModel): DeviceItemUiModel = DeviceItemUiModel( - deviceName = device.deviceName, - id = device.deviceId, - apps = device.apps - .map { mapToUi(it) }, - ) - - private fun mapToUi(app: DeviceAppDomainModel) = DeviceAppUiModel( - name = app.name, - packageName = app.packageName, - ) } diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/delegates/Mapper.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/delegates/Mapper.kt new file mode 100644 index 00000000..35d9fbb9 --- /dev/null +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/delegates/Mapper.kt @@ -0,0 +1,26 @@ +package io.github.openflocon.flocondesktop.main.ui.delegates + +import io.github.openflocon.domain.device.models.DeviceAppDomainModel +import io.github.openflocon.domain.device.models.DeviceDomainModel +import io.github.openflocon.flocondesktop.main.ui.model.DeviceAppUiModel +import io.github.openflocon.flocondesktop.main.ui.model.DeviceItemUiModel + +internal fun mapToUi(devices: List): List = devices.map { + it.mapToUi() +} + +internal fun DeviceDomainModel.mapToUi(): DeviceItemUiModel = DeviceItemUiModel( + deviceName = deviceName, + id = deviceId, +) + +internal fun mapAppsToUi(devices: List): List = devices.map { + it.mapToUi() +} + + +internal fun DeviceAppDomainModel.mapToUi() = DeviceAppUiModel( + name = name, + packageName = packageName, + iconEncoded = iconEncoded, +) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DeviceAppUiModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DeviceAppUiModel.kt index 49e4e025..037c859d 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DeviceAppUiModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DeviceAppUiModel.kt @@ -6,9 +6,11 @@ import androidx.compose.runtime.Immutable data class DeviceAppUiModel( val name: String, val packageName: String, + val iconEncoded: String?, ) fun previewDeviceAppUiModel() = DeviceAppUiModel( name = "App name", packageName = "Package name", + iconEncoded = null, ) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DeviceItemUiModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DeviceItemUiModel.kt index 9dc6dd65..2c1c44de 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DeviceItemUiModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DeviceItemUiModel.kt @@ -6,11 +6,9 @@ import androidx.compose.runtime.Immutable data class DeviceItemUiModel( val id: String, val deviceName: String, - val apps: List, ) fun previewDeviceItemUiModel() = DeviceItemUiModel( deviceName = "deviceName", id = "id", - apps = listOf(previewDeviceAppUiModel()), ) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DevicesStateUiModel.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DevicesStateUiModel.kt index 13082178..167afccc 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DevicesStateUiModel.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/model/DevicesStateUiModel.kt @@ -15,52 +15,48 @@ sealed interface DevicesStateUiModel { data class WithDevices( val devices: List, val deviceSelected: DeviceItemUiModel, - val appSelected: DeviceAppUiModel?, ) : DevicesStateUiModel } +@Immutable +sealed interface AppsStateUiModel { + + val appSelected: DeviceAppUiModel? + + @Immutable + data object Loading : AppsStateUiModel { + override val appSelected: DeviceAppUiModel? = null + } + + @Immutable + data object Empty : AppsStateUiModel { + override val appSelected: DeviceAppUiModel? = null + } + + @Immutable + data class WithApps( + val apps: List, + override val appSelected: DeviceAppUiModel?, + ) : AppsStateUiModel +} + fun previewDevicesStateUiModel(): DevicesStateUiModel = DevicesStateUiModel.WithDevices( devices = listOf( DeviceItemUiModel( deviceName = "deviceName1", id = "id1", - apps = listOf( - DeviceAppUiModel( - name = "appName1", - packageName = "packageName1", - ), - ), ), DeviceItemUiModel( deviceName = "deviceName2", id = "id2", - apps = listOf( - DeviceAppUiModel( - name = "appName2", - packageName = "packageName2", - ), - ), ), DeviceItemUiModel( deviceName = "deviceName", id = "id", - apps = listOf( - DeviceAppUiModel( - name = "appName", - packageName = "packageName", - ), - ), ), ), - appSelected = null, deviceSelected = DeviceItemUiModel( deviceName = "deviceName", id = "id", - apps = listOf( - DeviceAppUiModel( - name = "appName", - packageName = "packageName", - ), - ), ), ) diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/view/DeviceSelectorView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/view/DeviceSelectorView.kt index 7aaf9102..632c0898 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/view/DeviceSelectorView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/view/DeviceSelectorView.kt @@ -47,11 +47,15 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.shadow import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.graphics.toComposeImageBitmap import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastForEach import flocondesktop.composeapp.generated.resources.Res import flocondesktop.composeapp.generated.resources.smartphone +import io.github.openflocon.flocondesktop.main.ui.model.AppsStateUiModel import io.github.openflocon.flocondesktop.main.ui.model.DeviceAppUiModel import io.github.openflocon.flocondesktop.main.ui.model.DeviceItemUiModel import io.github.openflocon.flocondesktop.main.ui.model.DevicesStateUiModel @@ -61,6 +65,8 @@ import io.github.openflocon.library.designsystem.components.FloconCircularProgre import io.github.openflocon.library.designsystem.components.FloconIcon import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.ui.tooling.preview.Preview +import kotlin.io.encoding.Base64 +import org.jetbrains.skia.Image private val CelluleHeight = 64.dp @@ -68,6 +74,7 @@ private val CelluleHeight = 64.dp internal fun ColumnScope.DeviceSelectorView( panelExpanded: Boolean, devicesState: DevicesStateUiModel, + appsState: AppsStateUiModel, onDeviceSelected: (DeviceItemUiModel) -> Unit, onAppSelected: (DeviceAppUiModel) -> Unit, modifier: Modifier = Modifier, @@ -77,7 +84,8 @@ internal fun ColumnScope.DeviceSelectorView( ) { AnimatedVisibility(devicesState is DevicesStateUiModel.WithDevices) { DeviceAppSelector( - state = devicesState, + devicesState = devicesState, + appsState = appsState, panelExpanded = panelExpanded, onAppSelected = onAppSelected, ) @@ -95,7 +103,8 @@ internal fun ColumnScope.DeviceSelectorView( @Composable private fun DeviceAppSelector( - state: DevicesStateUiModel, + devicesState: DevicesStateUiModel, + appsState: AppsStateUiModel, panelExpanded: Boolean, onAppSelected: (DeviceAppUiModel) -> Unit, ) { @@ -106,7 +115,7 @@ private fun DeviceAppSelector( expanded = false } - if (state is DevicesStateUiModel.WithDevices) { + if (devicesState is DevicesStateUiModel.WithDevices) { ExposedDropdownMenuBox( expanded = expanded, onExpandedChange = { expanded = false }, @@ -114,13 +123,13 @@ private fun DeviceAppSelector( ) { val modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryNotEditable) - if (state.appSelected != null) { + appsState.appSelected?.let { DeviceAppName( - deviceApp = state.appSelected, + deviceApp = it, onClick = { expanded = true }, modifier = modifier, ) - } else { + } ?: run { Selector( onClick = { expanded = true }, ) { @@ -130,22 +139,30 @@ private fun DeviceAppSelector( ) } } - ExposedDropdownMenu( - expanded = expanded, - onDismissRequest = { expanded = false }, - modifier = Modifier.exposedDropdownSize(), - ) { - state.deviceSelected - .apps - .forEach { app -> - DeviceAppName( - deviceApp = app, - onClick = { - onAppSelected(app) - expanded = false - }, - ) + + when(appsState) { + AppsStateUiModel.Empty, + AppsStateUiModel.Loading -> { + // no op + } + is AppsStateUiModel.WithApps -> { + ExposedDropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false }, + modifier = Modifier.exposedDropdownSize(), + ) { + appsState.apps + .fastForEach { app -> + DeviceAppName( + deviceApp = app, + onClick = { + onAppSelected(app) + expanded = false + }, + ) + } } + } } } } @@ -364,26 +381,66 @@ private fun DeviceAppName( onClick = onClick, modifier = modifier, ) { - Column( - verticalArrangement = Arrangement.SpaceEvenly, + Row( modifier = Modifier.padding(horizontal = 8.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp), ) { - Text( - text = deviceApp.name, - style = FloconTheme.typography.labelMedium, - maxLines = 1, - overflow = TextOverflow.Ellipsis, - color = FloconTheme.colorPalette.onPanel, - ) - Text( - text = deviceApp.packageName, - style = FloconTheme.typography.bodySmall, - color = FloconTheme.colorPalette.onPanel.copy(alpha = 0.8f), + AppImage( + deviceApp = deviceApp, + modifier = Modifier.size(24.dp), ) + Column( + verticalArrangement = Arrangement.SpaceEvenly, + ) { + Text( + text = deviceApp.name, + style = FloconTheme.typography.labelMedium, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = FloconTheme.colorPalette.onPanel, + ) + Text( + text = deviceApp.packageName, + style = FloconTheme.typography.bodySmall, + color = FloconTheme.colorPalette.onPanel.copy(alpha = 0.8f), + ) + } } } } +@Composable +private fun AppImage( + deviceApp: DeviceAppUiModel, + modifier : Modifier = Modifier +) { + val imageBitmap = remember(deviceApp.iconEncoded) { + deviceApp.iconEncoded?.let { encoded -> + try { + val decodedBytes = Base64.decode(encoded) //, Base64.DEFAULT) + Image.makeFromEncoded(decodedBytes).toComposeImageBitmap() + } catch (e: Exception) { + null + } + } + } + + if (imageBitmap != null) { + Image( + bitmap = imageBitmap, + contentDescription = null, + modifier = modifier, + ) + } else { + // Fallback : affiche une icône par défaut si iconEncoded est null ou invalide + Image( + painter = painterResource(Res.drawable.smartphone), + contentDescription = null, + modifier = modifier, + ) + } +} + @Preview @Composable private fun DeviceViewPreview() { diff --git a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/view/leftpannel/LeftPannelView.kt b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/view/leftpannel/LeftPannelView.kt index afbef0a8..86151226 100644 --- a/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/view/leftpannel/LeftPannelView.kt +++ b/FloconDesktop/composeApp/src/commonMain/kotlin/io/github/openflocon/flocondesktop/main/ui/view/leftpannel/LeftPannelView.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastForEachIndexed import flocondesktop.composeapp.generated.resources.Res import flocondesktop.composeapp.generated.resources.app_icon_small +import io.github.openflocon.flocondesktop.main.ui.model.AppsStateUiModel import io.github.openflocon.flocondesktop.main.ui.model.DeviceAppUiModel import io.github.openflocon.flocondesktop.main.ui.model.DeviceItemUiModel import io.github.openflocon.flocondesktop.main.ui.model.DevicesStateUiModel @@ -59,6 +60,7 @@ fun LeftPanelView( expanded: Boolean, onClickItem: (LeftPanelItem) -> Unit, devicesState: DevicesStateUiModel, + appsState: AppsStateUiModel, onDeviceSelected: (DeviceItemUiModel) -> Unit, onAppSelected: (DeviceAppUiModel) -> Unit, modifier: Modifier = Modifier, @@ -89,6 +91,7 @@ fun LeftPanelView( .fillMaxWidth() .heightIn(min = 64.dp), devicesState = devicesState, + appsState = appsState, onDeviceSelected = onDeviceSelected, onAppSelected = onAppSelected, ) @@ -186,6 +189,7 @@ private fun LeftPanelViewPreview() { onAppSelected = {}, expanded = false, devicesState = previewDevicesStateUiModel(), + appsState = AppsStateUiModel.Empty, ) } } diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/local/LocalCurrentDeviceDataSource.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/local/LocalCurrentDeviceDataSource.kt new file mode 100644 index 00000000..62d0c14c --- /dev/null +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/local/LocalCurrentDeviceDataSource.kt @@ -0,0 +1,24 @@ +package io.github.openflocon.data.core.device.datasource.local + +import io.github.openflocon.domain.device.models.DeviceAppDomainModel +import io.github.openflocon.domain.device.models.DeviceId +import kotlinx.coroutines.flow.Flow + +interface LocalCurrentDeviceDataSource { + val currentDeviceId: Flow + suspend fun getCurrentDeviceId(): DeviceId? + suspend fun selectDevice(deviceId: DeviceId) + + suspend fun addNewDeviceConnectedForThisSession(deviceId: DeviceId) + suspend fun isKnownDeviceForThisSession(deviceId: DeviceId): Boolean + + // region app + fun observeDeviceSelectedApp(deviceId: DeviceId): Flow + suspend fun getDeviceSelectedApp(deviceId: DeviceId): DeviceAppDomainModel? + suspend fun selectApp(deviceId: DeviceId, app: DeviceAppDomainModel) + suspend fun isKnownAppForThisSession(deviceId: DeviceId, packageName: String) : Boolean + suspend fun addNewDeviceAppConnectedForThisSession(deviceId: DeviceId, packageName: String) + // endregion + + suspend fun clear() +} diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/local/LocalDevicesDataSource.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/local/LocalDevicesDataSource.kt new file mode 100644 index 00000000..108df9fe --- /dev/null +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/local/LocalDevicesDataSource.kt @@ -0,0 +1,31 @@ +package io.github.openflocon.data.core.device.datasource.local + +import io.github.openflocon.data.core.device.datasource.local.model.InsertResult +import io.github.openflocon.domain.device.models.DeviceAppDomainModel +import io.github.openflocon.domain.device.models.DeviceDomainModel +import io.github.openflocon.domain.device.models.DeviceId +import kotlinx.coroutines.flow.Flow + + +interface LocalDevicesDataSource { + // region device + val devices: Flow> + fun observeDeviceById(it: DeviceId): Flow + suspend fun getDeviceById(it: DeviceId): DeviceDomainModel? + suspend fun insertDevice(device: DeviceDomainModel) : InsertResult + // endregion + + // region apps + suspend fun insertDeviceApp(deviceId: DeviceId, app: DeviceAppDomainModel) : InsertResult + fun observeDeviceApps(deviceId: DeviceId) : Flow> + suspend fun getDeviceAppByPackage(deviceId: DeviceId, packageName: String) : DeviceAppDomainModel? + // endregion + + // region apps icons + suspend fun saveAppIcon(deviceId: DeviceId, appPackageName: String, iconEncoded: String) + suspend fun hasAppIcon(deviceId: DeviceId, appPackageName: String) : Boolean + // endregion + + suspend fun clear() +} + diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/local/model/InsertResult.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/local/model/InsertResult.kt new file mode 100644 index 00000000..e7b567dc --- /dev/null +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/local/model/InsertResult.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.data.core.device.datasource.local.model + +enum class InsertResult { + New, + Exists, +} diff --git a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/remote/RemoteDeviceDataSource.kt b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/remote/RemoteDeviceDataSource.kt index 354771dd..83d9c659 100644 --- a/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/remote/RemoteDeviceDataSource.kt +++ b/FloconDesktop/data/core/src/commonMain/kotlin/io/github/openflocon/data/core/device/datasource/remote/RemoteDeviceDataSource.kt @@ -1,7 +1,9 @@ package io.github.openflocon.data.core.device.datasource.remote +import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel import io.github.openflocon.domain.messages.models.FloconIncomingMessageDomainModel interface RemoteDeviceDataSource { fun getDeviceSerial(message: FloconIncomingMessageDomainModel) : String? + suspend fun askForDeviceAppIcon(deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel) } diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/DI.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/DI.kt index 90c1eae2..3ad84312 100644 --- a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/DI.kt +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/DI.kt @@ -5,6 +5,7 @@ import io.github.openflocon.data.local.analytics.analyticsModule import io.github.openflocon.data.local.dashboard.dashboardModule import io.github.openflocon.data.local.database.databaseModule import io.github.openflocon.data.local.deeplink.deeplinkModule +import io.github.openflocon.data.local.device.deviceModule import io.github.openflocon.data.local.files.filesModule import io.github.openflocon.data.local.images.imagesModule import io.github.openflocon.data.local.network.networkModule @@ -19,6 +20,7 @@ val dataLocalModule = module { dashboardModule, databaseModule, deeplinkModule, + deviceModule, filesModule, imagesModule, networkModule, diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/DI.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/DI.kt new file mode 100644 index 00000000..c6be6135 --- /dev/null +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/DI.kt @@ -0,0 +1,16 @@ +package io.github.openflocon.data.local.device + +import io.github.openflocon.data.core.device.datasource.local.LocalCurrentDeviceDataSource +import io.github.openflocon.data.core.device.datasource.local.LocalDevicesDataSource +import io.github.openflocon.data.core.images.datasource.ImagesLocalDataSource +import io.github.openflocon.data.local.device.datasource.local.LocalCurrentDeviceDataSourceInMemory +import io.github.openflocon.data.local.device.datasource.local.LocalDevicesDataSourceRoom +import io.github.openflocon.data.local.images.datasource.ImagesLocalDataSourceRoom +import org.koin.core.module.dsl.singleOf +import org.koin.dsl.bind +import org.koin.dsl.module + +internal val deviceModule = module { + singleOf(::LocalCurrentDeviceDataSourceInMemory) bind LocalCurrentDeviceDataSource::class + singleOf(::LocalDevicesDataSourceRoom) bind LocalDevicesDataSource::class +} diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/dao/DevicesDao.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/dao/DevicesDao.kt new file mode 100644 index 00000000..6da36d47 --- /dev/null +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/dao/DevicesDao.kt @@ -0,0 +1,70 @@ +package io.github.openflocon.data.local.device.datasource.dao + +import androidx.room.Dao +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import io.github.openflocon.data.local.device.datasource.model.DeviceAppEntity +import io.github.openflocon.data.local.device.datasource.model.DeviceEntity +import kotlinx.coroutines.flow.Flow + +@Dao +interface DevicesDao { + + @Query("SELECT * FROM DeviceEntity") + fun observeDevices(): Flow> + + @Query("SELECT * FROM DeviceEntity WHERE deviceId = :deviceId") + fun observeDeviceById(deviceId: String): Flow + + @Query("SELECT * FROM DeviceEntity WHERE deviceId = :deviceId") + suspend fun getDeviceById(deviceId: String): DeviceEntity? + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertDevice(device: DeviceEntity) + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun insertDeviceApp(app: DeviceAppEntity) + + @Query("DELETE FROM DeviceEntity") + suspend fun clear() + + @Query( + """ + SELECT * FROM DeviceAppEntity + WHERE parentDeviceId = :deviceId + """ + ) + fun observeDeviceApps(deviceId: String): Flow> + + @Query( + """ + SELECT * FROM DeviceAppEntity + WHERE parentDeviceId = :deviceId + AND packageName = :packageName + """ + ) + suspend fun getDeviceAppByPackageName( + deviceId: String, + packageName: String + ): DeviceAppEntity? + + @Query( + """ + UPDATE DeviceAppEntity + SET iconEncoded = :iconEncoded + WHERE parentDeviceId = :deviceId AND packageName = :packageName + """ + ) + suspend fun updateAppIcon(deviceId: String, packageName: String, iconEncoded: String) + + @Query( + """ + SELECT iconEncoded IS NOT NULL + FROM DeviceAppEntity + WHERE parentDeviceId = :deviceId + AND packageName = :appPackageName + """ + ) + suspend fun hasAppIcon(deviceId: String, appPackageName: String): Boolean +} diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/local/LocalCurrentDeviceDataSourceInMemory.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/local/LocalCurrentDeviceDataSourceInMemory.kt new file mode 100644 index 00000000..f5ec8ab2 --- /dev/null +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/local/LocalCurrentDeviceDataSourceInMemory.kt @@ -0,0 +1,72 @@ +package io.github.openflocon.data.local.device.datasource.local + +import io.github.openflocon.data.core.device.datasource.local.LocalCurrentDeviceDataSource +import io.github.openflocon.domain.device.models.DeviceAppDomainModel +import io.github.openflocon.domain.device.models.DeviceId +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.update + +// keep only in ram +class LocalCurrentDeviceDataSourceInMemory : LocalCurrentDeviceDataSource { + + private val _currentDeviceId = MutableStateFlow(null) + override val currentDeviceId = _currentDeviceId.asStateFlow() + + private val connectedDevicesForSession = MutableStateFlow(emptySet()) + private val connectedDevicesAndAppsForSession = MutableStateFlow(emptySet>()) + private val currentDeviceApp = MutableStateFlow(emptyMap()) + + override suspend fun getCurrentDeviceId(): DeviceId? { + return _currentDeviceId.value + } + + override suspend fun selectDevice(deviceId: DeviceId) { + _currentDeviceId.value = deviceId + } + + override suspend fun selectApp(deviceId: DeviceId, app: DeviceAppDomainModel) { + currentDeviceApp.update { + it + (deviceId to app) + } + } + + override suspend fun addNewDeviceConnectedForThisSession(deviceId: DeviceId) { + connectedDevicesForSession.update { it + deviceId } + } + + override suspend fun isKnownDeviceForThisSession(deviceId: DeviceId): Boolean { + return connectedDevicesForSession.first().contains(deviceId) + } + + override fun observeDeviceSelectedApp(deviceId: DeviceId): Flow { + return currentDeviceApp.map { it[deviceId] } + } + + override suspend fun getDeviceSelectedApp(deviceId: DeviceId): DeviceAppDomainModel? { + return currentDeviceApp.first()[deviceId] + } + + override suspend fun isKnownAppForThisSession( + deviceId: DeviceId, + packageName: String + ): Boolean { + val element = deviceId to packageName + return connectedDevicesAndAppsForSession.first().contains(element) + } + + override suspend fun addNewDeviceAppConnectedForThisSession( + deviceId: DeviceId, + packageName: String + ) { + val element = deviceId to packageName + connectedDevicesAndAppsForSession.update { it + element } + } + + override suspend fun clear() { + _currentDeviceId.update { null } + } +} diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/local/LocalDevicesDataSourceRoom.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/local/LocalDevicesDataSourceRoom.kt new file mode 100644 index 00000000..33d85e4e --- /dev/null +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/local/LocalDevicesDataSourceRoom.kt @@ -0,0 +1,92 @@ +package io.github.openflocon.data.local.device.datasource.local + +import io.github.openflocon.data.core.device.datasource.local.LocalDevicesDataSource +import io.github.openflocon.data.core.device.datasource.local.model.InsertResult +import io.github.openflocon.data.local.device.datasource.dao.DevicesDao +import io.github.openflocon.data.local.device.datasource.mapper.toDomainModel +import io.github.openflocon.data.local.device.datasource.mapper.toEntity +import io.github.openflocon.domain.device.models.DeviceAppDomainModel +import io.github.openflocon.domain.device.models.DeviceDomainModel +import io.github.openflocon.domain.device.models.DeviceId +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map + +class LocalDevicesDataSourceRoom( + private val dao: DevicesDao +) : LocalDevicesDataSource { + + override val devices: Flow> + get() = dao.observeDevices() + .map { it.map { device -> device.toDomainModel() } } + + override fun observeDeviceById(it: DeviceId): Flow { + return dao.observeDeviceById(deviceId = it).map { it?.toDomainModel() } + } + + override suspend fun getDeviceById(it: DeviceId): DeviceDomainModel? { + return dao.getDeviceById(deviceId = it)?.toDomainModel() + } + + override suspend fun insertDevice(device: DeviceDomainModel): InsertResult { + val deviceEntity = dao.getDeviceById(device.deviceId) + if (deviceEntity != null) { + return InsertResult.Exists + } else { + dao.insertDevice(device.toEntity()) + return InsertResult.New + } + } + + override suspend fun insertDeviceApp( + deviceId: DeviceId, + app: DeviceAppDomainModel + ): InsertResult { + val appEntity = + dao.getDeviceAppByPackageName(deviceId = deviceId, packageName = app.packageName) + if (appEntity != null) { + return InsertResult.Exists + } else { + dao.insertDeviceApp( + app.toEntity( + parentDeviceId = deviceId, + ) + ) + return InsertResult.New + } + } + + override fun observeDeviceApps(deviceId: DeviceId): Flow> { + return dao.observeDeviceApps(deviceId = deviceId) + .map { it.map { deviceApp -> deviceApp.toDomainModel() } } + } + + override suspend fun getDeviceAppByPackage( + deviceId: DeviceId, + packageName: String + ): DeviceAppDomainModel? { + return dao.getDeviceAppByPackageName(deviceId, packageName)?.toDomainModel() + } + + override suspend fun saveAppIcon( + deviceId: DeviceId, + appPackageName: String, + iconEncoded: String + ) { + dao.updateAppIcon( + deviceId = deviceId, + packageName = appPackageName, + iconEncoded = iconEncoded + ) + } + + override suspend fun hasAppIcon( + deviceId: DeviceId, + appPackageName: String + ): Boolean { + return dao.hasAppIcon(deviceId, appPackageName) + } + + override suspend fun clear() { + dao.clear() + } +} diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/mapper/Mapper.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/mapper/Mapper.kt new file mode 100644 index 00000000..53ce5401 --- /dev/null +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/mapper/Mapper.kt @@ -0,0 +1,37 @@ +package io.github.openflocon.data.local.device.datasource.mapper + +import io.github.openflocon.data.local.device.datasource.model.DeviceAppEntity +import io.github.openflocon.data.local.device.datasource.model.DeviceEntity +import io.github.openflocon.domain.device.models.DeviceAppDomainModel +import io.github.openflocon.domain.device.models.DeviceDomainModel + +internal fun DeviceAppEntity.toDomainModel(): DeviceAppDomainModel { + return DeviceAppDomainModel( + name = this.name, + packageName = this.packageName, + iconEncoded = this.iconEncoded + ) +} + +internal fun DeviceDomainModel.toEntity(): DeviceEntity { + return DeviceEntity( + deviceId = this.deviceId, + deviceName = this.deviceName + ) +} + +internal fun DeviceEntity.toDomainModel(): DeviceDomainModel { + return DeviceDomainModel( + deviceId = this.deviceId, + deviceName = this.deviceName + ) +} + +internal fun DeviceAppDomainModel.toEntity(parentDeviceId: String): DeviceAppEntity { + return DeviceAppEntity( + parentDeviceId = parentDeviceId, + name = this.name, + packageName = this.packageName, + iconEncoded = this.iconEncoded, + ) +} diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/model/DeviceAppEntity.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/model/DeviceAppEntity.kt new file mode 100644 index 00000000..8991ae5c --- /dev/null +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/model/DeviceAppEntity.kt @@ -0,0 +1,25 @@ +package io.github.openflocon.data.local.device.datasource.model + +import androidx.room.Entity +import androidx.room.ForeignKey + +@Entity( + foreignKeys = [ + ForeignKey( + entity = DeviceEntity::class, + parentColumns = ["deviceId"], + childColumns = ["parentDeviceId"], + onDelete = ForeignKey.Companion.CASCADE + ) + ], + primaryKeys = [ + "parentDeviceId", + "packageName" + ] +) +data class DeviceAppEntity( + val parentDeviceId: String, + val name: String, + val packageName: String, + val iconEncoded: String?, // base64 +) diff --git a/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/model/DeviceEntity.kt b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/model/DeviceEntity.kt new file mode 100644 index 00000000..78d3748d --- /dev/null +++ b/FloconDesktop/data/local/src/commonMain/kotlin/io/github/openflocon/data/local/device/datasource/model/DeviceEntity.kt @@ -0,0 +1,12 @@ +package io.github.openflocon.data.local.device.datasource.model + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity +data class DeviceEntity( + @PrimaryKey + val deviceId: String, + val deviceName: String, +) + diff --git a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/device/datasource/DeviceRemoteDataSourceImpl.kt b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/device/datasource/DeviceRemoteDataSourceImpl.kt index 6aba844b..05a1e7cd 100644 --- a/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/device/datasource/DeviceRemoteDataSourceImpl.kt +++ b/FloconDesktop/data/remote/src/commonMain/kotlin/com/flocon/data/remote/device/datasource/DeviceRemoteDataSourceImpl.kt @@ -1,15 +1,33 @@ package com.flocon.data.remote.device.datasource import com.flocon.data.remote.common.safeDecodeFromString +import com.flocon.data.remote.database.models.DatabaseOutgoingQueryMessage import com.flocon.data.remote.device.model.RegisterDeviceDataModel +import com.flocon.data.remote.models.FloconOutgoingMessageDataModel +import com.flocon.data.remote.models.toRemote +import com.flocon.data.remote.server.Server import io.github.openflocon.data.core.device.datasource.remote.RemoteDeviceDataSource +import io.github.openflocon.domain.Protocol +import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel import io.github.openflocon.domain.messages.models.FloconIncomingMessageDomainModel import kotlinx.serialization.json.Json class DeviceRemoteDataSourceImpl( private val json: Json, + private val server: Server, ) : RemoteDeviceDataSource { override fun getDeviceSerial(message: FloconIncomingMessageDomainModel): String? = json.safeDecodeFromString(message.body) ?.serial + + override suspend fun askForDeviceAppIcon(deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel) { + server.sendMessageToClient( + deviceIdAndPackageName = deviceIdAndPackageName.toRemote(), + message = FloconOutgoingMessageDataModel( + plugin = Protocol.ToDevice.Device.Plugin, + method = Protocol.ToDevice.Device.Method.GetAppIcon, + body = "" + ), + ) + } } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/Protocol.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/Protocol.kt index 8956bc00..cc6ee580 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/Protocol.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/Protocol.kt @@ -25,6 +25,7 @@ object Protocol { object Method { const val RegisterDevice = "registerDevice" + const val AppIcon = "appIcon" } } @@ -117,6 +118,14 @@ object Protocol { } } + object Device { + const val Plugin = "device" + + object Method { + const val GetAppIcon = "getAppIcon" + } + } + object Files { const val Plugin = "files" diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/analytics/usecase/ResetCurrentDeviceSelectedAnalyticsUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/analytics/usecase/ResetCurrentDeviceSelectedAnalyticsUseCase.kt index 7e9ede70..8930a6c2 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/analytics/usecase/ResetCurrentDeviceSelectedAnalyticsUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/analytics/usecase/ResetCurrentDeviceSelectedAnalyticsUseCase.kt @@ -1,26 +1,19 @@ package io.github.openflocon.domain.analytics.usecase -import io.github.openflocon.domain.device.usecase.GetCurrentDeviceAppUseCase -import io.github.openflocon.domain.device.usecase.GetCurrentDeviceIdUseCase import io.github.openflocon.domain.analytics.repository.AnalyticsRepository -import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel +import io.github.openflocon.domain.device.usecase.GetCurrentDeviceIdAndPackageNameUseCase class ResetCurrentDeviceSelectedAnalyticsUseCase( private val analyticsRepository: AnalyticsRepository, - private val getCurrentDeviceIdUseCase: GetCurrentDeviceIdUseCase, - private val getCurrentPackageNameUseCase: GetCurrentDeviceAppUseCase, + private val getCurrentDeviceIdAndPackageNameUseCase: GetCurrentDeviceIdAndPackageNameUseCase, private val getCurrentDeviceSelectedAnalyticsUseCase: GetCurrentDeviceSelectedAnalyticsUseCase, ) { suspend operator fun invoke() { - val deviceId = getCurrentDeviceIdUseCase() ?: return - val packageName = getCurrentPackageNameUseCase()?.packageName ?: return + val deviceAndApp = getCurrentDeviceIdAndPackageNameUseCase() ?: return val analyticsId = getCurrentDeviceSelectedAnalyticsUseCase() ?: return analyticsRepository.deleteAnalytics( - deviceIdAndPackageName = DeviceIdAndPackageNameDomainModel( - deviceId = deviceId, - packageName = packageName, - ), + deviceIdAndPackageName = deviceAndApp, analyticsId = analyticsId, ) } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/SelectCurrentDeviceDatabaseUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/SelectCurrentDeviceDatabaseUseCase.kt index 49852256..a121067a 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/SelectCurrentDeviceDatabaseUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/database/usecase/SelectCurrentDeviceDatabaseUseCase.kt @@ -8,7 +8,7 @@ class SelectCurrentDeviceDatabaseUseCase( private val databaseRepository: DatabaseRepository, private val getCurrentDeviceIdAndPackageNameUseCase: GetCurrentDeviceIdAndPackageNameUseCase, ) { - operator fun invoke(databaseId: DeviceDataBaseId) { + suspend operator fun invoke(databaseId: DeviceDataBaseId) { val current = getCurrentDeviceIdAndPackageNameUseCase() ?: return databaseRepository.selectDeviceDatabase( diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/DI.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/DI.kt index 8e62af67..acefccb5 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/DI.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/DI.kt @@ -1,33 +1,29 @@ package io.github.openflocon.domain.device -import io.github.openflocon.domain.device.usecase.GetCurrentDeviceAppUseCase import io.github.openflocon.domain.device.usecase.GetCurrentDeviceIdAndPackageNameUseCase import io.github.openflocon.domain.device.usecase.GetCurrentDeviceIdUseCase -import io.github.openflocon.domain.device.usecase.GetCurrentDeviceUseCase -import io.github.openflocon.domain.device.usecase.HandleDeviceUseCase -import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceAppUseCase +import io.github.openflocon.domain.device.usecase.HandleDeviceAndAppUseCase +import io.github.openflocon.domain.device.usecase.HandleNewAppUseCase +import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceAppsUseCase import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdAndPackageNameUseCase import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceIdUseCase -import io.github.openflocon.domain.device.usecase.ObserveCurrentDeviceUseCase import io.github.openflocon.domain.device.usecase.ObserveDevicesUseCase import io.github.openflocon.domain.device.usecase.SelectDeviceAppUseCase import io.github.openflocon.domain.device.usecase.SelectDeviceUseCase -import io.github.openflocon.domain.messages.usecase.HandleNewDeviceUseCase +import io.github.openflocon.domain.device.usecase.HandleNewDeviceUseCase import org.koin.core.module.dsl.factoryOf import org.koin.dsl.module internal val deviceModule = module { - factoryOf(::GetCurrentDeviceAppUseCase) factoryOf(::GetCurrentDeviceIdAndPackageNameUseCase) factoryOf(::GetCurrentDeviceIdUseCase) - factoryOf(::GetCurrentDeviceUseCase) - factoryOf(::HandleDeviceUseCase) + factoryOf(::HandleDeviceAndAppUseCase) factoryOf(::HandleNewDeviceUseCase) factoryOf(::ObserveCurrentDeviceIdUseCase) - factoryOf(::ObserveCurrentDeviceAppUseCase) + factoryOf(::ObserveCurrentDeviceAppsUseCase) factoryOf(::ObserveCurrentDeviceIdAndPackageNameUseCase) - factoryOf(::ObserveCurrentDeviceUseCase) factoryOf(::ObserveDevicesUseCase) factoryOf(::SelectDeviceAppUseCase) factoryOf(::SelectDeviceUseCase) + factoryOf(::HandleNewAppUseCase) } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceAppDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceAppDomainModel.kt index 2a4679cc..61911650 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceAppDomainModel.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceAppDomainModel.kt @@ -3,4 +3,5 @@ package io.github.openflocon.domain.device.models data class DeviceAppDomainModel( val name: String, val packageName: String, + val iconEncoded: String?, ) diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceDomainModel.kt index 58da4631..a14fea2b 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceDomainModel.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceDomainModel.kt @@ -3,5 +3,4 @@ package io.github.openflocon.domain.device.models data class DeviceDomainModel( val deviceId: DeviceId, val deviceName: String, - val apps: List, ) diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceWithAppDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceWithAppDomainModel.kt new file mode 100644 index 00000000..9789e677 --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceWithAppDomainModel.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.domain.device.models + +data class DeviceWithAppDomainModel( + val device: DeviceDomainModel, + val app: DeviceAppDomainModel, +) diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceWithAppsDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceWithAppsDomainModel.kt new file mode 100644 index 00000000..37737b0e --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/DeviceWithAppsDomainModel.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.domain.device.models + +data class DeviceWithAppsDomainModel( + val device: DeviceDomainModel, + val apps: List, +) diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/HandleDeviceResultDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/HandleDeviceResultDomainModel.kt index 9dda9625..1f1b91b0 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/HandleDeviceResultDomainModel.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/HandleDeviceResultDomainModel.kt @@ -2,5 +2,7 @@ package io.github.openflocon.domain.device.models data class HandleDeviceResultDomainModel( val deviceId: String, + val justConnectedForThisSession: Boolean, val isNewDevice: Boolean, + val isNewApp: Boolean, ) diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/RegisterDeviceWithAppDomainModel.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/RegisterDeviceWithAppDomainModel.kt new file mode 100644 index 00000000..f3b893ad --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/models/RegisterDeviceWithAppDomainModel.kt @@ -0,0 +1,6 @@ +package io.github.openflocon.domain.device.models + +data class RegisterDeviceWithAppDomainModel( + val device: DeviceDomainModel, + val app: DeviceAppDomainModel, +) diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/repository/DevicesRepository.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/repository/DevicesRepository.kt index 7b96a18b..fb2c55c8 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/repository/DevicesRepository.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/repository/DevicesRepository.kt @@ -2,27 +2,38 @@ package io.github.openflocon.domain.device.repository import io.github.openflocon.domain.device.models.DeviceAppDomainModel import io.github.openflocon.domain.device.models.DeviceDomainModel +import io.github.openflocon.domain.device.models.DeviceId +import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel +import io.github.openflocon.domain.device.models.DeviceWithAppsDomainModel +import io.github.openflocon.domain.device.models.DeviceWithAppDomainModel +import io.github.openflocon.domain.device.models.HandleDeviceResultDomainModel +import io.github.openflocon.domain.device.models.RegisterDeviceWithAppDomainModel import kotlinx.coroutines.flow.Flow interface DevicesRepository { val devices: Flow> + val currentDeviceId: Flow + + suspend fun getCurrentDeviceId(): DeviceId? // returns new if new device - suspend fun register(device: DeviceDomainModel) : Boolean + suspend fun register(registerDeviceWithApp: RegisterDeviceWithAppDomainModel) : HandleDeviceResultDomainModel + suspend fun getCurrentDevice(): DeviceDomainModel? + suspend fun selectDevice(deviceId: DeviceId) - suspend fun unregister(device: DeviceDomainModel) + // region apps + fun observeDeviceApps(deviceId: DeviceId): Flow> + fun observeDeviceSelectedApp(deviceId: DeviceId): Flow + suspend fun getDeviceSelectedApp(deviceId: DeviceId): DeviceAppDomainModel? + suspend fun getDeviceAppByPackage(deviceId: DeviceId, appPackageName: String) : DeviceAppDomainModel? + suspend fun selectApp(deviceId: DeviceId, app: DeviceAppDomainModel) + // endregion + + // region app icon + suspend fun saveAppIcon(deviceId: DeviceId, appPackageName: String, iconEncoded: String) + suspend fun hasAppIcon(deviceId: DeviceId, appPackageName: String) : Boolean + suspend fun askForDeviceAppIcon(deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel) +// endregion suspend fun clear() - - val currentDevice: Flow - - val currentDeviceApp: Flow - - fun getCurrentDevice(): DeviceDomainModel? - - fun getCurrentDeviceApp(): DeviceAppDomainModel? - - suspend fun selectDevice(device: DeviceDomainModel) - - suspend fun selectApp(app: DeviceAppDomainModel) } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceAppUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceAppUseCase.kt deleted file mode 100644 index c4374473..00000000 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceAppUseCase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.openflocon.domain.device.usecase - -import io.github.openflocon.domain.device.repository.DevicesRepository -import io.github.openflocon.domain.device.models.DeviceAppDomainModel - -class GetCurrentDeviceAppUseCase( - private val devicesRepository: DevicesRepository, -) { - operator fun invoke(): DeviceAppDomainModel? = devicesRepository.getCurrentDeviceApp() -} diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceIdAndPackageNameUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceIdAndPackageNameUseCase.kt index bab6d6fc..cc47aa71 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceIdAndPackageNameUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceIdAndPackageNameUseCase.kt @@ -1,14 +1,15 @@ package io.github.openflocon.domain.device.usecase import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel +import io.github.openflocon.domain.device.repository.DevicesRepository class GetCurrentDeviceIdAndPackageNameUseCase( - private val getCurrentDeviceUseCase: GetCurrentDeviceUseCase, - private val getCurrentDeviceAppUseCase: GetCurrentDeviceAppUseCase, + private val getCurrentDeviceIdUseCase: GetCurrentDeviceIdUseCase, + private val devicesRepository: DevicesRepository, ) { - operator fun invoke(): DeviceIdAndPackageNameDomainModel? { - val deviceId = getCurrentDeviceUseCase()?.deviceId ?: return null - val packageName = getCurrentDeviceAppUseCase()?.packageName ?: return null + suspend operator fun invoke(): DeviceIdAndPackageNameDomainModel? { + val deviceId = getCurrentDeviceIdUseCase() ?: return null + val packageName = devicesRepository.getDeviceSelectedApp(deviceId)?.packageName ?: return null return DeviceIdAndPackageNameDomainModel( deviceId = deviceId, diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceIdUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceIdUseCase.kt index deb9e8d1..71526e9e 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceIdUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceIdUseCase.kt @@ -1,9 +1,10 @@ package io.github.openflocon.domain.device.usecase import io.github.openflocon.domain.device.models.DeviceId +import io.github.openflocon.domain.device.repository.DevicesRepository class GetCurrentDeviceIdUseCase( - private val getCurrentDeviceUseCase: GetCurrentDeviceUseCase, + private val devicesRepository: DevicesRepository, ) { - operator fun invoke(): DeviceId? = getCurrentDeviceUseCase()?.deviceId + suspend operator fun invoke(): DeviceId? = devicesRepository.getCurrentDeviceId() } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceUseCase.kt deleted file mode 100644 index eecf25b7..00000000 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/GetCurrentDeviceUseCase.kt +++ /dev/null @@ -1,10 +0,0 @@ -package io.github.openflocon.domain.device.usecase - -import io.github.openflocon.domain.device.repository.DevicesRepository -import io.github.openflocon.domain.device.models.DeviceDomainModel - -class GetCurrentDeviceUseCase( - private val devicesRepository: DevicesRepository, -) { - operator fun invoke(): DeviceDomainModel? = devicesRepository.getCurrentDevice() -} diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleDeviceAndAppUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleDeviceAndAppUseCase.kt new file mode 100644 index 00000000..c853183c --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleDeviceAndAppUseCase.kt @@ -0,0 +1,14 @@ +package io.github.openflocon.domain.device.usecase + +import io.github.openflocon.domain.device.repository.DevicesRepository +import io.github.openflocon.domain.device.models.HandleDeviceResultDomainModel +import io.github.openflocon.domain.device.models.RegisterDeviceWithAppDomainModel + + +class HandleDeviceAndAppUseCase( + private val devicesRepository: DevicesRepository, +) { + suspend operator fun invoke(device: RegisterDeviceWithAppDomainModel): HandleDeviceResultDomainModel { + return devicesRepository.register(device) + } +} diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleDeviceUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleDeviceUseCase.kt deleted file mode 100644 index fccbd7bf..00000000 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleDeviceUseCase.kt +++ /dev/null @@ -1,18 +0,0 @@ -package io.github.openflocon.domain.device.usecase - -import io.github.openflocon.domain.device.repository.DevicesRepository -import io.github.openflocon.domain.device.models.DeviceDomainModel -import io.github.openflocon.domain.device.models.HandleDeviceResultDomainModel - - -class HandleDeviceUseCase( - private val devicesRepository: DevicesRepository, -) { - suspend operator fun invoke(device: DeviceDomainModel): HandleDeviceResultDomainModel { - val isNewDevice = devicesRepository.register(device) - return HandleDeviceResultDomainModel( - deviceId = device.deviceId, - isNewDevice = isNewDevice, - ) - } -} diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleNewAppUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleNewAppUseCase.kt new file mode 100644 index 00000000..ddb2a10a --- /dev/null +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleNewAppUseCase.kt @@ -0,0 +1,17 @@ +package io.github.openflocon.domain.device.usecase + +import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel +import io.github.openflocon.domain.device.repository.DevicesRepository + +class HandleNewAppUseCase( + private val devicesRepository: DevicesRepository, +) { + suspend operator fun invoke(deviceIdAndPackageName: DeviceIdAndPackageNameDomainModel) { + if(!devicesRepository.hasAppIcon( + deviceId = deviceIdAndPackageName.deviceId, + appPackageName = deviceIdAndPackageName.packageName + )) { + devicesRepository.askForDeviceAppIcon(deviceIdAndPackageName) + } + } +} diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/usecase/HandleNewDeviceUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleNewDeviceUseCase.kt similarity index 96% rename from FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/usecase/HandleNewDeviceUseCase.kt rename to FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleNewDeviceUseCase.kt index 940fe857..1b9d56a0 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/usecase/HandleNewDeviceUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/HandleNewDeviceUseCase.kt @@ -1,4 +1,4 @@ -package io.github.openflocon.domain.messages.usecase +package io.github.openflocon.domain.device.usecase import io.github.openflocon.domain.adb.repository.AdbRepository import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceAppUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceAppUseCase.kt index 773d3b50..88cffc2f 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceAppUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceAppUseCase.kt @@ -3,9 +3,18 @@ package io.github.openflocon.domain.device.usecase import io.github.openflocon.domain.device.repository.DevicesRepository import io.github.openflocon.domain.device.models.DeviceAppDomainModel import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf -class ObserveCurrentDeviceAppUseCase( +class ObserveCurrentDeviceAppsUseCase( private val devicesRepository: DevicesRepository, + private val observeCurrentDeviceIdUseCase: ObserveCurrentDeviceIdUseCase, ) { - operator fun invoke(): Flow = devicesRepository.currentDeviceApp + operator fun invoke(): Flow> = observeCurrentDeviceIdUseCase().flatMapLatest { + if (it == null) { + flowOf(emptyList()) + } else { + devicesRepository.observeDeviceApps(it) + } + } } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceIdAndPackageNameUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceIdAndPackageNameUseCase.kt index e45c11b6..9b8f45a2 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceIdAndPackageNameUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceIdAndPackageNameUseCase.kt @@ -1,23 +1,31 @@ package io.github.openflocon.domain.device.usecase -import io.github.openflocon.domain.device.repository.DevicesRepository import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel +import io.github.openflocon.domain.device.repository.DevicesRepository import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map class ObserveCurrentDeviceIdAndPackageNameUseCase( + private val observeCurrentDeviceIdUseCase: ObserveCurrentDeviceIdUseCase, private val devicesRepository: DevicesRepository, ) { - operator fun invoke(): Flow = combine( - devicesRepository.currentDevice, - devicesRepository.currentDeviceApp, - ) { device, app -> - if (device != null && app != null) { - DeviceIdAndPackageNameDomainModel(deviceId = device.deviceId, packageName = app.packageName) - } else { - null - } - } - .distinctUntilChanged() + operator fun invoke(): Flow = + observeCurrentDeviceIdUseCase().flatMapLatest { deviceId -> + if (deviceId == null) { + flowOf(null) + } else { + devicesRepository.observeDeviceSelectedApp(deviceId) + .map { app -> + app?.let { + DeviceIdAndPackageNameDomainModel( + deviceId = deviceId, + packageName = app.packageName, + ) + } + } + } + }.distinctUntilChanged() } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceIdUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceIdUseCase.kt index 0dc3484b..53cd0148 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceIdUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceIdUseCase.kt @@ -9,5 +9,5 @@ import kotlinx.coroutines.flow.map class ObserveCurrentDeviceIdUseCase( private val devicesRepository: DevicesRepository, ) { - operator fun invoke(): Flow = devicesRepository.currentDevice.map { it?.deviceId }.distinctUntilChanged() + operator fun invoke(): Flow = devicesRepository.currentDeviceId } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceUseCase.kt deleted file mode 100644 index beb182dd..00000000 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveCurrentDeviceUseCase.kt +++ /dev/null @@ -1,11 +0,0 @@ -package io.github.openflocon.domain.device.usecase - -import io.github.openflocon.domain.device.repository.DevicesRepository -import io.github.openflocon.domain.device.models.DeviceDomainModel -import kotlinx.coroutines.flow.Flow - -class ObserveCurrentDeviceUseCase( - private val devicesRepository: DevicesRepository, -) { - operator fun invoke(): Flow = devicesRepository.currentDevice -} diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveDevicesUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveDevicesUseCase.kt index 828cb926..5139fa8d 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveDevicesUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/ObserveDevicesUseCase.kt @@ -1,9 +1,11 @@ package io.github.openflocon.domain.device.usecase +import io.github.openflocon.domain.device.models.DeviceDomainModel import io.github.openflocon.domain.device.repository.DevicesRepository +import kotlinx.coroutines.flow.Flow class ObserveDevicesUseCase( private val devicesRepository: DevicesRepository, ) { - operator fun invoke() = devicesRepository.devices + operator fun invoke(): Flow> = devicesRepository.devices } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/SelectDeviceAppUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/SelectDeviceAppUseCase.kt index 8f1aaf0b..08338e88 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/SelectDeviceAppUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/SelectDeviceAppUseCase.kt @@ -5,14 +5,13 @@ import kotlinx.coroutines.flow.firstOrNull class SelectDeviceAppUseCase( private val devicesRepository: DevicesRepository, + private val getCurrentDeviceIdUseCase: GetCurrentDeviceIdUseCase, ) { suspend operator fun invoke(packageName: String) { - val app = devicesRepository.currentDevice - .firstOrNull() - ?.apps - ?.find { it.packageName == packageName } - ?: return + val deviceId = getCurrentDeviceIdUseCase() ?: return - devicesRepository.selectApp(app) + val app = devicesRepository.getDeviceAppByPackage(deviceId = deviceId, appPackageName = packageName) ?: return + + devicesRepository.selectApp(deviceId = deviceId, app = app) } } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/SelectDeviceUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/SelectDeviceUseCase.kt index fff9a914..4ced3cf2 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/SelectDeviceUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/device/usecase/SelectDeviceUseCase.kt @@ -7,11 +7,6 @@ class SelectDeviceUseCase( private val devicesRepository: DevicesRepository, ) { suspend operator fun invoke(deviceId: String) { - val device = devicesRepository.devices - .firstOrNull() - ?.find { it.deviceId == deviceId } - ?: return - - devicesRepository.selectDevice(device) + devicesRepository.selectDevice(deviceId) } } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/DI.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/DI.kt index f5e73aa4..877a128c 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/DI.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/DI.kt @@ -1,6 +1,7 @@ package io.github.openflocon.domain.messages import io.github.openflocon.domain.messages.usecase.HandleIncomingMessagesUseCase +import io.github.openflocon.domain.device.usecase.HandleNewAppUseCase import io.github.openflocon.domain.messages.usecase.StartServerUseCase import org.koin.core.module.dsl.factoryOf import org.koin.dsl.module @@ -10,8 +11,9 @@ internal val messagesModule = module { HandleIncomingMessagesUseCase( messagesRepository = get(), plugins = getAll(), - handleDeviceUseCase = get(), + handleDeviceAndAppUseCase = get(), handleNewDeviceUseCase = get(), + handleNewAppUseCase = get(), ) } factoryOf(::StartServerUseCase) diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/usecase/HandleIncomingMessagesUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/usecase/HandleIncomingMessagesUseCase.kt index 219a5e60..d146523f 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/usecase/HandleIncomingMessagesUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/messages/usecase/HandleIncomingMessagesUseCase.kt @@ -3,7 +3,10 @@ package io.github.openflocon.domain.messages.usecase import io.github.openflocon.domain.device.models.DeviceAppDomainModel import io.github.openflocon.domain.device.models.DeviceDomainModel import io.github.openflocon.domain.device.models.DeviceIdAndPackageNameDomainModel -import io.github.openflocon.domain.device.usecase.HandleDeviceUseCase +import io.github.openflocon.domain.device.models.RegisterDeviceWithAppDomainModel +import io.github.openflocon.domain.device.usecase.HandleDeviceAndAppUseCase +import io.github.openflocon.domain.device.usecase.HandleNewAppUseCase +import io.github.openflocon.domain.device.usecase.HandleNewDeviceUseCase import io.github.openflocon.domain.messages.models.FloconIncomingMessageDomainModel import io.github.openflocon.domain.messages.repository.MessagesReceiverRepository import io.github.openflocon.domain.messages.repository.MessagesRepository @@ -14,14 +17,15 @@ import kotlinx.coroutines.flow.onEach class HandleIncomingMessagesUseCase( private val messagesRepository: MessagesRepository, private val plugins: List, - private val handleDeviceUseCase: HandleDeviceUseCase, + private val handleDeviceAndAppUseCase: HandleDeviceAndAppUseCase, private val handleNewDeviceUseCase: HandleNewDeviceUseCase, + private val handleNewAppUseCase: HandleNewAppUseCase, ) { operator fun invoke(): Flow = messagesRepository .listenMessages() .onEach { - val handleDeviceResult = handleDeviceUseCase(device = getDevice(it)) + val handleDeviceResult = handleDeviceAndAppUseCase(device = getDeviceAndApp(it)) if (handleDeviceResult.isNewDevice) { handleNewDeviceUseCase( deviceIdAndPackageName = DeviceIdAndPackageNameDomainModel( @@ -30,14 +34,22 @@ class HandleIncomingMessagesUseCase( ) ) } + if (handleDeviceResult.isNewApp) { + handleNewAppUseCase( + deviceIdAndPackageName = DeviceIdAndPackageNameDomainModel( + deviceId = handleDeviceResult.deviceId, + packageName = it.appPackageName, + ) + ) + } plugins.forEach { plugin -> - if (handleDeviceResult.isNewDevice) { + if (handleDeviceResult.justConnectedForThisSession) { plugin.onDeviceConnected( deviceIdAndPackageName = DeviceIdAndPackageNameDomainModel( deviceId = handleDeviceResult.deviceId, packageName = it.appPackageName, ), - isNewDevice = true, // TODO on a next MR, for now handleDeviceResult.isNewDevice is always true the first time + isNewDevice = handleDeviceResult.isNewDevice, ) } if (plugin.pluginName.contains(it.plugin)) { @@ -47,14 +59,16 @@ class HandleIncomingMessagesUseCase( } .map { } - private fun getDevice(message: FloconIncomingMessageDomainModel): DeviceDomainModel = DeviceDomainModel( - deviceName = message.deviceName, - deviceId = message.deviceId, - apps = listOf( - DeviceAppDomainModel( + private fun getDeviceAndApp(message: FloconIncomingMessageDomainModel) = + RegisterDeviceWithAppDomainModel( + device = DeviceDomainModel( + deviceId = message.deviceId, + message.deviceName, + ), + app = DeviceAppDomainModel( name = message.appName, packageName = message.appPackageName, + iconEncoded = null, ), - ), - ) + ) } diff --git a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/sharedpreference/usecase/SelectCurrentDeviceSharedPreferenceUseCase.kt b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/sharedpreference/usecase/SelectCurrentDeviceSharedPreferenceUseCase.kt index 6298ae08..5b01c0d4 100644 --- a/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/sharedpreference/usecase/SelectCurrentDeviceSharedPreferenceUseCase.kt +++ b/FloconDesktop/domain/src/commonMain/kotlin/io/github/openflocon/domain/sharedpreference/usecase/SelectCurrentDeviceSharedPreferenceUseCase.kt @@ -8,7 +8,7 @@ class SelectCurrentDeviceSharedPreferenceUseCase( private val sharedPreferenceRepository: SharedPreferencesRepository, private val getCurrentDeviceIdAndPackageNameUseCase: GetCurrentDeviceIdAndPackageNameUseCase, ) { - operator fun invoke(sharedPreferenceId: DeviceSharedPreferenceId) { + suspend operator fun invoke(sharedPreferenceId: DeviceSharedPreferenceId) { val current = getCurrentDeviceIdAndPackageNameUseCase() ?: return sharedPreferenceRepository.selectDeviceSharedPreference( diff --git a/FloconDesktop/library/designsystem/src/androidMain/kotlin/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.android.kt b/FloconDesktop/library/designsystem/src/androidMain/kotlin/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.android.kt new file mode 100644 index 00000000..11321f76 --- /dev/null +++ b/FloconDesktop/library/designsystem/src/androidMain/kotlin/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.android.kt @@ -0,0 +1,18 @@ +package io.github.openflocon.library.designsystem.components + +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier + +@Composable +actual fun FloconVerticalScrollbar( + adapter: FloconScrollAdapter, + modifier: Modifier +) { +} + +@Composable +actual fun rememberFloconScrollbarAdapter(scrollState: LazyListState): FloconScrollAdapter { + return remember { object : FloconScrollAdapter{} } +} diff --git a/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.kt b/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.kt index 46656e30..8a953c58 100644 --- a/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.kt +++ b/FloconDesktop/library/designsystem/src/commonMain/kotlin/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.kt @@ -1,17 +1,16 @@ package io.github.openflocon.library.designsystem.components -import androidx.compose.foundation.VerticalScrollbar -import androidx.compose.foundation.v2.ScrollbarAdapter +import androidx.compose.foundation.lazy.LazyListState import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +interface FloconScrollAdapter + @Composable -fun FloconVerticalScrollbar( - adapter: ScrollbarAdapter, +expect fun rememberFloconScrollbarAdapter(scrollState: LazyListState) : FloconScrollAdapter + +@Composable +expect fun FloconVerticalScrollbar( + adapter: FloconScrollAdapter, modifier: Modifier = Modifier -) { - VerticalScrollbar( - adapter = adapter, - modifier = modifier - ) -} +) diff --git a/FloconDesktop/library/designsystem/src/desktopMain/java/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.desktop.kt b/FloconDesktop/library/designsystem/src/desktopMain/java/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.desktop.kt new file mode 100644 index 00000000..7addd307 --- /dev/null +++ b/FloconDesktop/library/designsystem/src/desktopMain/java/io/github/openflocon/library/designsystem/components/FloconVerticalScrollbar.desktop.kt @@ -0,0 +1,33 @@ +package io.github.openflocon.library.designsystem.components + +import androidx.compose.foundation.VerticalScrollbar +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.foundation.rememberScrollbarAdapter +import androidx.compose.foundation.v2.ScrollbarAdapter +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember + +data class FloconScrollAdapterDesktop( + val scrollbarAdapter: ScrollbarAdapter +) : FloconScrollAdapter + +@Composable +actual fun FloconVerticalScrollbar( + adapter: FloconScrollAdapter, + modifier: androidx.compose.ui.Modifier +) { + VerticalScrollbar( + adapter = (adapter as FloconScrollAdapterDesktop).scrollbarAdapter, + modifier = modifier + ) +} + +@Composable +actual fun rememberFloconScrollbarAdapter(scrollState: LazyListState): FloconScrollAdapter { + val adapter = rememberScrollbarAdapter(scrollState) + return remember(adapter) { + FloconScrollAdapterDesktop( + adapter + ) + } +}