feat(device): add visibility/luftdata/narodmon settings, device model icons, SDK refactor
- Add SetDeviceVisibilityUseCase, SetLuftdataUseCase, SetNarodmonUseCase - Introduce DeviceModel enum (Basic, Mobile, Solar, Radiation, Custom) - Add device-type drawables (active/inactive icons for each model) - Refactor Device SDK: repository, DAO, database, local/remote data sources - Update DeviceSettingsScreen, ManageViewModel, theme colors - Add/update string resources (default, ru, be) Made-with: Cursor
This commit is contained in:
@@ -45,3 +45,14 @@ Use this stack as the default foundation for all implementation work in `airmq-a
|
||||
5. Apollo GraphQL for GraphQL schema integration, client generation, and network operations.
|
||||
|
||||
If a requested change conflicts with this baseline, ask for explicit approval before introducing an alternative.
|
||||
|
||||
## Temporary or implicit implementations
|
||||
|
||||
When implementing something implicitly or obviously temporary, document it in `temp.md` at the project root with all important details:
|
||||
|
||||
- What was implemented
|
||||
- Why it is temporary or implicit
|
||||
- Location (files, components, modules)
|
||||
- Any follow-up work or conditions for proper replacement
|
||||
|
||||
Create `temp.md` if it does not exist.
|
||||
|
||||
8
.idea/markdown.xml
generated
Normal file
8
.idea/markdown.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownSettings">
|
||||
<option name="previewPanelProviderInfo">
|
||||
<ProviderInfo name="Compose (experimental)" className="com.intellij.markdown.compose.preview.ComposePanelProvider" />
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@@ -1,25 +1,33 @@
|
||||
package org.db3.airmq.features.device
|
||||
|
||||
import android.R as AndroidR
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Snackbar
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Switch
|
||||
@@ -27,24 +35,44 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import org.db3.airmq.R
|
||||
import org.db3.airmq.sdk.device.domain.DeviceModel
|
||||
import org.db3.airmq.features.common.AirMQButton
|
||||
import org.db3.airmq.ui.theme.AirMQTheme
|
||||
import org.db3.airmq.features.common.AirMQButtonStyle
|
||||
import org.db3.airmq.sdk.device.domain.OnlineStatus
|
||||
import org.db3.airmq.ui.theme.LegacyBackground
|
||||
import org.db3.airmq.ui.theme.LegacyBlack12
|
||||
import androidx.core.net.toUri
|
||||
|
||||
private const val INFO_URL = "https://docs.google.com/document/d/1JC41nHE5foYhS6beDFTA5a8dQqX3IQoqHJYZbPRtmuM/edit"
|
||||
|
||||
private fun roundLocation(d: Double): String =
|
||||
"%.5f".format(d).replace(',', '.')
|
||||
|
||||
private fun DeviceModel.iconRes(): Int = when (this) {
|
||||
DeviceModel.Basic -> R.drawable.ic_device_basic_active_10
|
||||
DeviceModel.Mobile -> R.drawable.ic_device_mobile_active_10
|
||||
DeviceModel.Solar -> R.drawable.ic_device_solar_active_10
|
||||
DeviceModel.Radiation -> R.drawable.ic_device_radiation_active_10
|
||||
DeviceModel.Custom -> R.drawable.ic_device_custom_active_10
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DeviceSettingsScreen(
|
||||
@@ -52,11 +80,15 @@ fun DeviceSettingsScreen(
|
||||
onOpenLocation: () -> Unit,
|
||||
onShowOnMap: () -> Unit,
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: DeviceSettingsViewModel = hiltViewModel()
|
||||
viewModel: DeviceSettingsViewModel = hiltViewModel(
|
||||
creationCallback = { factory: DeviceSettingsViewModel.Factory ->
|
||||
factory.create(deviceId)
|
||||
}
|
||||
)
|
||||
) {
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val toastDoneText = stringResource(R.string.toast_done)
|
||||
val context = LocalContext.current
|
||||
|
||||
LaunchedEffect(viewModel) {
|
||||
viewModel.actions.collectLatest { action ->
|
||||
@@ -64,21 +96,23 @@ fun DeviceSettingsScreen(
|
||||
is DeviceSettingsScreenContract.Action.ShowError -> {
|
||||
snackbarHostState.showSnackbar(action.message)
|
||||
}
|
||||
is DeviceSettingsScreenContract.Action.ShowSuccess -> {
|
||||
snackbarHostState.showSnackbar(toastDoneText)
|
||||
}
|
||||
is DeviceSettingsScreenContract.Action.ShowSuccess -> { /* no snackbar for success */ }
|
||||
is DeviceSettingsScreenContract.Action.OpenLocation -> onOpenLocation()
|
||||
is DeviceSettingsScreenContract.Action.ShowOnMap -> onShowOnMap()
|
||||
is DeviceSettingsScreenContract.Action.OpenInfoUrl -> {
|
||||
context.startActivity(Intent(Intent.ACTION_VIEW, INFO_URL.toUri()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
topBar = {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.statusBarsPadding()
|
||||
.background(LegacyBackground)
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
@@ -90,26 +124,42 @@ fun DeviceSettingsScreen(
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = stringResource(R.string.title_device),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
text = stringResource(R.string.title_device_config),
|
||||
style = MaterialTheme.typography.titleLarge.copy(
|
||||
fontSize = 24.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 8.dp),
|
||||
textAlign = androidx.compose.ui.text.style.TextAlign.Center
|
||||
)
|
||||
Spacer(modifier = Modifier.size(48.dp))
|
||||
}
|
||||
},
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) }
|
||||
snackbarHost = {
|
||||
SnackbarHost(
|
||||
hostState = snackbarHostState,
|
||||
snackbar = { data ->
|
||||
Snackbar(
|
||||
snackbarData = data,
|
||||
containerColor = MaterialTheme.colorScheme.inverseSurface,
|
||||
contentColor = MaterialTheme.colorScheme.inverseOnSurface
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.fillMaxWidth()
|
||||
.padding(innerPadding)
|
||||
) {
|
||||
when {
|
||||
uiState.isLoading -> {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.align(Alignment.Center)
|
||||
)
|
||||
uiState.device == null && uiState.isLoading -> {
|
||||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||||
}
|
||||
uiState.device == null -> {
|
||||
uiState.device == null && !uiState.isLoading -> {
|
||||
Text(
|
||||
text = stringResource(R.string.text_nothing_to_show),
|
||||
modifier = Modifier
|
||||
@@ -118,26 +168,48 @@ fun DeviceSettingsScreen(
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
Box(modifier = Modifier.fillMaxWidth()) {
|
||||
DeviceSettingsContent(
|
||||
state = uiState,
|
||||
onEvent = viewModel::onEvent,
|
||||
deviceName = uiState.device?.name ?: ""
|
||||
onEvent = viewModel::onEvent
|
||||
)
|
||||
if (uiState.isLoading) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black.copy(alpha = 0.3f)),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val PreviewHeaderColor = Color(0xFF1F5DA5)
|
||||
private val PreviewGreyColor = Color(0x61000000)
|
||||
private val PreviewBlack54 = Color(0x8A000000)
|
||||
private val PreviewOrange = Color(0xFFFF6F00)
|
||||
|
||||
@Composable
|
||||
private fun DeviceSettingsContent(
|
||||
state: DeviceSettingsScreenContract.State,
|
||||
onEvent: (DeviceSettingsScreenContract.Event) -> Unit,
|
||||
deviceName: String
|
||||
labelColorOverride: Color? = null,
|
||||
greyColorOverride: Color? = null,
|
||||
iconResOverride: Int? = null
|
||||
) {
|
||||
val device = state.device!!
|
||||
var showRenameDialog by remember { mutableStateOf(false) }
|
||||
var renameText by remember(deviceName) { mutableStateOf(deviceName) }
|
||||
var showConfigComingSoonDialog by remember { mutableStateOf(false) }
|
||||
var renameText by remember(device.name) { mutableStateOf(device.name) }
|
||||
val headerColor = labelColorOverride ?: colorResource(R.color.headerColor)
|
||||
val onSurfaceVariant = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
val greyColor = greyColorOverride ?: colorResource(R.color.black38)
|
||||
|
||||
if (showRenameDialog) {
|
||||
AlertDialog(
|
||||
@@ -178,9 +250,23 @@ private fun DeviceSettingsContent(
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
if (showConfigComingSoonDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showConfigComingSoonDialog = false },
|
||||
title = { Text(stringResource(R.string.coming_soon)) },
|
||||
text = { Text(stringResource(R.string.coming_soon)) },
|
||||
confirmButton = {
|
||||
androidx.compose.material3.TextButton(onClick = { showConfigComingSoonDialog = false }) {
|
||||
Text(stringResource(R.string.button_ok))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
val scrollState = rememberScrollState()
|
||||
val statusIcon = when (device.toOnlineStatus()) {
|
||||
OnlineStatus.Online -> R.drawable.device_chip_online
|
||||
val statusIcon = when {
|
||||
state.isConnected -> R.drawable.device_chip_online
|
||||
else -> R.drawable.device_chip_offline
|
||||
}
|
||||
|
||||
@@ -189,115 +275,458 @@ private fun DeviceSettingsContent(
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(scrollState)
|
||||
.background(LegacyBackground)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
HorizontalDivider(color = LegacyBlack12)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(72.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_chip),
|
||||
painter = painterResource(
|
||||
iconResOverride ?: device.model.iconRes()
|
||||
),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(48.dp)
|
||||
modifier = Modifier.size(40.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.size(16.dp))
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = device.name,
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
style = MaterialTheme.typography.bodyLarge.copy(fontSize = 16.sp)
|
||||
)
|
||||
Text(
|
||||
text = device.model,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = Color.Gray
|
||||
text = device.model.displayName,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(fontSize = 14.sp),
|
||||
color = greyColor
|
||||
)
|
||||
}
|
||||
Text(
|
||||
text = if (state.isConnected) stringResource(R.string.text_device_connected) else "",
|
||||
style = MaterialTheme.typography.labelSmall.copy(fontSize = 11.sp),
|
||||
color = greyColor
|
||||
)
|
||||
if (!state.isConnected) {
|
||||
Icon(
|
||||
painter = painterResource(statusIcon),
|
||||
contentDescription = device.toOnlineStatus().toString(),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
}
|
||||
|
||||
if (state.isPendingSync) {
|
||||
Text(
|
||||
text = stringResource(R.string.text_pending_sync),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = Color.Gray,
|
||||
modifier = Modifier.padding(top = 8.dp)
|
||||
)
|
||||
}
|
||||
HorizontalDivider(color = LegacyBlack12)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
|
||||
AirMQButton(
|
||||
text = stringResource(R.string.button_rename),
|
||||
ConfigRow(
|
||||
label = stringResource(R.string.text_device_name),
|
||||
value = device.name,
|
||||
onClick = { showRenameDialog = true },
|
||||
style = AirMQButtonStyle.Outlined,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
trailingIcon = R.drawable.ic_edit,
|
||||
iconTint = (labelColorOverride?.let { PreviewBlack54 } ?: colorResource(R.color.black54)),
|
||||
labelColor = headerColor
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
ConfigRowReadOnly(
|
||||
label = stringResource(R.string.text_device_id),
|
||||
value = device.id,
|
||||
labelColor = headerColor
|
||||
)
|
||||
|
||||
if (device.hasLocation()) {
|
||||
Text(
|
||||
text = stringResource(
|
||||
ConfigRowReadOnly(
|
||||
label = stringResource(R.string.text_device_ip),
|
||||
value = device.deviceAddress,
|
||||
labelColor = headerColor
|
||||
)
|
||||
|
||||
ConfigRowWithIndicator(
|
||||
label = stringResource(R.string.text_device_config_version),
|
||||
value = device.configVersion,
|
||||
showIndicator = state.configVersionNeedsUpdate,
|
||||
indicatorColor = labelColorOverride?.let { PreviewOrange } ?: colorResource(R.color.colorOrange),
|
||||
onClick = { if (state.configVersionNeedsUpdate) showConfigComingSoonDialog = true },
|
||||
trailingIcon = if (state.configVersionNeedsUpdate) {
|
||||
R.drawable.ic_upload
|
||||
} else null,
|
||||
iconTint = labelColorOverride?.let { PreviewBlack54 } ?: colorResource(R.color.black54),
|
||||
labelColor = headerColor,
|
||||
indicatorDrawableRes = null
|
||||
)
|
||||
|
||||
ConfigRowReadOnly(
|
||||
label = stringResource(R.string.text_device_wifi),
|
||||
value = state.wifiSsid,
|
||||
labelColor = headerColor
|
||||
)
|
||||
|
||||
HorizontalDivider(color = LegacyBlack12)
|
||||
|
||||
ConfigRowWithIndicator(
|
||||
label = stringResource(R.string.text_device_location),
|
||||
value = if (device.hasLocation()) {
|
||||
stringResource(
|
||||
R.string.text_device_location_set,
|
||||
device.latitude!!,
|
||||
device.longitude!!
|
||||
),
|
||||
style = MaterialTheme.typography.bodyMedium
|
||||
roundLocation(device.latitude!!),
|
||||
roundLocation(device.longitude!!)
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
AirMQButton(
|
||||
text = stringResource(R.string.title_location),
|
||||
} else {
|
||||
stringResource(R.string.text_device_location_unset)
|
||||
},
|
||||
showIndicator = !device.hasLocation(),
|
||||
indicatorColor = labelColorOverride?.let { PreviewOrange } ?: colorResource(R.color.colorOrange),
|
||||
onClick = { onEvent(DeviceSettingsScreenContract.Event.OpenLocationClicked) },
|
||||
style = AirMQButtonStyle.Outlined,
|
||||
modifier = Modifier.weight(1f)
|
||||
trailingIcon = if (device.hasLocation()) null else R.drawable.ic_edit,
|
||||
iconTint = labelColorOverride?.let { PreviewBlack54 } ?: colorResource(R.color.black54),
|
||||
labelColor = headerColor,
|
||||
indicatorDrawableRes = null
|
||||
)
|
||||
|
||||
ToggleRow(
|
||||
title = stringResource(R.string.text_device_narodmon_title),
|
||||
subtitle = stringResource(R.string.text_device_narodmon),
|
||||
checked = device.isNarodmonOn,
|
||||
onCheckedChange = { onEvent(DeviceSettingsScreenContract.Event.NarodmonToggled(it)) },
|
||||
onInfoClick = { onEvent(DeviceSettingsScreenContract.Event.NarodmonInfoClicked) },
|
||||
labelColor = headerColor,
|
||||
infoIconTint = labelColorOverride?.let { PreviewBlack54 } ?: colorResource(R.color.black54),
|
||||
infoIconResOverride = null
|
||||
)
|
||||
|
||||
ToggleRow(
|
||||
title = stringResource(R.string.text_device_luftdata_title),
|
||||
subtitle = stringResource(R.string.text_device_luftdata),
|
||||
checked = device.isLuftdataOn,
|
||||
onCheckedChange = { onEvent(DeviceSettingsScreenContract.Event.LuftdataToggled(it)) },
|
||||
onInfoClick = { onEvent(DeviceSettingsScreenContract.Event.LuftdataInfoClicked) },
|
||||
labelColor = headerColor,
|
||||
infoIconTint = labelColorOverride?.let { PreviewBlack54 } ?: colorResource(R.color.black54),
|
||||
infoIconResOverride = null
|
||||
)
|
||||
|
||||
VisibilityRow(
|
||||
label = stringResource(R.string.text_device_visibility),
|
||||
visibilityText = when {
|
||||
!device.hasLocation() -> stringResource(R.string.text_device_visibility_not_registered)
|
||||
device.isPublic -> stringResource(R.string.text_device_visibility_visible)
|
||||
else -> stringResource(R.string.text_device_visibility_private)
|
||||
},
|
||||
publishButtonText = if (device.isPublic) {
|
||||
stringResource(R.string.button_hide)
|
||||
} else {
|
||||
stringResource(R.string.button_publish)
|
||||
},
|
||||
isPublishButtonEnabled = device.hasLocation(),
|
||||
onPublishClick = {
|
||||
if (device.isPublic) {
|
||||
onEvent(DeviceSettingsScreenContract.Event.VisibilityHideClicked)
|
||||
} else {
|
||||
onEvent(DeviceSettingsScreenContract.Event.VisibilityPublishClicked)
|
||||
}
|
||||
},
|
||||
labelColor = greyColor
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
AirMQButton(
|
||||
text = stringResource(R.string.button_view_on_map),
|
||||
onClick = { onEvent(DeviceSettingsScreenContract.Event.ShowOnMapClicked) },
|
||||
style = AirMQButtonStyle.Outlined,
|
||||
modifier = Modifier.weight(1f)
|
||||
style = AirMQButtonStyle.Contained,
|
||||
enabled = device.hasLocation(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 80.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
}
|
||||
} else {
|
||||
AirMQButton(
|
||||
text = stringResource(R.string.button_register),
|
||||
onClick = { onEvent(DeviceSettingsScreenContract.Event.OpenLocationClicked) },
|
||||
style = AirMQButtonStyle.Outlined,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
@Composable
|
||||
private fun VisibilityRow(
|
||||
label: String,
|
||||
visibilityText: String,
|
||||
publishButtonText: String,
|
||||
isPublishButtonEnabled: Boolean,
|
||||
onPublishClick: () -> Unit,
|
||||
labelColor: Color
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = stringResource(R.string.text_data_sharing),
|
||||
style = MaterialTheme.typography.bodyLarge
|
||||
text = label,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(fontSize = 14.sp),
|
||||
color = labelColor
|
||||
)
|
||||
Switch(
|
||||
checked = device.dataSharingEnabled,
|
||||
onCheckedChange = { onEvent(DeviceSettingsScreenContract.Event.DataSharingToggled(it)) }
|
||||
Text(
|
||||
text = visibilityText,
|
||||
style = MaterialTheme.typography.bodyLarge.copy(fontSize = 16.sp)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
AirMQButton(
|
||||
text = stringResource(R.string.button_firmware_update),
|
||||
onClick = { onEvent(DeviceSettingsScreenContract.Event.FirmwareUpdateClicked) },
|
||||
style = AirMQButtonStyle.Gradient,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
text = publishButtonText,
|
||||
onClick = onPublishClick,
|
||||
enabled = isPublishButtonEnabled,
|
||||
style = AirMQButtonStyle.Text,
|
||||
modifier = Modifier.width(170.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConfigRow(
|
||||
label: String,
|
||||
value: String,
|
||||
onClick: () -> Unit,
|
||||
trailingIcon: Int,
|
||||
iconTint: Color,
|
||||
labelColor: Color
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.labelMedium.copy(fontSize = 12.sp, fontWeight = FontWeight.Medium),
|
||||
color = labelColor
|
||||
)
|
||||
Text(
|
||||
text = value,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(fontSize = 14.sp)
|
||||
)
|
||||
}
|
||||
Icon(
|
||||
painter = painterResource(trailingIcon),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = iconTint
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConfigRowReadOnly(
|
||||
label: String,
|
||||
value: String,
|
||||
labelColor: Color
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.labelMedium.copy(fontSize = 12.sp, fontWeight = FontWeight.Medium),
|
||||
color = labelColor
|
||||
)
|
||||
Text(
|
||||
text = value,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(fontSize = 14.sp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConfigRowWithIndicator(
|
||||
label: String,
|
||||
value: String,
|
||||
showIndicator: Boolean,
|
||||
indicatorColor: Color,
|
||||
onClick: () -> Unit,
|
||||
trailingIcon: Int?,
|
||||
iconTint: Color,
|
||||
labelColor: Color,
|
||||
indicatorDrawableRes: Int? = null
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.clickable(onClick = onClick)
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(modifier = Modifier.weight(1f)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = label,
|
||||
style = MaterialTheme.typography.labelMedium.copy(fontSize = 12.sp, fontWeight = FontWeight.Medium),
|
||||
color = labelColor
|
||||
)
|
||||
if (showIndicator) {
|
||||
Spacer(modifier = Modifier.size(5.dp))
|
||||
Icon(
|
||||
painter = painterResource(indicatorDrawableRes ?: R.drawable.circle_indicator),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(10.dp),
|
||||
tint = indicatorColor
|
||||
)
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = value,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(fontSize = 14.sp)
|
||||
)
|
||||
}
|
||||
if (trailingIcon != null) {
|
||||
Icon(
|
||||
painter = painterResource(trailingIcon),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = iconTint
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ToggleRow(
|
||||
title: String,
|
||||
subtitle: String,
|
||||
checked: Boolean,
|
||||
onCheckedChange: (Boolean) -> Unit,
|
||||
onInfoClick: () -> Unit,
|
||||
labelColor: Color,
|
||||
infoIconTint: Color,
|
||||
infoIconResOverride: Int? = null
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(56.dp)
|
||||
.clickable { onCheckedChange(!checked) }
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.labelMedium.copy(fontSize = 12.sp, fontWeight = FontWeight.Medium),
|
||||
color = labelColor
|
||||
)
|
||||
Text(
|
||||
text = subtitle,
|
||||
style = MaterialTheme.typography.bodyMedium.copy(fontSize = 14.sp)
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = onInfoClick,
|
||||
modifier = Modifier
|
||||
.size(32.dp)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(infoIconResOverride ?: R.drawable.ic_pref_info),
|
||||
contentDescription = null,
|
||||
tint = infoIconTint
|
||||
)
|
||||
}
|
||||
}
|
||||
Switch(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Device config – no location")
|
||||
@Composable
|
||||
private fun PreviewDeviceSettingsContentDefault() {
|
||||
AirMQTheme {
|
||||
DeviceSettingsContent(
|
||||
state = DeviceSettingsScreenContract.previewState(),
|
||||
onEvent = {},
|
||||
labelColorOverride = PreviewHeaderColor,
|
||||
greyColorOverride = PreviewGreyColor,
|
||||
iconResOverride = AndroidR.drawable.ic_menu_mylocation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Device config – with location")
|
||||
@Composable
|
||||
private fun PreviewDeviceSettingsContentWithLocation() {
|
||||
AirMQTheme {
|
||||
DeviceSettingsContent(
|
||||
state = DeviceSettingsScreenContract.previewState(
|
||||
device = DeviceSettingsScreenContract.previewDevice(hasLocation = true, isPublic = false)
|
||||
),
|
||||
onEvent = {},
|
||||
labelColorOverride = PreviewHeaderColor,
|
||||
greyColorOverride = PreviewGreyColor,
|
||||
iconResOverride = AndroidR.drawable.ic_menu_mylocation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Device config – public visibility")
|
||||
@Composable
|
||||
private fun PreviewDeviceSettingsContentPublicVisibility() {
|
||||
AirMQTheme {
|
||||
DeviceSettingsContent(
|
||||
state = DeviceSettingsScreenContract.previewState(
|
||||
device = DeviceSettingsScreenContract.previewDevice(hasLocation = true, isPublic = true)
|
||||
),
|
||||
onEvent = {},
|
||||
labelColorOverride = PreviewHeaderColor,
|
||||
greyColorOverride = PreviewGreyColor,
|
||||
iconResOverride = AndroidR.drawable.ic_menu_mylocation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Device config – config needs update")
|
||||
@Composable
|
||||
private fun PreviewDeviceSettingsContentConfigNeedsUpdate() {
|
||||
AirMQTheme {
|
||||
DeviceSettingsContent(
|
||||
state = DeviceSettingsScreenContract.previewState(
|
||||
configVersionNeedsUpdate = true
|
||||
),
|
||||
onEvent = {},
|
||||
labelColorOverride = PreviewHeaderColor,
|
||||
greyColorOverride = PreviewGreyColor,
|
||||
iconResOverride = AndroidR.drawable.ic_menu_mylocation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, name = "Device config – Narodmon/Luftdata on")
|
||||
@Composable
|
||||
private fun PreviewDeviceSettingsContentTogglesOn() {
|
||||
AirMQTheme {
|
||||
DeviceSettingsContent(
|
||||
state = DeviceSettingsScreenContract.previewState(
|
||||
device = DeviceSettingsScreenContract.previewDevice(
|
||||
isNarodmonOn = true,
|
||||
isLuftdataOn = true
|
||||
)
|
||||
),
|
||||
onEvent = {},
|
||||
labelColorOverride = PreviewHeaderColor,
|
||||
greyColorOverride = PreviewGreyColor,
|
||||
iconResOverride = AndroidR.drawable.ic_menu_mylocation
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,65 @@
|
||||
package org.db3.airmq.features.device
|
||||
|
||||
import org.db3.airmq.sdk.device.domain.Device
|
||||
import org.db3.airmq.sdk.device.domain.DeviceModel
|
||||
import org.db3.airmq.sdk.device.domain.OnlineFreshness
|
||||
|
||||
object DeviceSettingsScreenContract {
|
||||
|
||||
fun previewState(
|
||||
device: Device? = previewDevice(),
|
||||
isLoading: Boolean = false,
|
||||
isPendingSync: Boolean = false,
|
||||
configVersionNeedsUpdate: Boolean = false,
|
||||
isConnected: Boolean = true,
|
||||
wifiSsid: String = "Захарова 42"
|
||||
): State = State(
|
||||
device = device,
|
||||
isLoading = isLoading,
|
||||
isPendingSync = isPendingSync,
|
||||
configVersionNeedsUpdate = configVersionNeedsUpdate,
|
||||
isConnected = isConnected,
|
||||
wifiSsid = wifiSsid
|
||||
)
|
||||
|
||||
fun previewDevice(
|
||||
id: String = "12312",
|
||||
name: String = "Захарова 42",
|
||||
model: DeviceModel = DeviceModel.Mobile,
|
||||
deviceAddress: String = "127.0.0.1",
|
||||
configVersion: String = "42",
|
||||
isNarodmonOn: Boolean = false,
|
||||
isLuftdataOn: Boolean = false,
|
||||
hasLocation: Boolean = false,
|
||||
isPublic: Boolean = false
|
||||
): Device = Device(
|
||||
id = id,
|
||||
name = name,
|
||||
model = model,
|
||||
firmwareVersion = "1.0",
|
||||
deviceAddress = deviceAddress,
|
||||
configVersion = configVersion,
|
||||
isNarodmonOn = isNarodmonOn,
|
||||
isLuftdataOn = isLuftdataOn,
|
||||
locationId = null,
|
||||
latitude = if (hasLocation) 53.9 else null,
|
||||
longitude = if (hasLocation) 27.5 else null,
|
||||
isPublic = isPublic,
|
||||
city = null,
|
||||
dataSharingEnabled = false,
|
||||
isOnline = true,
|
||||
onlineFreshness = OnlineFreshness.Fresh,
|
||||
ownerId = null
|
||||
)
|
||||
|
||||
data class State(
|
||||
val device: Device? = null,
|
||||
val isLoading: Boolean = false,
|
||||
val errorMessage: String? = null,
|
||||
val isPendingSync: Boolean = false
|
||||
val isPendingSync: Boolean = false,
|
||||
val configVersionNeedsUpdate: Boolean = false,
|
||||
val isConnected: Boolean = false,
|
||||
val wifiSsid: String = ""
|
||||
)
|
||||
|
||||
sealed interface Action {
|
||||
@@ -16,15 +67,23 @@ object DeviceSettingsScreenContract {
|
||||
data object ShowSuccess : Action
|
||||
data object OpenLocation : Action
|
||||
data object ShowOnMap : Action
|
||||
data object OpenInfoUrl : Action
|
||||
}
|
||||
|
||||
sealed interface Event {
|
||||
data class RenameSubmitted(val name: String) : Event
|
||||
data object RenameRowClicked : Event
|
||||
data object ConfigUpdateClicked : Event
|
||||
data class NarodmonToggled(val enabled: Boolean) : Event
|
||||
data class LuftdataToggled(val enabled: Boolean) : Event
|
||||
data object NarodmonInfoClicked : Event
|
||||
data object LuftdataInfoClicked : Event
|
||||
data class LocationSet(val latitude: Double, val longitude: Double) : Event
|
||||
data object LocationRemoved : Event
|
||||
data class DataSharingToggled(val enabled: Boolean) : Event
|
||||
data object FirmwareUpdateClicked : Event
|
||||
data object OpenLocationClicked : Event
|
||||
data object ShowOnMapClicked : Event
|
||||
data object VisibilityPublishClicked : Event
|
||||
data object VisibilityHideClicked : Event
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package org.db3.airmq.features.device
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
@@ -16,23 +19,32 @@ import kotlinx.coroutines.launch
|
||||
import org.db3.airmq.features.device.usecases.GetDeviceUseCase
|
||||
import org.db3.airmq.features.device.usecases.ObservePendingSyncUseCase
|
||||
import org.db3.airmq.features.device.usecases.RenameDeviceUseCase
|
||||
import org.db3.airmq.features.device.usecases.SetDataSharingUseCase
|
||||
import org.db3.airmq.features.device.usecases.SetDeviceLocationUseCase
|
||||
import org.db3.airmq.features.device.usecases.SetDeviceVisibilityUseCase
|
||||
import org.db3.airmq.features.device.usecases.SetLuftdataUseCase
|
||||
import org.db3.airmq.features.device.usecases.SetNarodmonUseCase
|
||||
import org.db3.airmq.features.device.usecases.TriggerFirmwareUpdateUseCase
|
||||
import javax.inject.Inject
|
||||
import org.db3.airmq.sdk.device.domain.Device
|
||||
import org.db3.airmq.sdk.device.domain.DeviceModel
|
||||
import org.db3.airmq.sdk.device.domain.OnlineFreshness
|
||||
|
||||
@HiltViewModel
|
||||
class DeviceSettingsViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
@HiltViewModel(assistedFactory = DeviceSettingsViewModel.Factory::class)
|
||||
class DeviceSettingsViewModel @AssistedInject constructor(
|
||||
@Assisted private val deviceId: String,
|
||||
private val getDeviceUseCase: GetDeviceUseCase,
|
||||
private val renameDeviceUseCase: RenameDeviceUseCase,
|
||||
private val setDeviceLocationUseCase: SetDeviceLocationUseCase,
|
||||
private val setDataSharingUseCase: SetDataSharingUseCase,
|
||||
private val setNarodmonUseCase: SetNarodmonUseCase,
|
||||
private val setLuftdataUseCase: SetLuftdataUseCase,
|
||||
private val setDeviceVisibilityUseCase: SetDeviceVisibilityUseCase,
|
||||
private val triggerFirmwareUpdateUseCase: TriggerFirmwareUpdateUseCase,
|
||||
private val observePendingSyncUseCase: ObservePendingSyncUseCase
|
||||
) : ViewModel() {
|
||||
|
||||
private val deviceId: String = checkNotNull(savedStateHandle.get<String>("deviceId"))
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(deviceId: String): DeviceSettingsViewModel
|
||||
}
|
||||
|
||||
private val _uiState = MutableStateFlow(DeviceSettingsScreenContract.State())
|
||||
val uiState: StateFlow<DeviceSettingsScreenContract.State> = _uiState.asStateFlow()
|
||||
@@ -48,22 +60,60 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
) { device, hasPending ->
|
||||
device to hasPending
|
||||
}.collect { (device, hasPending) ->
|
||||
val displayDevice = device ?: stubDevice()
|
||||
val configVersionNeedsUpdate = deriveConfigVersionNeedsUpdate(displayDevice)
|
||||
val isConnected = true // Stub: always connected for UI testing
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
device = device,
|
||||
isPendingSync = hasPending
|
||||
device = displayDevice,
|
||||
isPendingSync = hasPending,
|
||||
configVersionNeedsUpdate = configVersionNeedsUpdate,
|
||||
isConnected = isConnected,
|
||||
wifiSsid = if (device == null) "Захарова 42" else it.wifiSsid.takeIf { s -> s.isNotEmpty() } ?: "—"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun stubDevice(): Device = Device(
|
||||
id = "12312",
|
||||
name = "Захарова 42",
|
||||
model = DeviceModel.Mobile,
|
||||
firmwareVersion = "1.0",
|
||||
deviceAddress = "127.0.0.1",
|
||||
configVersion = "42",
|
||||
isNarodmonOn = false,
|
||||
isLuftdataOn = false,
|
||||
locationId = null,
|
||||
latitude = null,
|
||||
longitude = null,
|
||||
isPublic = false,
|
||||
city = null,
|
||||
dataSharingEnabled = false,
|
||||
isOnline = true,
|
||||
onlineFreshness = OnlineFreshness.Fresh,
|
||||
ownerId = null
|
||||
)
|
||||
|
||||
private fun deriveConfigVersionNeedsUpdate(device: Device): Boolean {
|
||||
// Stub: compare configVersion to expected (e.g. 42); for testing assume up-to-date
|
||||
return false
|
||||
}
|
||||
|
||||
fun onEvent(event: DeviceSettingsScreenContract.Event) {
|
||||
when (event) {
|
||||
is DeviceSettingsScreenContract.Event.RenameSubmitted -> renameDevice(event.name)
|
||||
is DeviceSettingsScreenContract.Event.RenameRowClicked -> { /* UI opens dialog */ }
|
||||
is DeviceSettingsScreenContract.Event.ConfigUpdateClicked -> configUpdateStub()
|
||||
is DeviceSettingsScreenContract.Event.NarodmonToggled -> setNarodmon(event.enabled)
|
||||
is DeviceSettingsScreenContract.Event.LuftdataToggled -> setLuftdata(event.enabled)
|
||||
is DeviceSettingsScreenContract.Event.NarodmonInfoClicked,
|
||||
is DeviceSettingsScreenContract.Event.LuftdataInfoClicked -> _actions.tryEmit(
|
||||
DeviceSettingsScreenContract.Action.OpenInfoUrl
|
||||
)
|
||||
is DeviceSettingsScreenContract.Event.LocationSet -> setLocation(event.latitude, event.longitude)
|
||||
is DeviceSettingsScreenContract.Event.LocationRemoved -> removeLocation()
|
||||
is DeviceSettingsScreenContract.Event.DataSharingToggled -> setDataSharing(event.enabled)
|
||||
is DeviceSettingsScreenContract.Event.FirmwareUpdateClicked -> triggerFirmwareUpdate()
|
||||
is DeviceSettingsScreenContract.Event.OpenLocationClicked -> _actions.tryEmit(
|
||||
DeviceSettingsScreenContract.Action.OpenLocation
|
||||
@@ -71,12 +121,24 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
is DeviceSettingsScreenContract.Event.ShowOnMapClicked -> _actions.tryEmit(
|
||||
DeviceSettingsScreenContract.Action.ShowOnMap
|
||||
)
|
||||
is DeviceSettingsScreenContract.Event.VisibilityPublishClicked -> setVisibility(true)
|
||||
is DeviceSettingsScreenContract.Event.VisibilityHideClicked -> setVisibility(false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun configUpdateStub() {
|
||||
viewModelScope.launch {
|
||||
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
|
||||
delay(800)
|
||||
_uiState.update { it.copy(isLoading = false) }
|
||||
_actions.tryEmit(DeviceSettingsScreenContract.Action.ShowSuccess)
|
||||
}
|
||||
}
|
||||
|
||||
private fun renameDevice(name: String) {
|
||||
viewModelScope.launch {
|
||||
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
|
||||
delay(500) // Stub simulation
|
||||
val result = renameDeviceUseCase(deviceId, name)
|
||||
_uiState.update { it.copy(isLoading = false) }
|
||||
if (result.isSuccess) {
|
||||
@@ -91,6 +153,42 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun setNarodmon(enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
|
||||
delay(300)
|
||||
val result = setNarodmonUseCase(deviceId, enabled)
|
||||
_uiState.update { it.copy(isLoading = false) }
|
||||
if (result.isSuccess) {
|
||||
_actions.tryEmit(DeviceSettingsScreenContract.Action.ShowSuccess)
|
||||
} else {
|
||||
_actions.tryEmit(
|
||||
DeviceSettingsScreenContract.Action.ShowError(
|
||||
result.exceptionOrNull()?.message ?: "Failed to update Narodmon"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLuftdata(enabled: Boolean) {
|
||||
viewModelScope.launch {
|
||||
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
|
||||
delay(300)
|
||||
val result = setLuftdataUseCase(deviceId, enabled)
|
||||
_uiState.update { it.copy(isLoading = false) }
|
||||
if (result.isSuccess) {
|
||||
_actions.tryEmit(DeviceSettingsScreenContract.Action.ShowSuccess)
|
||||
} else {
|
||||
_actions.tryEmit(
|
||||
DeviceSettingsScreenContract.Action.ShowError(
|
||||
result.exceptionOrNull()?.message ?: "Failed to update Sensor.community"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setLocation(latitude: Double, longitude: Double) {
|
||||
viewModelScope.launch {
|
||||
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
|
||||
@@ -125,17 +223,16 @@ class DeviceSettingsViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun setDataSharing(enabled: Boolean) {
|
||||
private fun setVisibility(isPublic: Boolean) {
|
||||
viewModelScope.launch {
|
||||
_uiState.update { it.copy(isLoading = true, errorMessage = null) }
|
||||
val result = setDataSharingUseCase(deviceId, enabled)
|
||||
delay(300)
|
||||
val result = setDeviceVisibilityUseCase(deviceId, isPublic)
|
||||
_uiState.update { it.copy(isLoading = false) }
|
||||
if (result.isSuccess) {
|
||||
_actions.tryEmit(DeviceSettingsScreenContract.Action.ShowSuccess)
|
||||
} else {
|
||||
if (result.isFailure) {
|
||||
_actions.tryEmit(
|
||||
DeviceSettingsScreenContract.Action.ShowError(
|
||||
result.exceptionOrNull()?.message ?: "Failed to update data sharing"
|
||||
result.exceptionOrNull()?.message ?: "Failed to update visibility"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.db3.airmq.features.device.usecases
|
||||
|
||||
import org.db3.airmq.sdk.device.domain.DeviceRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Use case to set device visibility (publish/hide location on map).
|
||||
* Only effective when device has location.
|
||||
*/
|
||||
class SetDeviceVisibilityUseCase @Inject constructor(
|
||||
private val repository: DeviceRepository
|
||||
) {
|
||||
suspend operator fun invoke(deviceId: String, isPublic: Boolean): Result<Unit> =
|
||||
repository.setVisibility(deviceId, isPublic)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.db3.airmq.features.device.usecases
|
||||
|
||||
import org.db3.airmq.sdk.device.domain.DeviceRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Use case to enable or disable Sensor.community (Luftdata) data sharing for a device.
|
||||
*/
|
||||
class SetLuftdataUseCase @Inject constructor(
|
||||
private val repository: DeviceRepository
|
||||
) {
|
||||
suspend operator fun invoke(deviceId: String, enabled: Boolean): Result<Unit> =
|
||||
repository.setLuftdata(deviceId, enabled)
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package org.db3.airmq.features.device.usecases
|
||||
|
||||
import org.db3.airmq.sdk.device.domain.DeviceRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Use case to enable or disable Narodmon.ru data sharing for a device.
|
||||
*/
|
||||
class SetNarodmonUseCase @Inject constructor(
|
||||
private val repository: DeviceRepository
|
||||
) {
|
||||
suspend operator fun invoke(deviceId: String, enabled: Boolean): Result<Unit> =
|
||||
repository.setNarodmon(deviceId, enabled)
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class ManageViewModel @Inject constructor(
|
||||
return DeviceItem(
|
||||
id = id,
|
||||
name = name,
|
||||
extra = model.ifEmpty { "mobile" },
|
||||
extra = model.displayName,
|
||||
status = statusText,
|
||||
hasLocation = hasLocation()
|
||||
)
|
||||
|
||||
@@ -13,6 +13,8 @@ val LegacyOnBackground = Color(0xFF000000)
|
||||
val LegacyOnSurface = Color(0xFF000000)
|
||||
val LegacyOutline = Color(0x61000000)
|
||||
val LegacyOutlineLight = Color(0x3B000000)
|
||||
val LegacyBlack12 = Color(0x1F000000)
|
||||
val LegacyBlack38 = Color(0x61000000)
|
||||
val LegacyNavSelected = Color(0xFFFFFFFF)
|
||||
val LegacyNavUnselected = Color(0x8AFFFFFF)
|
||||
val LegacyNavContainer = Color(0xFF295989)
|
||||
|
||||
10
app/src/main/res/drawable/circle_indicator.xml
Normal file
10
app/src/main/res/drawable/circle_indicator.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="10dp"
|
||||
android:height="10dp"
|
||||
android:viewportWidth="10"
|
||||
android:viewportHeight="10">
|
||||
<path
|
||||
android:fillColor="@color/sensorOrange"
|
||||
android:pathData="M5,5m-5,0a5,5 0,1 1,10 0a5,5 0,1 1,-10 0"/>
|
||||
</vector>
|
||||
21
app/src/main/res/drawable/ic_device_basic_active_10.xml
Normal file
21
app/src/main/res/drawable/ic_device_basic_active_10.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.28C1,11.28 0,12.28 0,25.58c0,13.29 1,14.29 14.29,14.29c13.3,0 14.3,-1 14.3,-14.29C28.59,12.28 27.59,11.28 14.29,11.28zM24.56,18.44c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86c0.79,-0.79 2.08,-0.79 2.86,0C25.35,16.37 25.35,17.65 24.56,18.44z"
|
||||
android:strokeAlpha="0.54"
|
||||
android:fillAlpha="0.54"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M39.85,18.81v0.05c0,0.03 -0.01,0.05 -0.01,0.08c0,0.02 -0.01,0.04 -0.02,0.07c-0.17,0.76 -0.94,1.23 -1.7,1.05c-0.07,-0.01 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.92,-0.76 -0.9,-1.35v-0.08c0,-0.01 0,-0.01 0,-0.01c0.01,-0.05 0.02,-0.1 0.02,-0.15c0.31,-2.26 0.03,-4.5 -0.73,-6.54c-0.12,-0.32 -0.25,-0.64 -0.4,-0.96c-0.06,-0.13 -0.12,-0.26 -0.19,-0.38c-0.04,-0.1 -0.09,-0.19 -0.14,-0.28c-0.05,-0.1 -0.1,-0.2 -0.16,-0.29c-0.01,-0.03 -0.02,-0.05 -0.03,-0.07c-0.05,-0.07 -0.09,-0.15 -0.14,-0.23c-0.04,-0.08 -0.1,-0.17 -0.15,-0.25s-0.1,-0.16 -0.15,-0.24C34.91,9.08 34.85,9 34.8,8.92c-0.33,-0.48 -0.68,-0.94 -1.07,-1.37c-0.06,-0.07 -0.12,-0.14 -0.19,-0.21c-0.14,-0.15 -0.29,-0.3 -0.44,-0.45c-0.05,-0.06 -0.11,-0.12 -0.17,-0.17c-0.07,-0.07 -0.14,-0.13 -0.21,-0.2c-0.1,-0.09 -0.2,-0.17 -0.29,-0.26C32.32,6.17 32.22,6.09 32.11,6C32,5.91 31.9,5.83 31.79,5.75c-0.86,-0.66 -1.81,-1.22 -2.83,-1.66c-0.12,-0.06 -0.25,-0.11 -0.38,-0.16c-0.12,-0.05 -0.25,-0.1 -0.38,-0.15c-0.11,-0.04 -0.23,-0.08 -0.34,-0.11c-0.02,-0.01 -0.03,-0.02 -0.05,-0.02c-1.36,-0.46 -2.81,-0.7 -4.28,-0.7c-0.79,0 -1.58,0.07 -2.37,0.21c-0.02,0 -0.05,0.01 -0.07,0.01c-0.68,0.1 -1.36,-0.31 -1.56,-0.99l-0.02,-0.07c-0.19,-0.74 0.25,-1.5 1,-1.71c0.03,0 0.06,-0.01 0.09,-0.02c0.03,0 0.06,-0.01 0.08,-0.01c2.77,-0.49 5.53,-0.24 8.06,0.61c0.03,0 0.05,0.01 0.07,0.02c0.13,0.04 0.26,0.09 0.38,0.13c0.13,0.05 0.27,0.1 0.4,0.16c0.03,0.01 0.07,0.02 0.1,0.04c1.98,0.8 3.79,1.98 5.32,3.47l0.37,0.37c0.11,0.11 0.21,0.22 0.32,0.34c0.01,0.01 0.03,0.03 0.04,0.05c0.07,0.07 0.14,0.15 0.2,0.22c0.05,0.06 0.1,0.11 0.15,0.17c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.14,0.19 0.21,0.28C36.87,6.94 37,7.12 37.12,7.3c0.02,0.02 0.03,0.05 0.05,0.07c0.05,0.09 0.11,0.17 0.16,0.26c0.13,0.19 0.25,0.39 0.37,0.59c0.06,0.1 0.12,0.2 0.18,0.3c0.08,0.15 0.16,0.3 0.24,0.45c0.05,0.09 0.1,0.19 0.15,0.29c0.01,0.01 0.01,0.02 0.02,0.03c0.07,0.15 0.14,0.3 0.21,0.45c0,0.01 0.01,0.02 0.01,0.03c0.14,0.3 0.27,0.62 0.39,0.93c0.01,0.01 0.01,0.02 0.01,0.03c0.01,0.01 0.01,0.01 0.01,0.02c0.01,0.02 0.02,0.05 0.03,0.07C39.88,13.31 40.23,16.04 39.85,18.81z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M34.12,17.63v0.07c-0.01,0.06 -0.02,0.13 -0.03,0.19c-0.18,0.77 -0.95,1.24 -1.71,1.06c-0.06,-0.02 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.91,-0.76 -0.9,-1.35c0.01,-0.06 0.01,-0.12 0.02,-0.18c0.12,-1.83 -0.39,-3.6 -1.38,-5.05c-0.23,-0.35 -0.49,-0.67 -0.77,-0.97l-0.18,-0.18c-0.09,-0.1 -0.19,-0.18 -0.28,-0.27c-0.07,-0.07 -0.15,-0.13 -0.22,-0.19c-0.06,-0.06 -0.13,-0.11 -0.2,-0.16c-0.04,-0.03 -0.08,-0.07 -0.12,-0.1c-0.11,-0.09 -0.23,-0.17 -0.35,-0.25c-0.06,-0.04 -0.12,-0.08 -0.18,-0.12c-0.01,0 -0.01,0 -0.01,0c-0.19,-0.12 -0.38,-0.24 -0.58,-0.34c-0.06,-0.03 -0.13,-0.07 -0.19,-0.1c-0.01,0 -0.02,-0.01 -0.03,-0.01c-0.13,-0.06 -0.25,-0.12 -0.38,-0.17c-0.01,-0.01 -0.02,-0.01 -0.02,-0.01c-1.25,-0.53 -2.63,-0.74 -4.03,-0.58c-0.69,0.1 -1.35,-0.33 -1.54,-1.02c-0.21,-0.76 0.23,-1.54 0.99,-1.75c0.03,-0.01 0.07,-0.01 0.1,-0.02c0.08,-0.01 0.15,-0.02 0.22,-0.02c1.77,-0.2 3.51,0.05 5.1,0.68c0.01,0 0.01,0 0.03,0.01c0.18,0.07 0.36,0.14 0.54,0.22c1.02,0.47 1.97,1.1 2.81,1.87c0.15,0.14 0.3,0.28 0.44,0.43c0.07,0.07 0.14,0.14 0.2,0.21c0.24,0.26 0.46,0.53 0.68,0.81c0.08,0.11 0.16,0.22 0.24,0.34c0.04,0.05 0.08,0.11 0.12,0.17s0.08,0.11 0.12,0.17c0.03,0.06 0.07,0.12 0.11,0.18c0.03,0.05 0.06,0.11 0.09,0.16c0.1,0.16 0.19,0.33 0.28,0.5c0.05,0.09 0.09,0.17 0.13,0.26c0,0 0,0 0.01,0.01c0.04,0.09 0.09,0.19 0.13,0.29c0.01,0.02 0.02,0.04 0.03,0.06c0.01,0.03 0.02,0.06 0.04,0.09C33.94,14.09 34.26,15.84 34.12,17.63z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
11
app/src/main/res/drawable/ic_device_basic_inactive_10.xml
Normal file
11
app/src/main/res/drawable/ic_device_basic_inactive_10.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.41C1,11.41 0,12.41 0,25.71C0,39 1,40 14.29,40c13.3,0 14.3,-1 14.3,-14.29C28.59,12.41 27.59,11.41 14.29,11.41zM24.56,18.57c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86c0.79,-0.79 2.08,-0.79 2.86,0C25.35,16.5 25.35,17.78 24.56,18.57z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
21
app/src/main/res/drawable/ic_device_custom_active_10.xml
Normal file
21
app/src/main/res/drawable/ic_device_custom_active_10.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.28C1,11.28 0,12.28 0,25.58c0,13.29 1,14.29 14.29,14.29c13.3,0 14.3,-1 14.3,-14.29C28.59,12.28 27.59,11.28 14.29,11.28zM18.79,27.57c0,0.21 -0.02,0.4 -0.04,0.59l1.27,0.99c0.11,0.09 0.14,0.25 0.07,0.38l-1.2,2.08c-0.07,0.13 -0.23,0.19 -0.37,0.13l-1.49,-0.6c-0.31,0.24 -0.65,0.44 -1.02,0.59l-0.22,1.59c-0.02,0.14 -0.15,0.25 -0.3,0.25h-2.4c-0.15,0 -0.27,-0.11 -0.29,-0.25l-0.23,-1.59c-0.37,-0.15 -0.7,-0.35 -1.01,-0.59l-1.5,0.6c-0.13,0.05 -0.29,0 -0.36,-0.13l-1.2,-2.08c-0.08,-0.13 -0.05,-0.29 0.07,-0.38l1.26,-0.99c-0.02,-0.19 -0.04,-0.39 -0.04,-0.59c0,-0.2 0.02,-0.39 0.04,-0.59l-1.26,-0.99c-0.12,-0.09 -0.15,-0.25 -0.07,-0.38l1.2,-2.08c0.07,-0.13 0.22,-0.18 0.36,-0.13l1.5,0.6c0.31,-0.23 0.64,-0.44 1.01,-0.59l0.23,-1.59c0.02,-0.14 0.14,-0.25 0.29,-0.25h2.4c0.15,0 0.28,0.11 0.3,0.25l0.22,1.59c0.37,0.15 0.71,0.35 1.02,0.59l1.49,-0.6c0.13,-0.05 0.3,0 0.37,0.13l1.2,2.08c0.07,0.13 0.04,0.29 -0.07,0.38l-1.27,0.99C18.77,27.18 18.79,27.37 18.79,27.57zM24.56,18.44c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86c0.79,-0.79 2.08,-0.79 2.86,0C25.35,16.37 25.35,17.65 24.56,18.44zM14.29,25.47c-1.16,0 -2.1,0.94 -2.1,2.1c0,1.16 0.94,2.1 2.1,2.1s2.1,-0.94 2.1,-2.1C16.39,26.41 15.45,25.47 14.29,25.47z"
|
||||
android:strokeAlpha="0.54"
|
||||
android:fillAlpha="0.54"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M39.84,18.81v0.05c0,0.03 -0.01,0.05 -0.01,0.08c0,0.02 -0.01,0.04 -0.02,0.07c-0.17,0.76 -0.94,1.23 -1.7,1.05c-0.07,-0.01 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.92,-0.76 -0.9,-1.35v-0.08c0,-0.01 0,-0.01 0,-0.01c0.01,-0.05 0.02,-0.1 0.02,-0.15c0.31,-2.26 0.03,-4.5 -0.73,-6.54c-0.12,-0.32 -0.25,-0.64 -0.4,-0.96c-0.06,-0.13 -0.12,-0.26 -0.19,-0.38c-0.04,-0.1 -0.09,-0.19 -0.14,-0.28c-0.05,-0.1 -0.1,-0.2 -0.16,-0.29c-0.01,-0.03 -0.02,-0.05 -0.03,-0.07c-0.05,-0.07 -0.09,-0.15 -0.14,-0.23c-0.04,-0.08 -0.1,-0.17 -0.15,-0.25S35,9.25 34.95,9.17c-0.05,-0.08 -0.11,-0.16 -0.16,-0.24c-0.33,-0.48 -0.68,-0.94 -1.07,-1.37c-0.06,-0.07 -0.12,-0.14 -0.19,-0.21c-0.14,-0.15 -0.29,-0.3 -0.44,-0.45c-0.05,-0.06 -0.11,-0.12 -0.17,-0.17c-0.07,-0.07 -0.14,-0.13 -0.21,-0.2c-0.1,-0.09 -0.2,-0.17 -0.29,-0.26c-0.11,-0.09 -0.21,-0.17 -0.32,-0.26c-0.11,-0.09 -0.21,-0.17 -0.32,-0.25c-0.86,-0.66 -1.81,-1.22 -2.83,-1.66c-0.12,-0.06 -0.25,-0.11 -0.38,-0.16c-0.12,-0.05 -0.25,-0.1 -0.38,-0.15c-0.11,-0.04 -0.23,-0.08 -0.34,-0.11c-0.02,-0.01 -0.03,-0.02 -0.05,-0.02c-1.36,-0.46 -2.81,-0.7 -4.28,-0.7c-0.79,0 -1.58,0.07 -2.37,0.21c-0.02,0 -0.05,0.01 -0.07,0.01c-0.68,0.1 -1.36,-0.31 -1.56,-0.99L19.5,2.12c-0.19,-0.74 0.25,-1.5 1,-1.71c0.03,0 0.06,-0.01 0.09,-0.02c0.03,0 0.06,-0.01 0.08,-0.01c2.77,-0.49 5.53,-0.24 8.06,0.61c0.03,0 0.05,0.01 0.07,0.02c0.13,0.04 0.26,0.09 0.38,0.13c0.13,0.05 0.27,0.1 0.4,0.16c0.03,0.01 0.07,0.02 0.1,0.04c1.98,0.8 3.79,1.98 5.32,3.47l0.37,0.37c0.11,0.11 0.21,0.22 0.32,0.34c0.01,0.01 0.03,0.03 0.04,0.05c0.07,0.07 0.14,0.15 0.2,0.22c0.05,0.06 0.1,0.11 0.15,0.17c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.14,0.19 0.21,0.28c0.13,0.17 0.26,0.35 0.38,0.53c0.02,0.02 0.03,0.05 0.05,0.07c0.05,0.09 0.11,0.17 0.16,0.26c0.13,0.19 0.25,0.39 0.37,0.59c0.06,0.1 0.12,0.2 0.18,0.3c0.08,0.15 0.16,0.3 0.24,0.45c0.05,0.09 0.1,0.19 0.15,0.29c0.01,0.01 0.01,0.02 0.02,0.03c0.07,0.15 0.14,0.3 0.21,0.45c0,0.01 0.01,0.02 0.01,0.03c0.14,0.3 0.27,0.62 0.39,0.93c0.01,0.01 0.01,0.02 0.01,0.03c0.01,0.01 0.01,0.01 0.01,0.02c0.01,0.02 0.02,0.05 0.03,0.07C39.87,13.31 40.22,16.04 39.84,18.81z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M34.11,17.63v0.07c-0.01,0.06 -0.02,0.13 -0.03,0.19c-0.18,0.77 -0.95,1.24 -1.71,1.06c-0.06,-0.02 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.91,-0.76 -0.9,-1.35c0.01,-0.06 0.01,-0.12 0.02,-0.18c0.12,-1.83 -0.39,-3.6 -1.38,-5.05c-0.23,-0.35 -0.49,-0.67 -0.77,-0.97l-0.18,-0.18c-0.09,-0.1 -0.19,-0.18 -0.28,-0.27c-0.07,-0.07 -0.15,-0.13 -0.22,-0.19c-0.06,-0.06 -0.13,-0.11 -0.2,-0.16c-0.04,-0.03 -0.08,-0.07 -0.12,-0.1c-0.11,-0.09 -0.23,-0.17 -0.35,-0.25c-0.06,-0.04 -0.12,-0.08 -0.18,-0.12c-0.01,0 -0.01,0 -0.01,0c-0.19,-0.12 -0.38,-0.24 -0.58,-0.34c-0.06,-0.03 -0.13,-0.07 -0.19,-0.1c-0.01,0 -0.02,-0.01 -0.03,-0.01c-0.13,-0.06 -0.25,-0.12 -0.38,-0.17c-0.01,-0.01 -0.02,-0.01 -0.02,-0.01c-1.25,-0.53 -2.63,-0.74 -4.03,-0.58c-0.69,0.1 -1.35,-0.33 -1.54,-1.02c-0.21,-0.76 0.23,-1.54 0.99,-1.75c0.03,-0.01 0.07,-0.01 0.1,-0.02c0.08,-0.01 0.15,-0.02 0.22,-0.02c1.77,-0.2 3.51,0.05 5.1,0.68c0.01,0 0.01,0 0.03,0.01c0.18,0.07 0.36,0.14 0.54,0.22c1.02,0.47 1.97,1.1 2.81,1.87c0.15,0.14 0.3,0.28 0.44,0.43c0.07,0.07 0.14,0.14 0.2,0.21c0.24,0.26 0.46,0.53 0.68,0.81c0.08,0.11 0.16,0.22 0.24,0.34c0.04,0.05 0.08,0.11 0.12,0.17s0.08,0.11 0.12,0.17c0.03,0.06 0.07,0.12 0.11,0.18c0.03,0.05 0.06,0.11 0.09,0.16c0.1,0.16 0.19,0.33 0.28,0.5c0.05,0.09 0.09,0.17 0.13,0.26c0,0 0,0 0.01,0.01c0.04,0.09 0.09,0.19 0.13,0.29c0.01,0.02 0.02,0.04 0.03,0.06c0.01,0.03 0.02,0.06 0.04,0.09C33.93,14.09 34.25,15.84 34.11,17.63z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
11
app/src/main/res/drawable/ic_device_custom_inactive_10.xml
Normal file
11
app/src/main/res/drawable/ic_device_custom_inactive_10.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.41C1,11.41 0,12.41 0,25.71C0,39 1,40 14.29,40c13.3,0 14.3,-1 14.3,-14.29C28.59,12.41 27.59,11.41 14.29,11.41zM18.79,27.7c0,0.21 -0.02,0.4 -0.04,0.59l1.27,0.99c0.11,0.09 0.14,0.25 0.07,0.38l-1.2,2.08c-0.07,0.13 -0.23,0.19 -0.37,0.13l-1.49,-0.6c-0.31,0.24 -0.65,0.44 -1.02,0.59l-0.22,1.59c-0.02,0.14 -0.15,0.25 -0.3,0.25h-2.4c-0.15,0 -0.27,-0.11 -0.29,-0.25l-0.23,-1.59c-0.37,-0.15 -0.7,-0.35 -1.01,-0.59l-1.5,0.6c-0.13,0.05 -0.29,0 -0.36,-0.13l-1.2,-2.08c-0.08,-0.13 -0.05,-0.29 0.07,-0.38l1.26,-0.99C9.81,28.1 9.79,27.9 9.79,27.7c0,-0.2 0.02,-0.39 0.04,-0.59l-1.26,-0.99c-0.12,-0.09 -0.15,-0.25 -0.07,-0.38l1.2,-2.08c0.07,-0.13 0.22,-0.18 0.36,-0.13l1.5,0.6c0.31,-0.23 0.64,-0.44 1.01,-0.59l0.23,-1.59c0.02,-0.14 0.14,-0.25 0.29,-0.25h2.4c0.15,0 0.28,0.11 0.3,0.25l0.22,1.59c0.37,0.15 0.71,0.35 1.02,0.59l1.49,-0.6c0.13,-0.05 0.3,0 0.37,0.13l1.2,2.08c0.07,0.13 0.04,0.29 -0.07,0.38l-1.27,0.99C18.77,27.31 18.79,27.5 18.79,27.7zM24.56,18.57c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86c0.79,-0.79 2.08,-0.79 2.86,0C25.35,16.5 25.35,17.78 24.56,18.57zM14.29,25.6c-1.16,0 -2.1,0.94 -2.1,2.1c0,1.16 0.94,2.1 2.1,2.1s2.1,-0.94 2.1,-2.1C16.39,26.54 15.45,25.6 14.29,25.6z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
23
app/src/main/res/drawable/ic_device_gas_active_10.xml
Normal file
23
app/src/main/res/drawable/ic_device_gas_active_10.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40"
|
||||
tools:ignore="UnusedResources">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.3,11.31c-13.26,0 -14.25,1 -14.25,14.26c0,13.26 1,14.25 14.25,14.25c13.27,0 14.26,-1 14.26,-14.25C28.57,12.3 27.57,11.31 14.3,11.31zM22.17,29.73c-0.35,1.15 -1.37,1.95 -2.48,2.06c-1.02,0.1 -1.78,-0.41 -1.99,-0.56c-0.2,0.18 -0.49,0.41 -0.85,0.65c-0.09,0.06 -0.33,0.2 -0.61,0.33c-0.87,0.39 -1.69,0.44 -1.91,0.45c-0.5,0.02 -1.22,-0.03 -2.03,-0.34c-0.68,-0.26 -1.17,-0.62 -1.48,-0.88c-0.17,0.11 -0.43,0.25 -0.78,0.35c-0.31,0.1 -0.95,0.29 -1.69,0.08c-0.6,-0.17 -0.98,-0.53 -1.14,-0.68c-0.17,-0.16 -0.82,-0.81 -0.89,-1.84c-0.04,-0.67 0.2,-1.17 0.28,-1.36c0.23,-0.46 0.53,-0.77 0.72,-0.94c0.18,-0.14 0.43,-0.34 0.82,-0.46c0.41,-0.11 0.76,-0.07 0.94,-0.03c0.1,-0.45 0.32,-1.22 0.89,-2c0.67,-0.93 1.47,-1.39 1.72,-1.53c1.88,-1.02 3.74,-0.45 3.97,-0.38c0.02,0.01 0.05,0.02 0.08,0.03c0.36,0.12 1.58,0.57 2.47,1.78c0.08,0.12 0.22,0.31 0.36,0.57c-0.22,-0.01 -0.76,0.02 -1.3,0.37c-0.4,0.26 -0.62,0.58 -0.73,0.77c-0.32,-0.17 -1.21,-0.59 -2.26,-0.37c-0.31,0.07 -0.87,0.19 -1.33,0.67c-0.43,0.44 -0.55,0.94 -0.57,1.06c-0.13,0.61 0.05,1.1 0.13,1.28c0.01,0.02 0.02,0.04 0.02,0.05c-0.02,-0.14 -0.03,-0.37 0.03,-0.63c0.01,-0.1 0.08,-0.38 0.29,-0.68c0.41,-0.58 1.06,-0.75 1.27,-0.8c0.14,-0.03 0.58,-0.14 1.13,-0.02c0.87,0.17 1.41,0.72 1.6,0.94c0.09,-0.22 0.32,-0.68 0.81,-1.08c0.14,-0.11 0.59,-0.45 1.24,-0.57c1.23,-0.21 2.16,0.49 2.25,0.56C22.04,27.27 22.53,28.52 22.17,29.73zM24.55,18.45c-0.78,0.79 -2.06,0.79 -2.85,0c-0.79,-0.79 -0.79,-2.06 0,-2.85c0.79,-0.79 2.07,-0.79 2.85,0C25.34,16.38 25.34,17.66 24.55,18.45z"
|
||||
android:strokeAlpha="0.54"
|
||||
android:fillAlpha="0.54"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M39.8,18.81v0.05c0,0.03 -0.01,0.05 -0.01,0.08c0,0.02 -0.01,0.04 -0.02,0.07c-0.17,0.76 -0.94,1.23 -1.7,1.05c-0.07,-0.01 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.92,-0.76 -0.9,-1.35v-0.08c0,-0.01 0,-0.01 0,-0.01c0.01,-0.05 0.02,-0.1 0.02,-0.15c0.31,-2.25 0.03,-4.49 -0.73,-6.52c-0.12,-0.32 -0.25,-0.64 -0.4,-0.96c-0.06,-0.13 -0.12,-0.26 -0.19,-0.38c-0.04,-0.1 -0.09,-0.19 -0.14,-0.28c-0.05,-0.1 -0.1,-0.2 -0.16,-0.29c-0.01,-0.03 -0.02,-0.05 -0.03,-0.07c-0.05,-0.07 -0.09,-0.15 -0.14,-0.23c-0.04,-0.08 -0.1,-0.17 -0.15,-0.25s-0.1,-0.16 -0.15,-0.24c-0.05,-0.08 -0.11,-0.16 -0.16,-0.24c-0.33,-0.48 -0.68,-0.94 -1.07,-1.37c-0.06,-0.07 -0.12,-0.14 -0.19,-0.21c-0.14,-0.15 -0.29,-0.3 -0.44,-0.45c-0.05,-0.06 -0.11,-0.12 -0.17,-0.17c-0.07,-0.07 -0.14,-0.13 -0.21,-0.2c-0.1,-0.09 -0.2,-0.17 -0.29,-0.26c-0.11,-0.09 -0.21,-0.17 -0.32,-0.26c-0.11,-0.09 -0.21,-0.17 -0.32,-0.25c-0.86,-0.66 -1.81,-1.22 -2.82,-1.66c-0.12,-0.06 -0.25,-0.11 -0.38,-0.16c-0.12,-0.05 -0.25,-0.1 -0.38,-0.15c-0.11,-0.04 -0.23,-0.08 -0.34,-0.11c-0.02,-0.01 -0.03,-0.02 -0.05,-0.02C26.43,3.24 24.99,3 23.52,3c-0.79,0 -1.58,0.07 -2.36,0.21c-0.02,0 -0.05,0.01 -0.07,0.01c-0.68,0.1 -1.36,-0.31 -1.56,-0.99l-0.02,-0.07c-0.19,-0.74 0.25,-1.5 1,-1.71c0.03,0 0.06,-0.01 0.09,-0.02c0.03,0 0.06,-0.01 0.08,-0.01c2.76,-0.49 5.52,-0.24 8.04,0.61c0.03,0 0.05,0.01 0.07,0.02c0.13,0.04 0.26,0.09 0.38,0.13c0.13,0.05 0.27,0.1 0.4,0.16c0.03,0.01 0.07,0.02 0.1,0.04c1.98,0.8 3.78,1.98 5.31,3.46l0.37,0.37c0.11,0.11 0.21,0.22 0.32,0.34c0.01,0.01 0.03,0.03 0.04,0.05c0.07,0.07 0.14,0.15 0.2,0.22c0.05,0.06 0.1,0.11 0.15,0.17c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.14,0.19 0.21,0.28c0.13,0.17 0.26,0.35 0.38,0.53c0.02,0.02 0.03,0.05 0.05,0.07c0.05,0.09 0.11,0.17 0.16,0.26c0.13,0.19 0.25,0.39 0.37,0.59c0.06,0.1 0.12,0.2 0.18,0.3c0.08,0.15 0.16,0.3 0.24,0.45c0.05,0.09 0.1,0.19 0.15,0.29c0.01,0.01 0.01,0.02 0.02,0.03c0.07,0.15 0.14,0.3 0.21,0.45c0,0.01 0.01,0.02 0.01,0.03c0.14,0.3 0.27,0.62 0.39,0.93c0.01,0.01 0.01,0.02 0.01,0.03c0.01,0.01 0.01,0.01 0.01,0.02c0.01,0.02 0.02,0.05 0.03,0.07C39.83,13.32 40.18,16.04 39.8,18.81z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M34.08,17.63v0.07c-0.01,0.06 -0.02,0.13 -0.03,0.19c-0.18,0.77 -0.95,1.24 -1.71,1.06c-0.06,-0.02 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.91,-0.76 -0.9,-1.35c0.01,-0.06 0.01,-0.12 0.02,-0.18c0.12,-1.83 -0.39,-3.59 -1.38,-5.04c-0.23,-0.35 -0.49,-0.67 -0.77,-0.97l-0.18,-0.18c-0.09,-0.1 -0.19,-0.18 -0.28,-0.27c-0.07,-0.07 -0.15,-0.13 -0.22,-0.19c-0.06,-0.06 -0.13,-0.11 -0.2,-0.16c-0.04,-0.03 -0.08,-0.07 -0.12,-0.1c-0.11,-0.09 -0.23,-0.17 -0.35,-0.25c-0.06,-0.04 -0.12,-0.08 -0.18,-0.12c-0.01,0 -0.01,0 -0.01,0c-0.19,-0.12 -0.38,-0.24 -0.58,-0.34c-0.06,-0.03 -0.13,-0.07 -0.19,-0.1c-0.01,0 -0.02,-0.01 -0.03,-0.01c-0.13,-0.06 -0.25,-0.12 -0.38,-0.17c-0.01,-0.01 -0.02,-0.01 -0.02,-0.01c-1.25,-0.53 -2.62,-0.74 -4.02,-0.58c-0.69,0.1 -1.35,-0.33 -1.54,-1.02c-0.21,-0.76 0.23,-1.54 0.99,-1.75c0.03,-0.01 0.07,-0.01 0.1,-0.02c0.08,-0.01 0.15,-0.02 0.22,-0.02c1.77,-0.2 3.5,0.05 5.09,0.68c0.01,0 0.01,0 0.03,0.01c0.18,0.07 0.36,0.14 0.54,0.22c1.02,0.47 1.97,1.1 2.8,1.87c0.15,0.14 0.3,0.28 0.44,0.43c0.07,0.07 0.14,0.14 0.2,0.21c0.24,0.26 0.46,0.53 0.68,0.81c0.08,0.11 0.16,0.22 0.24,0.34c0.04,0.05 0.08,0.11 0.12,0.17c0.04,0.06 0.08,0.11 0.12,0.17c0.03,0.06 0.07,0.12 0.11,0.18c0.03,0.05 0.06,0.11 0.09,0.16c0.1,0.16 0.19,0.33 0.28,0.5c0.05,0.09 0.09,0.17 0.13,0.26c0,0 0,0 0.01,0.01c0.04,0.09 0.09,0.19 0.13,0.29c0.01,0.02 0.02,0.04 0.03,0.06c0.01,0.03 0.02,0.06 0.04,0.09C33.9,14.1 34.22,15.84 34.08,17.63z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
13
app/src/main/res/drawable/ic_device_gas_inactive_10.xml
Normal file
13
app/src/main/res/drawable/ic_device_gas_inactive_10.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40"
|
||||
tools:ignore="UnusedResources">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.41C1,11.41 0,12.41 0,25.71C0,39 1,40 14.29,40c13.3,0 14.3,-1 14.3,-14.29C28.59,12.41 27.59,11.41 14.29,11.41zM22.18,29.88c-0.35,1.15 -1.37,1.95 -2.49,2.07c-1.02,0.1 -1.78,-0.41 -2,-0.56c-0.2,0.18 -0.49,0.41 -0.85,0.65c-0.09,0.06 -0.33,0.2 -0.61,0.33c-0.87,0.39 -1.69,0.44 -1.91,0.45c-0.5,0.02 -1.22,-0.03 -2.04,-0.34c-0.68,-0.26 -1.17,-0.62 -1.48,-0.88c-0.17,0.11 -0.43,0.25 -0.78,0.35c-0.31,0.1 -0.95,0.29 -1.69,0.08c-0.6,-0.17 -0.98,-0.53 -1.14,-0.68c-0.17,-0.16 -0.82,-0.81 -0.89,-1.84c-0.04,-0.67 0.2,-1.17 0.28,-1.36c0.23,-0.46 0.53,-0.77 0.72,-0.94c0.18,-0.14 0.43,-0.34 0.82,-0.46c0.41,-0.11 0.76,-0.07 0.94,-0.03c0.1,-0.45 0.32,-1.22 0.89,-2.01c0.67,-0.93 1.47,-1.39 1.72,-1.53c1.88,-1.02 3.75,-0.45 3.98,-0.38c0.02,0.01 0.05,0.02 0.08,0.03c0.36,0.12 1.58,0.57 2.48,1.78c0.08,0.12 0.22,0.31 0.36,0.57c-0.22,-0.01 -0.76,0.02 -1.3,0.37c-0.4,0.26 -0.62,0.58 -0.73,0.77c-0.32,-0.17 -1.21,-0.59 -2.27,-0.37c-0.31,0.07 -0.87,0.19 -1.33,0.67c-0.43,0.44 -0.55,0.94 -0.57,1.06c-0.13,0.61 0.05,1.1 0.13,1.28c0.01,0.02 0.02,0.04 0.02,0.05c-0.02,-0.14 -0.03,-0.37 0.03,-0.63c0.01,-0.1 0.08,-0.38 0.29,-0.68c0.41,-0.58 1.06,-0.75 1.27,-0.8c0.14,-0.03 0.58,-0.14 1.13,-0.02c0.87,0.17 1.41,0.72 1.6,0.94c0.09,-0.22 0.32,-0.68 0.81,-1.08c0.14,-0.11 0.59,-0.45 1.24,-0.57c1.23,-0.21 2.17,0.49 2.26,0.56C22.05,27.41 22.54,28.67 22.18,29.88zM24.56,18.57c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86c0.79,-0.79 2.08,-0.79 2.86,0C25.35,16.5 25.35,17.78 24.56,18.57z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
21
app/src/main/res/drawable/ic_device_mobile_active_10.xml
Normal file
21
app/src/main/res/drawable/ic_device_mobile_active_10.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.28C1,11.28 0,12.28 0,25.58c0,13.29 1,14.29 14.29,14.29c13.3,0 14.3,-1 14.3,-14.29C28.59,12.28 27.59,11.28 14.29,11.28zM22.3,28.53c0,0.82 -0.66,1.49 -1.49,1.49h-0.75v0.3c0,0.82 -0.65,1.49 -1.49,1.49H7.79c-0.82,0 -1.49,-0.67 -1.49,-1.49v-5.48c0,-0.83 0.67,-1.49 1.49,-1.49h10.78c0.84,0 1.49,0.66 1.49,1.49v0.46h0.75c0.83,0 1.49,0.67 1.49,1.49V28.53zM24.56,18.44c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86s2.08,-0.79 2.86,0C25.35,16.37 25.35,17.65 24.56,18.44zM17.99,24.84H13.1c-0.33,0 -0.6,0.27 -0.6,0.6v4.28c0,0.33 0.27,0.6 0.6,0.6h4.89c0.23,0 0.41,-0.12 0.5,-0.3c0.06,-0.09 0.08,-0.19 0.08,-0.3v-4.28c0,-0.05 0,-0.09 -0.02,-0.14C18.5,25.03 18.28,24.84 17.99,24.84z"
|
||||
android:strokeAlpha="0.54"
|
||||
android:fillAlpha="0.54"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M39.85,18.81v0.05c0,0.03 -0.01,0.05 -0.01,0.08c0,0.02 -0.01,0.04 -0.02,0.07c-0.17,0.76 -0.94,1.23 -1.7,1.05c-0.07,-0.01 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.92,-0.76 -0.9,-1.35v-0.08c0,-0.01 0,-0.01 0,-0.01c0.01,-0.05 0.02,-0.1 0.02,-0.15c0.31,-2.26 0.03,-4.5 -0.73,-6.54c-0.12,-0.32 -0.25,-0.64 -0.4,-0.96c-0.06,-0.13 -0.12,-0.26 -0.19,-0.38c-0.04,-0.1 -0.09,-0.19 -0.14,-0.28c-0.05,-0.1 -0.1,-0.2 -0.16,-0.29c-0.01,-0.03 -0.02,-0.05 -0.03,-0.07c-0.05,-0.07 -0.09,-0.15 -0.14,-0.23c-0.04,-0.08 -0.1,-0.17 -0.15,-0.25s-0.1,-0.16 -0.15,-0.24c-0.05,-0.08 -0.11,-0.16 -0.16,-0.24c-0.33,-0.48 -0.68,-0.94 -1.07,-1.37c-0.06,-0.07 -0.12,-0.14 -0.19,-0.21C33.4,7.2 33.25,7.05 33.1,6.9c-0.05,-0.06 -0.11,-0.12 -0.17,-0.17c-0.07,-0.07 -0.14,-0.13 -0.21,-0.2c-0.1,-0.09 -0.2,-0.17 -0.29,-0.26c-0.11,-0.09 -0.21,-0.17 -0.32,-0.26C32,5.92 31.9,5.84 31.79,5.76c-0.86,-0.66 -1.81,-1.22 -2.83,-1.66c-0.12,-0.06 -0.25,-0.11 -0.38,-0.16c-0.12,-0.05 -0.25,-0.1 -0.38,-0.15c-0.11,-0.04 -0.23,-0.08 -0.34,-0.11c-0.02,-0.01 -0.03,-0.02 -0.05,-0.02c-1.36,-0.46 -2.81,-0.7 -4.28,-0.7c-0.79,0 -1.58,0.07 -2.37,0.21c-0.02,0 -0.05,0.01 -0.07,0.01c-0.68,0.1 -1.36,-0.31 -1.56,-0.99l-0.02,-0.07c-0.19,-0.74 0.25,-1.5 1,-1.71c0.03,0 0.06,-0.01 0.09,-0.02c0.03,0 0.06,-0.01 0.08,-0.01c2.77,-0.49 5.53,-0.24 8.06,0.61c0.03,0 0.05,0.01 0.07,0.02c0.13,0.04 0.26,0.09 0.38,0.13c0.13,0.05 0.27,0.1 0.4,0.16c0.03,0.01 0.07,0.02 0.1,0.04c1.98,0.8 3.79,1.98 5.32,3.47l0.37,0.37c0.11,0.11 0.21,0.22 0.32,0.34c0.01,0.01 0.03,0.03 0.04,0.05c0.07,0.07 0.14,0.15 0.2,0.22c0.05,0.06 0.1,0.11 0.15,0.17c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.14,0.19 0.21,0.28c0.13,0.17 0.26,0.35 0.38,0.53c0.02,0.02 0.03,0.05 0.05,0.07c0.05,0.09 0.11,0.17 0.16,0.26c0.13,0.19 0.25,0.39 0.37,0.59c0.06,0.1 0.12,0.2 0.18,0.3c0.08,0.15 0.16,0.3 0.24,0.45c0.05,0.09 0.1,0.19 0.15,0.29c0.01,0.01 0.01,0.02 0.02,0.03c0.07,0.15 0.14,0.3 0.21,0.45c0,0.01 0.01,0.02 0.01,0.03c0.14,0.3 0.27,0.62 0.39,0.93c0.01,0.01 0.01,0.02 0.01,0.03c0.01,0.01 0.01,0.01 0.01,0.02c0.01,0.02 0.02,0.05 0.03,0.07C39.88,13.31 40.23,16.04 39.85,18.81z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M34.12,17.63v0.07c-0.01,0.06 -0.02,0.13 -0.03,0.19c-0.18,0.77 -0.95,1.24 -1.71,1.06c-0.06,-0.02 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.91,-0.76 -0.9,-1.35c0.01,-0.06 0.01,-0.12 0.02,-0.18c0.12,-1.83 -0.39,-3.6 -1.38,-5.05c-0.23,-0.35 -0.49,-0.67 -0.77,-0.97l-0.18,-0.18c-0.09,-0.1 -0.19,-0.18 -0.28,-0.27c-0.07,-0.07 -0.15,-0.13 -0.22,-0.19c-0.06,-0.06 -0.13,-0.11 -0.2,-0.16c-0.04,-0.03 -0.08,-0.07 -0.12,-0.1c-0.11,-0.09 -0.23,-0.17 -0.35,-0.25c-0.06,-0.04 -0.12,-0.08 -0.18,-0.12c-0.01,0 -0.01,0 -0.01,0c-0.19,-0.12 -0.38,-0.24 -0.58,-0.34c-0.06,-0.03 -0.13,-0.07 -0.19,-0.1c-0.01,0 -0.02,-0.01 -0.03,-0.01c-0.13,-0.06 -0.25,-0.12 -0.38,-0.17c-0.01,-0.01 -0.02,-0.01 -0.02,-0.01c-1.25,-0.53 -2.63,-0.74 -4.03,-0.58c-0.69,0.1 -1.35,-0.33 -1.54,-1.02c-0.21,-0.76 0.23,-1.54 0.99,-1.75c0.03,-0.01 0.07,-0.01 0.1,-0.02c0.08,-0.01 0.15,-0.02 0.22,-0.02c1.77,-0.2 3.51,0.05 5.1,0.68c0.01,0 0.01,0 0.03,0.01c0.18,0.07 0.36,0.14 0.54,0.22c1.02,0.47 1.97,1.1 2.81,1.87c0.15,0.14 0.3,0.28 0.44,0.43c0.07,0.07 0.14,0.14 0.2,0.21c0.24,0.26 0.46,0.53 0.68,0.81c0.08,0.11 0.16,0.22 0.24,0.34c0.04,0.05 0.08,0.11 0.12,0.17s0.08,0.11 0.12,0.17c0.03,0.06 0.07,0.12 0.11,0.18c0.03,0.05 0.06,0.11 0.09,0.16c0.1,0.16 0.19,0.33 0.28,0.5c0.05,0.09 0.09,0.17 0.13,0.26c0,0 0,0 0.01,0.01c0.04,0.09 0.09,0.19 0.13,0.29c0.01,0.02 0.02,0.04 0.03,0.06c0.01,0.03 0.02,0.06 0.04,0.09C33.94,14.09 34.26,15.84 34.12,17.63z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
11
app/src/main/res/drawable/ic_device_mobile_inactive_10.xml
Normal file
11
app/src/main/res/drawable/ic_device_mobile_inactive_10.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.41C1,11.41 0,12.41 0,25.71C0,39 1,40 14.29,40c13.3,0 14.3,-1 14.3,-14.29C28.59,12.41 27.59,11.41 14.29,11.41zM22.3,28.66c0,0.82 -0.66,1.49 -1.49,1.49h-0.75v0.3c0,0.82 -0.65,1.49 -1.49,1.49H7.79c-0.82,0 -1.49,-0.67 -1.49,-1.49v-5.48c0,-0.83 0.67,-1.49 1.49,-1.49h10.78c0.84,0 1.49,0.66 1.49,1.49v0.46h0.75c0.83,0 1.49,0.67 1.49,1.49V28.66zM24.56,18.57c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86s2.08,-0.79 2.86,0C25.35,16.5 25.35,17.78 24.56,18.57zM17.99,24.97H13.1c-0.33,0 -0.6,0.27 -0.6,0.6v4.28c0,0.33 0.27,0.6 0.6,0.6h4.89c0.23,0 0.41,-0.12 0.5,-0.3c0.06,-0.09 0.08,-0.19 0.08,-0.3v-4.28c0,-0.05 0,-0.09 -0.02,-0.14C18.5,25.16 18.28,24.97 17.99,24.97z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
23
app/src/main/res/drawable/ic_device_radiation_active_10.xml
Normal file
23
app/src/main/res/drawable/ic_device_radiation_active_10.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.28C1,11.28 0,12.28 0,25.58c0,13.29 1,14.29 14.29,14.29c13.3,0 14.3,-1 14.3,-14.29C28.59,12.28 27.59,11.28 14.29,11.28zM13.55,25.71c0.22,-0.14 0.48,-0.21 0.75,-0.21s0.53,0.07 0.74,0.21c0.46,0.25 0.76,0.74 0.76,1.3s-0.3,1.04 -0.76,1.29c-0.21,0.14 -0.47,0.21 -0.74,0.21s-0.53,-0.07 -0.75,-0.21c-0.45,-0.25 -0.76,-0.73 -0.76,-1.29S13.1,25.96 13.55,25.71zM7.3,26.49c0.16,-2.2 1.33,-4.11 3.05,-5.28c0.23,-0.16 0.55,-0.07 0.69,0.17l1.99,3.44c-0.75,0.44 -1.26,1.26 -1.26,2.19H7.79C7.51,27.01 7.28,26.77 7.3,26.49zM17.35,33.32c-0.92,0.45 -1.96,0.7 -3.05,0.7c-1.1,0 -2.14,-0.25 -3.06,-0.7c-0.26,-0.12 -0.34,-0.44 -0.2,-0.69l1.99,-3.44c0.37,0.22 0.81,0.34 1.27,0.34c0.46,0 0.89,-0.12 1.26,-0.34l1.99,3.44C17.69,32.88 17.61,33.2 17.35,33.32zM20.8,27.01h-3.98c0,-0.93 -0.51,-1.76 -1.26,-2.19l1.99,-3.44c0.14,-0.24 0.46,-0.33 0.69,-0.17c1.72,1.17 2.89,3.08 3.05,5.28C21.31,26.77 21.08,27.01 20.8,27.01zM24.56,18.44c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86c0.79,-0.79 2.08,-0.79 2.86,0C25.35,16.37 25.35,17.65 24.56,18.44z"
|
||||
android:strokeAlpha="0.54"
|
||||
android:fillAlpha="0.54"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M39.85,18.81v0.05c0,0.03 -0.01,0.05 -0.01,0.08c0,0.02 -0.01,0.04 -0.02,0.07c-0.17,0.76 -0.94,1.23 -1.7,1.05c-0.07,-0.01 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.92,-0.76 -0.9,-1.35v-0.08c0,-0.01 0,-0.01 0,-0.01c0.01,-0.05 0.02,-0.1 0.02,-0.15c0.31,-2.26 0.03,-4.5 -0.73,-6.54c-0.12,-0.32 -0.25,-0.64 -0.4,-0.96c-0.06,-0.13 -0.12,-0.26 -0.19,-0.38c-0.04,-0.1 -0.09,-0.19 -0.14,-0.28c-0.05,-0.1 -0.1,-0.2 -0.16,-0.29c-0.01,-0.03 -0.02,-0.05 -0.03,-0.07c-0.05,-0.07 -0.09,-0.15 -0.14,-0.23c-0.04,-0.08 -0.1,-0.17 -0.15,-0.25c-0.05,-0.08 -0.1,-0.16 -0.15,-0.24c-0.05,-0.08 -0.11,-0.16 -0.16,-0.24c-0.33,-0.48 -0.68,-0.94 -1.07,-1.37c-0.06,-0.07 -0.12,-0.14 -0.19,-0.21C33.4,7.2 33.25,7.05 33.1,6.9c-0.05,-0.06 -0.11,-0.12 -0.17,-0.17c-0.07,-0.07 -0.14,-0.13 -0.21,-0.2c-0.1,-0.09 -0.2,-0.17 -0.29,-0.26c-0.11,-0.09 -0.21,-0.17 -0.32,-0.26C32,5.92 31.9,5.84 31.79,5.76c-0.86,-0.66 -1.81,-1.22 -2.83,-1.66c-0.12,-0.06 -0.25,-0.11 -0.38,-0.16c-0.12,-0.05 -0.25,-0.1 -0.38,-0.15c-0.11,-0.04 -0.23,-0.08 -0.34,-0.11c-0.02,-0.01 -0.03,-0.02 -0.05,-0.02c-1.36,-0.46 -2.81,-0.7 -4.28,-0.7c-0.79,0 -1.58,0.07 -2.37,0.21c-0.02,0 -0.05,0.01 -0.07,0.01c-0.68,0.1 -1.36,-0.31 -1.56,-0.99l-0.02,-0.07c-0.19,-0.74 0.25,-1.5 1,-1.71c0.03,0 0.06,-0.01 0.09,-0.02c0.03,0 0.06,-0.01 0.08,-0.01c2.77,-0.49 5.53,-0.24 8.06,0.61c0.03,0 0.05,0.01 0.07,0.02c0.13,0.04 0.26,0.09 0.38,0.13c0.13,0.05 0.27,0.1 0.4,0.16c0.03,0.01 0.07,0.02 0.1,0.04c1.98,0.8 3.79,1.98 5.32,3.47l0.37,0.37c0.11,0.11 0.21,0.22 0.32,0.34c0.01,0.01 0.03,0.03 0.04,0.05c0.07,0.07 0.14,0.15 0.2,0.22c0.05,0.06 0.1,0.11 0.15,0.17c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.14,0.19 0.21,0.28c0.13,0.17 0.26,0.35 0.38,0.53c0.02,0.02 0.03,0.05 0.05,0.07c0.05,0.09 0.11,0.17 0.16,0.26c0.13,0.19 0.25,0.39 0.37,0.59c0.06,0.1 0.12,0.2 0.18,0.3c0.08,0.15 0.16,0.3 0.24,0.45c0.05,0.09 0.1,0.19 0.15,0.29c0.01,0.01 0.01,0.02 0.02,0.03c0.07,0.15 0.14,0.3 0.21,0.45c0,0.01 0.01,0.02 0.01,0.03c0.14,0.3 0.27,0.62 0.39,0.93c0.01,0.01 0.01,0.02 0.01,0.03c0.01,0.01 0.01,0.01 0.01,0.02c0.01,0.02 0.02,0.05 0.03,0.07C39.88,13.31 40.23,16.04 39.85,18.81z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"
|
||||
tools:ignore="VectorPath" />
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M34.12,17.63v0.07c-0.01,0.06 -0.02,0.13 -0.03,0.19c-0.18,0.77 -0.95,1.24 -1.71,1.06c-0.06,-0.02 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.91,-0.76 -0.9,-1.35c0.01,-0.06 0.01,-0.12 0.02,-0.18c0.12,-1.83 -0.39,-3.6 -1.38,-5.05c-0.23,-0.35 -0.49,-0.67 -0.77,-0.97l-0.18,-0.18c-0.09,-0.1 -0.19,-0.18 -0.28,-0.27c-0.07,-0.07 -0.15,-0.13 -0.22,-0.19c-0.06,-0.06 -0.13,-0.11 -0.2,-0.16c-0.04,-0.03 -0.08,-0.07 -0.12,-0.1c-0.11,-0.09 -0.23,-0.17 -0.35,-0.25c-0.06,-0.04 -0.12,-0.08 -0.18,-0.12c-0.01,0 -0.01,0 -0.01,0c-0.19,-0.12 -0.38,-0.24 -0.58,-0.34c-0.06,-0.03 -0.13,-0.07 -0.19,-0.1c-0.01,0 -0.02,-0.01 -0.03,-0.01c-0.13,-0.06 -0.25,-0.12 -0.38,-0.17c-0.01,-0.01 -0.02,-0.01 -0.02,-0.01c-1.25,-0.53 -2.63,-0.74 -4.03,-0.58c-0.69,0.1 -1.35,-0.33 -1.54,-1.02c-0.21,-0.76 0.23,-1.54 0.99,-1.75c0.03,-0.01 0.07,-0.01 0.1,-0.02c0.08,-0.01 0.15,-0.02 0.22,-0.02c1.77,-0.2 3.51,0.05 5.1,0.68c0.01,0 0.01,0 0.03,0.01c0.18,0.07 0.36,0.14 0.54,0.22c1.02,0.47 1.97,1.1 2.81,1.87c0.15,0.14 0.3,0.28 0.44,0.43c0.07,0.07 0.14,0.14 0.2,0.21c0.24,0.26 0.46,0.53 0.68,0.81c0.08,0.11 0.16,0.22 0.24,0.34c0.04,0.05 0.08,0.11 0.12,0.17s0.08,0.11 0.12,0.17c0.03,0.06 0.07,0.12 0.11,0.18c0.03,0.05 0.06,0.11 0.09,0.16c0.1,0.16 0.19,0.33 0.28,0.5c0.05,0.09 0.09,0.17 0.13,0.26c0,0 0,0 0.01,0.01c0.04,0.09 0.09,0.19 0.13,0.29c0.01,0.02 0.02,0.04 0.03,0.06c0.01,0.03 0.02,0.06 0.04,0.09C33.94,14.09 34.26,15.84 34.12,17.63z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.28C1,11.28 0,12.28 0,25.58c0,13.29 1,14.29 14.29,14.29c13.3,0 14.3,-1 14.3,-14.29C28.59,12.28 27.59,11.28 14.29,11.28zM13.55,25.71c0.22,-0.14 0.48,-0.21 0.75,-0.21s0.53,0.07 0.74,0.21c0.46,0.25 0.76,0.74 0.76,1.3s-0.3,1.04 -0.76,1.29c-0.21,0.14 -0.47,0.21 -0.74,0.21s-0.53,-0.07 -0.75,-0.21c-0.45,-0.25 -0.76,-0.73 -0.76,-1.29S13.1,25.96 13.55,25.71zM7.3,26.49c0.16,-2.2 1.33,-4.11 3.05,-5.28c0.23,-0.16 0.55,-0.07 0.69,0.17l1.99,3.44c-0.75,0.44 -1.26,1.26 -1.26,2.19H7.79C7.51,27.01 7.28,26.77 7.3,26.49zM17.35,33.32c-0.92,0.45 -1.96,0.7 -3.05,0.7c-1.1,0 -2.14,-0.25 -3.06,-0.7c-0.26,-0.12 -0.34,-0.44 -0.2,-0.69l1.99,-3.44c0.37,0.22 0.81,0.34 1.27,0.34c0.46,0 0.89,-0.12 1.26,-0.34l1.99,3.44C17.69,32.88 17.61,33.2 17.35,33.32zM20.8,27.01h-3.98c0,-0.93 -0.51,-1.76 -1.26,-2.19l1.99,-3.44c0.14,-0.24 0.46,-0.33 0.69,-0.17c1.72,1.17 2.89,3.08 3.05,5.28C21.31,26.77 21.08,27.01 20.8,27.01zM24.56,18.44c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86c0.79,-0.79 2.08,-0.79 2.86,0C25.35,16.37 25.35,17.65 24.56,18.44z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
21
app/src/main/res/drawable/ic_device_solar_active_10.xml
Normal file
21
app/src/main/res/drawable/ic_device_solar_active_10.xml
Normal file
@@ -0,0 +1,21 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.32,11.32C1.09,11.32 0.1,12.32 0.1,25.55c0,13.22 0.99,14.22 14.22,14.22c13.23,0 14.23,-0.99 14.23,-14.22C28.55,12.32 27.55,11.32 14.32,11.32zM13.65,20.79c0,-0.38 0.3,-0.68 0.68,-0.68h0.01c0.38,0 0.68,0.3 0.68,0.68v1.33c0,0.37 -0.3,0.67 -0.69,0.67c-0.38,0 -0.68,-0.3 -0.68,-0.68V20.79zM9.07,22.29L9.07,22.29c0.27,-0.27 0.7,-0.27 0.96,-0.01l0.95,0.95c0.13,0.13 0.2,0.3 0.2,0.48c0,0.17 -0.07,0.34 -0.21,0.48c-0.27,0.27 -0.69,0.27 -0.96,0l-0.94,-0.95C8.81,22.98 8.81,22.56 9.07,22.29zM6.87,27.54L6.87,27.54c0,-0.38 0.3,-0.68 0.67,-0.68h1.37c0.38,0 0.68,0.3 0.68,0.68v0.01c0,0.37 -0.3,0.67 -0.68,0.66H7.53C7.16,28.21 6.87,27.91 6.87,27.54zM10.97,31.85L10,32.82c-0.26,0.27 -0.69,0.27 -0.95,0H9.04c-0.26,-0.27 -0.26,-0.69 0,-0.96l0.98,-0.97c0.26,-0.27 0.69,-0.27 0.96,0v0.01C11.24,31.16 11.24,31.59 10.97,31.85zM15,34.3c0,0.38 -0.3,0.68 -0.68,0.68c-0.38,0 -0.68,-0.3 -0.68,-0.68v-1.32c0,-0.38 0.3,-0.68 0.68,-0.68c0.38,0 0.68,0.3 0.68,0.68V34.3zM14.33,30.37c-1.56,0 -2.83,-1.26 -2.83,-2.83c0,-1.56 1.26,-2.84 2.83,-2.84s2.84,1.27 2.84,2.84C17.16,29.1 15.89,30.37 14.33,30.37zM17.68,23.22l0.97,-0.97c0.27,-0.26 0.69,-0.26 0.96,0v0.01c0.27,0.26 0.27,0.69 0,0.95l-0.97,0.97c-0.26,0.27 -0.69,0.27 -0.95,0h-0.01C17.42,23.91 17.42,23.49 17.68,23.22zM19.58,32.8c-0.27,0.27 -0.69,0.27 -0.96,0l-0.94,-0.94c-0.14,-0.14 -0.2,-0.31 -0.2,-0.48c0,-0.17 0.06,-0.34 0.2,-0.48c0.27,-0.27 0.69,-0.27 0.96,0l0.94,0.94C19.85,32.11 19.85,32.53 19.58,32.8zM21.79,27.54c0,0.37 -0.3,0.67 -0.68,0.67h-1.36c-0.37,0 -0.67,-0.3 -0.67,-0.67v-0.01c0,-0.37 0.3,-0.67 0.67,-0.67h1.36C21.49,26.87 21.79,27.16 21.79,27.54L21.79,27.54zM24.54,18.45c-0.78,0.79 -2.06,0.79 -2.85,0c-0.79,-0.79 -0.79,-2.06 0,-2.85c0.79,-0.79 2.07,-0.79 2.85,0C25.32,16.39 25.32,17.66 24.54,18.45z"
|
||||
android:strokeAlpha="0.54"
|
||||
android:fillAlpha="0.54"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M39.75,18.81v0.05c0,0.03 -0.01,0.05 -0.01,0.08c0,0.02 -0.01,0.04 -0.02,0.07c-0.17,0.76 -0.94,1.22 -1.69,1.04c-0.07,-0.01 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.92,-0.76 -0.9,-1.34v-0.08c0,-0.01 0,-0.01 0,-0.01c0.01,-0.05 0.02,-0.1 0.02,-0.15c0.31,-2.25 0.03,-4.48 -0.73,-6.51c-0.12,-0.32 -0.25,-0.64 -0.4,-0.96c-0.06,-0.13 -0.12,-0.26 -0.19,-0.38c-0.04,-0.1 -0.09,-0.19 -0.14,-0.28c-0.05,-0.1 -0.1,-0.2 -0.16,-0.29c-0.01,-0.03 -0.02,-0.05 -0.03,-0.07c-0.05,-0.07 -0.09,-0.15 -0.14,-0.23c-0.04,-0.08 -0.1,-0.17 -0.15,-0.25c-0.05,-0.08 -0.1,-0.16 -0.15,-0.24c-0.05,-0.08 -0.11,-0.16 -0.16,-0.24c-0.33,-0.48 -0.68,-0.94 -1.06,-1.36c-0.06,-0.07 -0.12,-0.14 -0.19,-0.21c-0.14,-0.15 -0.29,-0.3 -0.44,-0.45c-0.05,-0.06 -0.11,-0.12 -0.17,-0.17c-0.07,-0.07 -0.14,-0.13 -0.21,-0.2c-0.1,-0.09 -0.2,-0.17 -0.29,-0.26c-0.11,-0.09 -0.21,-0.17 -0.32,-0.26c-0.11,-0.09 -0.21,-0.17 -0.32,-0.25c-0.86,-0.66 -1.8,-1.21 -2.82,-1.65c-0.12,-0.06 -0.25,-0.11 -0.38,-0.16c-0.12,-0.05 -0.25,-0.1 -0.38,-0.15c-0.11,-0.04 -0.23,-0.08 -0.34,-0.11c-0.02,-0.01 -0.03,-0.02 -0.05,-0.02c-1.35,-0.46 -2.8,-0.7 -4.26,-0.7c-0.79,0 -1.57,0.07 -2.36,0.21c-0.02,0 -0.05,0.01 -0.07,0.01c-0.68,0.1 -1.35,-0.31 -1.55,-0.99l-0.02,-0.07c-0.19,-0.74 0.25,-1.49 0.99,-1.7c0.03,0 0.06,-0.01 0.09,-0.02c0.03,0 0.06,-0.01 0.08,-0.01c2.76,-0.49 5.5,-0.24 8.02,0.61c0.03,0 0.05,0.01 0.07,0.02c0.13,0.04 0.26,0.09 0.38,0.13c0.13,0.05 0.27,0.1 0.4,0.16c0.03,0.01 0.07,0.02 0.1,0.04c1.97,0.8 3.77,1.97 5.29,3.45l0.37,0.37c0.11,0.11 0.21,0.22 0.32,0.34c0.01,0.01 0.03,0.03 0.04,0.05c0.07,0.07 0.14,0.15 0.2,0.22c0.05,0.06 0.1,0.11 0.15,0.17c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.15,0.18 0.22,0.27c0.07,0.09 0.14,0.19 0.21,0.28c0.13,0.17 0.26,0.35 0.38,0.53c0.02,0.02 0.03,0.05 0.05,0.07c0.05,0.09 0.11,0.17 0.16,0.26c0.13,0.19 0.25,0.39 0.37,0.59c0.06,0.1 0.12,0.2 0.18,0.3c0.08,0.15 0.16,0.3 0.24,0.45c0.05,0.09 0.1,0.19 0.15,0.29c0.01,0.01 0.01,0.02 0.02,0.03c0.07,0.15 0.14,0.3 0.21,0.45c0,0.01 0.01,0.02 0.01,0.03c0.14,0.3 0.27,0.62 0.39,0.93c0.01,0.01 0.01,0.02 0.01,0.03c0.01,0.01 0.01,0.01 0.01,0.02c0.01,0.02 0.02,0.05 0.03,0.07C39.78,13.34 40.13,16.06 39.75,18.81z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M34.05,17.64v0.07c-0.01,0.06 -0.02,0.13 -0.03,0.19c-0.18,0.77 -0.95,1.23 -1.7,1.05c-0.06,-0.02 -0.13,-0.04 -0.19,-0.06c-0.56,-0.22 -0.91,-0.76 -0.9,-1.34c0.01,-0.06 0.01,-0.12 0.02,-0.18c0.12,-1.82 -0.39,-3.58 -1.37,-5.02c-0.23,-0.35 -0.49,-0.67 -0.77,-0.97l-0.18,-0.18c-0.09,-0.1 -0.19,-0.18 -0.28,-0.27c-0.07,-0.07 -0.15,-0.13 -0.22,-0.19c-0.06,-0.06 -0.13,-0.11 -0.2,-0.16c-0.04,-0.03 -0.08,-0.07 -0.12,-0.1c-0.11,-0.09 -0.23,-0.17 -0.35,-0.25c-0.06,-0.04 -0.12,-0.08 -0.18,-0.12c-0.01,0 -0.01,0 -0.01,0C27.39,10 27.2,9.88 27,9.78c-0.06,-0.03 -0.13,-0.07 -0.19,-0.1c-0.01,0 -0.02,-0.01 -0.03,-0.01c-0.13,-0.06 -0.25,-0.12 -0.38,-0.17c-0.01,-0.01 -0.02,-0.01 -0.02,-0.01c-1.24,-0.53 -2.62,-0.74 -4.01,-0.58c-0.69,0.1 -1.34,-0.33 -1.53,-1.01c-0.21,-0.76 0.23,-1.53 0.99,-1.74c0.03,-0.01 0.07,-0.01 0.1,-0.02c0.08,-0.01 0.15,-0.02 0.22,-0.02c1.76,-0.2 3.49,0.05 5.07,0.68c0.01,0 0.01,0 0.03,0.01c0.18,0.07 0.36,0.14 0.54,0.22c1.01,0.47 1.96,1.09 2.8,1.86c0.15,0.14 0.3,0.28 0.44,0.43c0.07,0.07 0.14,0.14 0.2,0.21c0.24,0.26 0.46,0.53 0.68,0.81c0.08,0.11 0.16,0.22 0.24,0.34c0.04,0.05 0.08,0.11 0.12,0.17c0.04,0.06 0.08,0.11 0.12,0.17c0.03,0.06 0.07,0.12 0.11,0.18c0.03,0.05 0.06,0.11 0.09,0.16c0.1,0.16 0.19,0.33 0.28,0.5c0.05,0.09 0.09,0.17 0.13,0.26c0,0 0,0 0.01,0.01c0.04,0.09 0.09,0.19 0.13,0.29c0.01,0.02 0.02,0.04 0.03,0.06c0.01,0.03 0.02,0.06 0.04,0.09C33.87,14.12 34.19,15.86 34.05,17.64z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
11
app/src/main/res/drawable/ic_device_solar_inactive_10.xml
Normal file
11
app/src/main/res/drawable/ic_device_solar_inactive_10.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="40dp"
|
||||
android:height="40dp"
|
||||
android:viewportWidth="40"
|
||||
android:viewportHeight="40">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M14.29,11.41C1,11.41 0,12.41 0,25.71C0,39 1,40 14.29,40c13.3,0 14.3,-1 14.3,-14.29C28.59,12.41 27.59,11.41 14.29,11.41zM13.62,20.92c0,-0.38 0.3,-0.68 0.68,-0.68h0.01c0.38,0 0.68,0.3 0.68,0.68v1.34c0,0.37 -0.3,0.67 -0.69,0.67c-0.38,0 -0.68,-0.3 -0.68,-0.68V20.92zM9.02,22.43l0.01,-0.01c0.26,-0.26 0.69,-0.26 0.95,0l0.95,0.95c0.13,0.13 0.2,0.3 0.2,0.48c0,0.17 -0.07,0.34 -0.21,0.48c-0.27,0.27 -0.69,0.27 -0.96,0l-0.94,-0.95C8.75,23.12 8.75,22.7 9.02,22.43zM6.8,27.71V27.7c0,-0.37 0.3,-0.67 0.67,-0.67h1.38c0.38,0 0.68,0.3 0.68,0.68v0.01c0,0.37 -0.3,0.67 -0.68,0.66H7.47C7.1,28.38 6.8,28.08 6.8,27.71zM10.92,32.04l-0.97,0.97c-0.26,0.27 -0.69,0.27 -0.95,0H8.99c-0.26,-0.27 -0.26,-0.69 0,-0.96l0.98,-0.97c0.26,-0.27 0.69,-0.27 0.96,0v0.01C11.2,31.35 11.2,31.78 10.92,32.04zM14.98,34.5c0,0.38 -0.3,0.68 -0.68,0.68s-0.68,-0.3 -0.68,-0.68v-1.33c0,-0.38 0.3,-0.68 0.68,-0.68s0.68,0.3 0.68,0.68V34.5zM14.3,30.55c-1.57,0 -2.84,-1.27 -2.84,-2.84s1.27,-2.85 2.84,-2.85s2.85,1.28 2.85,2.85S15.87,30.55 14.3,30.55zM17.67,23.37l0.97,-0.97c0.27,-0.26 0.69,-0.26 0.96,0v0.01c0.27,0.26 0.27,0.69 0,0.95l-0.97,0.97c-0.26,0.27 -0.69,0.27 -0.95,0h-0.01C17.41,24.06 17.41,23.64 17.67,23.37zM19.58,32.99c-0.27,0.27 -0.69,0.27 -0.96,0l-0.94,-0.94c-0.14,-0.14 -0.2,-0.31 -0.2,-0.48c0,-0.17 0.06,-0.34 0.2,-0.48c0.27,-0.27 0.69,-0.27 0.96,0l0.94,0.94C19.85,32.3 19.85,32.72 19.58,32.99zM21.8,27.71c0,0.37 -0.3,0.67 -0.68,0.67h-1.37c-0.37,0 -0.67,-0.3 -0.67,-0.67V27.7c0,-0.37 0.3,-0.67 0.67,-0.67h1.37c0.38,0 0.68,0.3 0.68,0.67V27.71zM24.56,18.57c-0.78,0.79 -2.07,0.79 -2.86,0c-0.79,-0.79 -0.79,-2.07 0,-2.86c0.79,-0.79 2.08,-0.79 2.86,0C25.35,16.5 25.35,17.78 24.56,18.57z"
|
||||
android:strokeAlpha="0.22"
|
||||
android:fillAlpha="0.22"/>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_edit.xml
Normal file
10
app/src/main/res/drawable/ic_edit.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M3,17.46v3.04c0,0.28 0.22,0.5 0.5,0.5h3.04c0.13,0 0.26,-0.05 0.35,-0.15L17.81,9.94l-3.75,-3.75L3.15,17.1c-0.1,0.1 -0.15,0.22 -0.15,0.36zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
|
||||
</vector>
|
||||
9
app/src/main/res/drawable/ic_upload.xml
Normal file
9
app/src/main/res/drawable/ic_upload.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M6.84,14.04L6.84,14.04c0.47,0.47 1.21,0.47 1.68,0l2.28,-2.28l0,11.04c0,0.66 0.54,1.2 1.2,1.2l0,0c0.66,0 1.2,-0.54 1.2,-1.2l0,-11.04l2.28,2.28c0.47,0.47 1.21,0.47 1.68,0l0,0c0.47,-0.47 0.47,-1.21 0,-1.68l-4.31,-4.31c-0.47,-0.47 -1.22,-0.47 -1.69,0l-4.32,4.31C6.37,12.83 6.37,13.57 6.84,14.04zM20.4,2.4l0,4.8c0,0.66 0.54,1.2 1.2,1.2l0,0c0.66,0 1.2,-0.54 1.2,-1.2l0,-4.8c0,-1.32 -1.08,-2.4 -2.4,-2.4L3.6,0C2.28,0 1.2,1.08 1.2,2.4l0,4.8c0,0.66 0.54,1.2 1.2,1.2l0,0c0.66,0 1.2,-0.54 1.2,-1.2l0,-4.8L20.4,2.4z"/>
|
||||
</vector>
|
||||
@@ -101,6 +101,7 @@
|
||||
<string name="text_device_firmware">Версія прашыўкі</string>
|
||||
<string name="text_device_location">Месцазнаходжанне</string>
|
||||
<string name="text_device_location_unset">Прылада не зарэгістравана</string>
|
||||
<string name="text_device_connected">падлучана</string>
|
||||
<string name="text_device_visibility">Бачнасць</string>
|
||||
<string name="text_device_visibility_visible">Апублікавана</string>
|
||||
<string name="text_device_visibility_private">Схавана</string>
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
<string name="text_device_firmware">Версия прошивки</string>
|
||||
<string name="text_device_location">Местоположение</string>
|
||||
<string name="text_device_location_unset">Устройство не зарегистрировано</string>
|
||||
<string name="text_device_connected">подключено</string>
|
||||
<string name="text_device_visibility">Видимость</string>
|
||||
<string name="text_device_visibility_visible">Опубликовано</string>
|
||||
<string name="text_device_visibility_private">Скрыто</string>
|
||||
|
||||
@@ -11,9 +11,13 @@
|
||||
<color name="sensorGreen">#FF00C853</color>
|
||||
<color name="sensorYellow">#FFFFD54F</color>
|
||||
<color name="sensorOrange">#FFFF9800</color>
|
||||
<color name="colorGreen">#FF00C853</color>
|
||||
<color name="colorOrange">#FFFF6F00</color>
|
||||
<color name="sensorRed">#FFF44336</color>
|
||||
<color name="sensorPink">#FFEC407A</color>
|
||||
<color name="sensorPurple">#FF8E24AA</color>
|
||||
<color name="black38">#61000000</color>
|
||||
<color name="black54">#8A000000</color>
|
||||
<color name="headerColor">#1F5DA5</color>
|
||||
<color name="windowBackground">#FAFAFA</color>
|
||||
</resources>
|
||||
@@ -158,6 +158,7 @@
|
||||
<string name="text_device_location">Location</string>
|
||||
<string name="text_device_location_unset">Device is not registered</string>
|
||||
<string name="text_device_location_set" translatable="false">%s, %s</string>
|
||||
<string name="text_device_connected">connected</string>
|
||||
<string name="text_device_visibility">Visibility</string>
|
||||
<string name="text_device_visibility_visible">Public</string>
|
||||
<string name="text_device_visibility_private">Private</string>
|
||||
|
||||
@@ -112,6 +112,87 @@ class DeviceRepositoryImpl @Inject constructor(
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun setNarodmon(deviceId: String, enabled: Boolean): Result<Unit> {
|
||||
val current = localDataSource.getDevice(deviceId) ?: return Result.failure(NoSuchElementException("Device not found: $deviceId"))
|
||||
val previous = current.isNarodmonOn
|
||||
|
||||
localDataSource.updateNarodmon(deviceId, enabled)
|
||||
val mutationId = UUID.randomUUID().toString()
|
||||
localDataSource.enqueuePendingMutation(
|
||||
PendingMutation(
|
||||
id = mutationId,
|
||||
type = PendingMutationType.NARODMON,
|
||||
deviceId = deviceId,
|
||||
payload = """{"enabled":$enabled}""",
|
||||
createdAt = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
|
||||
val result = remoteDataSource.setNarodmon(deviceId, enabled)
|
||||
if (result.isSuccess) {
|
||||
localDataSource.removePendingMutationsForDevice(deviceId, PendingMutationType.NARODMON)
|
||||
} else {
|
||||
localDataSource.updateNarodmon(deviceId, previous)
|
||||
localDataSource.removePendingMutationsForDevice(deviceId, PendingMutationType.NARODMON)
|
||||
return result
|
||||
}
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun setLuftdata(deviceId: String, enabled: Boolean): Result<Unit> {
|
||||
val current = localDataSource.getDevice(deviceId) ?: return Result.failure(NoSuchElementException("Device not found: $deviceId"))
|
||||
val previous = current.isLuftdataOn
|
||||
|
||||
localDataSource.updateLuftdata(deviceId, enabled)
|
||||
val mutationId = UUID.randomUUID().toString()
|
||||
localDataSource.enqueuePendingMutation(
|
||||
PendingMutation(
|
||||
id = mutationId,
|
||||
type = PendingMutationType.LUFTDATA,
|
||||
deviceId = deviceId,
|
||||
payload = """{"enabled":$enabled}""",
|
||||
createdAt = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
|
||||
val result = remoteDataSource.setLuftdata(deviceId, enabled)
|
||||
if (result.isSuccess) {
|
||||
localDataSource.removePendingMutationsForDevice(deviceId, PendingMutationType.LUFTDATA)
|
||||
} else {
|
||||
localDataSource.updateLuftdata(deviceId, previous)
|
||||
localDataSource.removePendingMutationsForDevice(deviceId, PendingMutationType.LUFTDATA)
|
||||
return result
|
||||
}
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun setVisibility(deviceId: String, isPublic: Boolean): Result<Unit> {
|
||||
val current = localDataSource.getDevice(deviceId) ?: return Result.failure(NoSuchElementException("Device not found: $deviceId"))
|
||||
val previous = current.isPublic
|
||||
|
||||
localDataSource.updateIsPublic(deviceId, isPublic)
|
||||
val mutationId = UUID.randomUUID().toString()
|
||||
localDataSource.enqueuePendingMutation(
|
||||
PendingMutation(
|
||||
id = mutationId,
|
||||
type = PendingMutationType.VISIBILITY,
|
||||
deviceId = deviceId,
|
||||
payload = """{"isPublic":$isPublic}""",
|
||||
createdAt = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
|
||||
val result = remoteDataSource.setVisibility(deviceId, isPublic)
|
||||
if (result.isSuccess) {
|
||||
localDataSource.removePendingMutationsForDevice(deviceId, PendingMutationType.VISIBILITY)
|
||||
} else {
|
||||
localDataSource.updateIsPublic(deviceId, previous)
|
||||
localDataSource.removePendingMutationsForDevice(deviceId, PendingMutationType.VISIBILITY)
|
||||
return result
|
||||
}
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun triggerFirmwareUpdate(deviceId: String): Result<Unit> =
|
||||
remoteDataSource.triggerFirmwareUpdate(deviceId)
|
||||
|
||||
|
||||
@@ -30,6 +30,15 @@ interface DeviceDao {
|
||||
@Query("UPDATE device SET dataSharingEnabled = :enabled WHERE id = :deviceId")
|
||||
suspend fun updateDataSharing(deviceId: String, enabled: Boolean)
|
||||
|
||||
@Query("UPDATE device SET isNarodmonOn = :enabled WHERE id = :deviceId")
|
||||
suspend fun updateNarodmon(deviceId: String, enabled: Boolean)
|
||||
|
||||
@Query("UPDATE device SET isLuftdataOn = :enabled WHERE id = :deviceId")
|
||||
suspend fun updateLuftdata(deviceId: String, enabled: Boolean)
|
||||
|
||||
@Query("UPDATE device SET isPublic = :isPublic WHERE id = :deviceId")
|
||||
suspend fun updateIsPublic(deviceId: String, isPublic: Boolean)
|
||||
|
||||
@Query("UPDATE device SET isOnline = :isOnline, isOnlineUpdatedAt = :updatedAt WHERE id = :deviceId")
|
||||
suspend fun updateOnlineStatus(deviceId: String, isOnline: Boolean, updatedAt: Long)
|
||||
|
||||
|
||||
@@ -2,10 +2,27 @@ package org.db3.airmq.sdk.device.data.local
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
|
||||
val DEVICE_DB_MIGRATION_1_2 = object : Migration(1, 2) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("ALTER TABLE device ADD COLUMN deviceAddress TEXT NOT NULL DEFAULT ''")
|
||||
db.execSQL("ALTER TABLE device ADD COLUMN configVersion TEXT NOT NULL DEFAULT ''")
|
||||
db.execSQL("ALTER TABLE device ADD COLUMN isNarodmonOn INTEGER NOT NULL DEFAULT 0")
|
||||
db.execSQL("ALTER TABLE device ADD COLUMN isLuftdataOn INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
|
||||
val DEVICE_DB_MIGRATION_2_3 = object : Migration(2, 3) {
|
||||
override fun migrate(db: SupportSQLiteDatabase) {
|
||||
db.execSQL("ALTER TABLE device ADD COLUMN isPublic INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
}
|
||||
|
||||
@Database(
|
||||
entities = [DeviceEntity::class, PendingMutationEntity::class],
|
||||
version = 1,
|
||||
version = 3,
|
||||
exportSchema = false
|
||||
)
|
||||
abstract class DeviceDatabase : RoomDatabase() {
|
||||
|
||||
@@ -18,9 +18,14 @@ data class DeviceEntity(
|
||||
val name: String,
|
||||
val model: String,
|
||||
val firmwareVersion: String,
|
||||
val deviceAddress: String,
|
||||
val configVersion: String,
|
||||
val isNarodmonOn: Boolean,
|
||||
val isLuftdataOn: Boolean,
|
||||
val locationId: String? = null,
|
||||
val latitude: Double? = null,
|
||||
val longitude: Double? = null,
|
||||
val isPublic: Boolean = false,
|
||||
val dataSharingEnabled: Boolean = false,
|
||||
val isOnline: Boolean = false,
|
||||
val isOnlineUpdatedAt: Long? = null,
|
||||
|
||||
@@ -3,6 +3,7 @@ package org.db3.airmq.sdk.device.data.local
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import org.db3.airmq.sdk.device.domain.Device
|
||||
import org.db3.airmq.sdk.device.domain.DeviceModel
|
||||
import org.db3.airmq.sdk.device.domain.OnlineFreshness
|
||||
import org.db3.airmq.sdk.device.domain.PendingMutation
|
||||
import org.db3.airmq.sdk.device.domain.PendingMutationType
|
||||
@@ -47,6 +48,18 @@ class DeviceLocalDataSource @Inject constructor(
|
||||
deviceDao.updateDataSharing(deviceId, enabled)
|
||||
}
|
||||
|
||||
suspend fun updateNarodmon(deviceId: String, enabled: Boolean) {
|
||||
deviceDao.updateNarodmon(deviceId, enabled)
|
||||
}
|
||||
|
||||
suspend fun updateLuftdata(deviceId: String, enabled: Boolean) {
|
||||
deviceDao.updateLuftdata(deviceId, enabled)
|
||||
}
|
||||
|
||||
suspend fun updateIsPublic(deviceId: String, isPublic: Boolean) {
|
||||
deviceDao.updateIsPublic(deviceId, isPublic)
|
||||
}
|
||||
|
||||
suspend fun updateOnlineStatus(deviceId: String, isOnline: Boolean, updatedAt: Long) {
|
||||
deviceDao.updateOnlineStatus(deviceId, isOnline, updatedAt)
|
||||
}
|
||||
@@ -91,11 +104,16 @@ class DeviceLocalDataSource @Inject constructor(
|
||||
return Device(
|
||||
id = id,
|
||||
name = name,
|
||||
model = model,
|
||||
model = DeviceModel.fromString(model),
|
||||
firmwareVersion = firmwareVersion,
|
||||
deviceAddress = deviceAddress,
|
||||
configVersion = configVersion,
|
||||
isNarodmonOn = isNarodmonOn,
|
||||
isLuftdataOn = isLuftdataOn,
|
||||
locationId = locationId,
|
||||
latitude = latitude,
|
||||
longitude = longitude,
|
||||
isPublic = isPublic,
|
||||
city = city,
|
||||
dataSharingEnabled = dataSharingEnabled,
|
||||
isOnline = isOnline,
|
||||
@@ -107,11 +125,16 @@ class DeviceLocalDataSource @Inject constructor(
|
||||
private fun Device.toEntity(): DeviceEntity = DeviceEntity(
|
||||
id = id,
|
||||
name = name,
|
||||
model = model,
|
||||
model = model.toStorageString(),
|
||||
firmwareVersion = firmwareVersion,
|
||||
deviceAddress = deviceAddress,
|
||||
configVersion = configVersion,
|
||||
isNarodmonOn = isNarodmonOn,
|
||||
isLuftdataOn = isLuftdataOn,
|
||||
locationId = locationId,
|
||||
latitude = latitude,
|
||||
longitude = longitude,
|
||||
isPublic = isPublic,
|
||||
dataSharingEnabled = dataSharingEnabled,
|
||||
isOnline = isOnline,
|
||||
isOnlineUpdatedAt = if (onlineFreshness == OnlineFreshness.Fresh) System.currentTimeMillis() else null,
|
||||
|
||||
@@ -4,6 +4,7 @@ import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import org.db3.airmq.sdk.device.domain.Device
|
||||
import org.db3.airmq.sdk.device.domain.DeviceModel
|
||||
import org.db3.airmq.sdk.device.domain.OnlineFreshness
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -39,6 +40,21 @@ interface DeviceRemoteDataSource {
|
||||
*/
|
||||
suspend fun setDataSharing(deviceId: String, enabled: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Execute Narodmon.ru toggle. Phase 1: No-op. Phase 2: Apollo mutation.
|
||||
*/
|
||||
suspend fun setNarodmon(deviceId: String, enabled: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Execute Sensor.community (Luftdata) toggle. Phase 1: No-op. Phase 2: Apollo mutation.
|
||||
*/
|
||||
suspend fun setLuftdata(deviceId: String, enabled: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Execute set visibility (publish/hide) mutation. Phase 1: No-op. Phase 2: Apollo mutation.
|
||||
*/
|
||||
suspend fun setVisibility(deviceId: String, isPublic: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Execute firmware update. Phase 1: No-op. Phase 2: Apollo mutation.
|
||||
*/
|
||||
@@ -56,11 +72,16 @@ class MockDeviceRemoteDataSource @Inject constructor() : DeviceRemoteDataSource
|
||||
Device(
|
||||
id = "device-1",
|
||||
name = "AirMQ #42",
|
||||
model = "mobile",
|
||||
model = DeviceModel.Mobile,
|
||||
firmwareVersion = "1.0",
|
||||
deviceAddress = "192.168.1.100",
|
||||
configVersion = "42",
|
||||
isNarodmonOn = true,
|
||||
isLuftdataOn = false,
|
||||
locationId = "loc-1",
|
||||
latitude = 53.9,
|
||||
longitude = 27.5,
|
||||
isPublic = false,
|
||||
city = "Minsk",
|
||||
dataSharingEnabled = true,
|
||||
isOnline = true,
|
||||
@@ -70,11 +91,16 @@ class MockDeviceRemoteDataSource @Inject constructor() : DeviceRemoteDataSource
|
||||
Device(
|
||||
id = "device-2",
|
||||
name = "AirMQ #17",
|
||||
model = "mobile",
|
||||
model = DeviceModel.Mobile,
|
||||
firmwareVersion = "1.0",
|
||||
deviceAddress = "192.168.1.101",
|
||||
configVersion = "41",
|
||||
isNarodmonOn = false,
|
||||
isLuftdataOn = false,
|
||||
locationId = null,
|
||||
latitude = null,
|
||||
longitude = null,
|
||||
isPublic = false,
|
||||
city = null,
|
||||
dataSharingEnabled = false,
|
||||
isOnline = false,
|
||||
@@ -102,6 +128,15 @@ class MockDeviceRemoteDataSource @Inject constructor() : DeviceRemoteDataSource
|
||||
override suspend fun setDataSharing(deviceId: String, enabled: Boolean): Result<Unit> =
|
||||
Result.success(Unit)
|
||||
|
||||
override suspend fun setNarodmon(deviceId: String, enabled: Boolean): Result<Unit> =
|
||||
Result.success(Unit)
|
||||
|
||||
override suspend fun setLuftdata(deviceId: String, enabled: Boolean): Result<Unit> =
|
||||
Result.success(Unit)
|
||||
|
||||
override suspend fun setVisibility(deviceId: String, isPublic: Boolean): Result<Unit> =
|
||||
Result.success(Unit)
|
||||
|
||||
override suspend fun triggerFirmwareUpdate(deviceId: String): Result<Unit> =
|
||||
Result.success(Unit)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
import org.db3.airmq.sdk.device.data.DeviceRepositoryImpl
|
||||
import org.db3.airmq.sdk.device.data.local.DEVICE_DB_MIGRATION_1_2
|
||||
import org.db3.airmq.sdk.device.data.local.DEVICE_DB_MIGRATION_2_3
|
||||
import org.db3.airmq.sdk.device.data.local.DeviceDao
|
||||
import org.db3.airmq.sdk.device.data.local.DeviceDatabase
|
||||
import org.db3.airmq.sdk.device.data.local.PendingMutationDao
|
||||
@@ -28,7 +30,7 @@ object DeviceDatabaseModule {
|
||||
context,
|
||||
DeviceDatabase::class.java,
|
||||
"airmq_device_db"
|
||||
).build()
|
||||
).addMigrations(DEVICE_DB_MIGRATION_1_2, DEVICE_DB_MIGRATION_2_3).build()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
|
||||
@@ -26,11 +26,16 @@ enum class OnlineFreshness {
|
||||
*
|
||||
* @param id Unique device identifier
|
||||
* @param name Display name
|
||||
* @param model Device model identifier
|
||||
* @param model Device model type
|
||||
* @param firmwareVersion Firmware version string
|
||||
* @param deviceAddress Device IP address
|
||||
* @param configVersion Config version string
|
||||
* @param isNarodmonOn Whether Narodmon.ru data sharing is enabled
|
||||
* @param isLuftdataOn Whether Sensor.community data sharing is enabled
|
||||
* @param locationId Optional location identifier
|
||||
* @param latitude Optional latitude
|
||||
* @param longitude Optional longitude
|
||||
* @param isPublic Whether device location is published (visible to others)
|
||||
* @param city Optional city name for the location
|
||||
* @param dataSharingEnabled Whether data sharing is enabled
|
||||
* @param isOnline Whether the device is currently online
|
||||
@@ -40,11 +45,16 @@ enum class OnlineFreshness {
|
||||
data class Device(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val model: String,
|
||||
val model: DeviceModel,
|
||||
val firmwareVersion: String,
|
||||
val deviceAddress: String,
|
||||
val configVersion: String,
|
||||
val isNarodmonOn: Boolean,
|
||||
val isLuftdataOn: Boolean,
|
||||
val locationId: String? = null,
|
||||
val latitude: Double? = null,
|
||||
val longitude: Double? = null,
|
||||
val isPublic: Boolean = false,
|
||||
val city: String? = null,
|
||||
val dataSharingEnabled: Boolean = false,
|
||||
val isOnline: Boolean = false,
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package org.db3.airmq.sdk.device.domain
|
||||
|
||||
/**
|
||||
* Device model type. Maps to legacy numeric codes and string identifiers from API/DB.
|
||||
*/
|
||||
enum class DeviceModel(val displayName: String, private val storageValue: String) {
|
||||
Basic("Basic", "0"),
|
||||
Mobile("Mobile", "1"),
|
||||
Solar("Solar", "2"),
|
||||
Radiation("Radiation", "4"),
|
||||
Custom("Custom", "-1");
|
||||
|
||||
fun toStorageString(): String = storageValue
|
||||
|
||||
companion object {
|
||||
fun fromString(value: String?): DeviceModel = when (value?.lowercase()) {
|
||||
"0", "basic" -> Basic
|
||||
"1", "mobile" -> Mobile
|
||||
"2", "solar" -> Solar
|
||||
"4", "radiation" -> Radiation
|
||||
else -> Custom
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,22 @@ interface DeviceRepository {
|
||||
*/
|
||||
suspend fun setDataSharing(deviceId: String, enabled: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Enable or disable Narodmon.ru data sharing.
|
||||
*/
|
||||
suspend fun setNarodmon(deviceId: String, enabled: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Enable or disable Sensor.community (Luftdata) data sharing.
|
||||
*/
|
||||
suspend fun setLuftdata(deviceId: String, enabled: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Set device visibility (publish/hide location on map).
|
||||
* Only effective when device has location.
|
||||
*/
|
||||
suspend fun setVisibility(deviceId: String, isPublic: Boolean): Result<Unit>
|
||||
|
||||
/**
|
||||
* Trigger firmware update. Requires connectivity.
|
||||
*
|
||||
|
||||
@@ -6,7 +6,10 @@ package org.db3.airmq.sdk.device.domain
|
||||
enum class PendingMutationType {
|
||||
RENAME,
|
||||
LOCATION,
|
||||
DATA_SHARING
|
||||
DATA_SHARING,
|
||||
NARODMON,
|
||||
LUFTDATA,
|
||||
VISIBILITY
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user