feat(manage): rework Manage screen layout and device list

- Match device item design to reference (ic_chip, status chips, trailing icon)
- Add header (Your devices) and footer to device list
- CTA at bottom, centered 46% width, same style for Sign in/Add device
- Window background #FAFAFA, LegacyBackground on list area
- No spacing between device items, no horizontal margin on list
- Add DeviceItem.extra, ic_go_to_location drawable
- Replace personal data with User/user@example.com
- Strengthen app-recreation-core rule: path check before edits
- Restore real auth logic in ManageViewModel

Made-with: Cursor
This commit is contained in:
2026-03-02 21:51:50 +01:00
parent 9a80ce5dff
commit b607d0198b
15 changed files with 276 additions and 118 deletions

View File

@@ -5,13 +5,22 @@ alwaysApply: true
# AIRMQ Recreation Core Rules
## CRITICAL: Path check before any edit
**BEFORE editing any file, verify its path.**
- Path contains `airmq-android` but **NOT** `airmq-android-2026` → **NEVER EDIT.** Read-only reference.
- Path contains `airmq-android-2026` → OK to edit.
Example: `C:\Users\sysop\Desktop\airmq-android\app\...` → **DO NOT MODIFY.**
## Hard repository constraint
ALL CHANGES ONLY IN `airmq-android-2026` REPO; NO CHANGES EVER IN `airmq-android`.
## Repository boundaries
1. Do not modify anything under `C:\Users\sysop\Desktop\airmq-android`.
1. **NEVER** modify anything under `C:\Users\sysop\Desktop\airmq-android`.
2. Treat `airmq-android` as read-only reference only.
3. Create and apply all code/config/build changes only under `C:\Users\sysop\Desktop\airmq-android-2026`.

View File

@@ -2,6 +2,7 @@ package org.db3.airmq.features.manage
import androidx.compose.foundation.background
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -17,9 +18,6 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
@@ -38,6 +36,7 @@ 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
@@ -48,6 +47,7 @@ import org.db3.airmq.features.manage.ManageScreenContract.DeviceItem
import org.db3.airmq.features.manage.ManageScreenContract.Event
import org.db3.airmq.features.manage.ManageScreenContract.State
import org.db3.airmq.ui.theme.AirMQTheme
import org.db3.airmq.ui.theme.LegacyBackground
import org.db3.airmq.ui.theme.LegacyNavGradientEnd
import org.db3.airmq.ui.theme.LegacyNavGradientStart
@@ -58,8 +58,7 @@ fun ManageScreen(
onOpenSettings: () -> Unit,
onOpenLogin: () -> Unit,
onOpenLocation: () -> Unit,
onOpenWidgetConstructor: () -> Unit,
onBackToDashboard: () -> Unit,
onOpenAddLocation: () -> Unit,
viewModel: ManageViewModel = hiltViewModel()
) {
val uiState by viewModel.uiState.collectAsState()
@@ -71,23 +70,20 @@ fun ManageScreen(
Action.OpenSetup -> onOpenSetup()
is Action.OpenDevice -> onOpenDevice()
is Action.OpenLocation -> onOpenLocation()
is Action.OpenAddLocation -> onOpenAddLocation()
}
}
}
ManageScreenContent(
uiState = uiState,
onEvent = viewModel::onEvent,
onOpenWidgetConstructor = onOpenWidgetConstructor,
onBackToDashboard = onBackToDashboard
onEvent = viewModel::onEvent
)
}
@Composable
private fun ManageScreenContent(
uiState: State,
onEvent: (Event) -> Unit,
onOpenWidgetConstructor: () -> Unit,
onBackToDashboard: () -> Unit
onEvent: (Event) -> Unit
) {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Column(
@@ -104,33 +100,31 @@ private fun ManageScreenContent(
when (uiState.isAuthorized) {
false -> AnonymousContent(
modifier = Modifier.weight(1f),
devicesLabel = uiState.devicesLabel,
onSignIn = { onEvent(Event.SignInClicked) }
devicesLabel = uiState.devicesLabel
)
true -> AuthorizedContent(
devicesLabel = uiState.devicesLabel,
modifier = Modifier.weight(1f),
devices = uiState.devices,
onOpenSetup = { onEvent(Event.SetupClicked) },
onOpenDevice = { onEvent(Event.DeviceClicked(it)) },
onOpenLocation = { onEvent(Event.DeviceLocationClicked(it)) }
onOpenLocation = { onEvent(Event.DeviceLocationClicked(it)) },
onAddLocation = { onEvent.invoke(Event.AddDeviceLocationClicked(it))}
)
}
if (uiState.isAuthorized) {
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center
) {
AirMQButton(
text = stringResource(id = R.string.manage_open_widget_constructor),
onClick = onOpenWidgetConstructor,
text = if (uiState.isAuthorized) stringResource(id = R.string.button_setup)
else stringResource(id = R.string.button_sign_in),
onClick = if (uiState.isAuthorized) { { onEvent(Event.SetupClicked) } }
else { { onEvent(Event.SignInClicked) } },
style = AirMQButtonStyle.Gradient,
leadingIconRes = if (uiState.isAuthorized) null else R.drawable.ic_account,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
)
AirMQButton(
text = stringResource(id = R.string.back_to_dashboard),
onClick = onBackToDashboard,
style = AirMQButtonStyle.Outlined,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp)
.fillMaxWidth(0.46f)
.padding(bottom = 16.dp)
)
}
}
@@ -222,8 +216,7 @@ private fun ProfileHeader(
@Composable
private fun AnonymousContent(
modifier: Modifier = Modifier,
devicesLabel: String,
onSignIn: () -> Unit
devicesLabel: String
) {
Box(
modifier = modifier
@@ -236,94 +229,143 @@ private fun AnonymousContent(
color = Color(0xFFBDBDBD),
modifier = Modifier.align(Alignment.Center)
)
AirMQButton(
text = stringResource(id = R.string.button_sign_in),
onClick = onSignIn,
style = AirMQButtonStyle.Gradient,
leadingIconRes = R.drawable.ic_account,
modifier = Modifier
.align(Alignment.BottomCenter)
.fillMaxWidth(0.46f)
.padding(bottom = 16.dp)
)
}
}
@Composable
private fun AuthorizedContent(
devicesLabel: String,
modifier: Modifier = Modifier,
devices: List<DeviceItem>,
onOpenSetup: () -> Unit,
onOpenDevice: (String) -> Unit,
onOpenLocation: (String) -> Unit
onOpenLocation: (String) -> Unit,
onAddLocation: (String) -> Unit
) {
Column(
modifier = Modifier
modifier = modifier
.fillMaxWidth()
.padding(16.dp)
.background(LegacyBackground)
) {
Text(text = devicesLabel, style = MaterialTheme.typography.titleMedium)
Spacer(modifier = Modifier.height(12.dp))
AirMQButton(
text = stringResource(id = R.string.button_setup),
onClick = onOpenSetup,
style = AirMQButtonStyle.Gradient,
modifier = Modifier.fillMaxWidth()
)
Spacer(modifier = Modifier.height(12.dp))
LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
items(devices, key = { it.id }) { device ->
DeviceRow(
item = device,
onOpenDevice = { onOpenDevice(device.id) },
onOpenLocation = { onOpenLocation(device.id) }
if (devices.isEmpty()) {
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(horizontal = 16.dp, vertical = 8.dp)
) {
Text(
text = stringResource(id = R.string.text_nothing_to_show),
style = MaterialTheme.typography.headlineSmall.copy(fontWeight = FontWeight.Light),
color = Color(0xFF757575),
modifier = Modifier.align(Alignment.Center)
)
}
} else {
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(top = 8.dp),
verticalArrangement = Arrangement.Top
) {
item(key = "header") {
DeviceListHeader()
}
items(devices, key = { it.id }) { device ->
DeviceRow(
item = device,
onOpenDevice = { onOpenDevice(device.id) },
onOpenLocation = { onOpenLocation(device.id) },
onAddLocation = { onAddLocation(device.id) }
)
}
item(key = "footer") {
DeviceListFooter()
}
}
}
}
}
private val Black38 = Color(0x61000000)
@Composable
private fun DeviceListHeader() {
Text(
text = stringResource(id = R.string.text_your_devices),
modifier = Modifier
.fillMaxWidth()
.padding(start = 16.dp, top = 22.dp),
fontSize = 14.sp,
color = Black38,
fontWeight = FontWeight.Medium
)
}
@Composable
private fun DeviceListFooter() {
Spacer(modifier = Modifier.height(72.dp))
}
@Composable
private fun DeviceRow(
item: DeviceItem,
onOpenDevice: () -> Unit,
onOpenLocation: () -> Unit
onOpenLocation: () -> Unit,
onAddLocation: () -> Unit
) {
Card(
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
val isOnline = item.status.equals(stringResource(id = R.string.map_status_online), ignoreCase = true)
Row(
modifier = Modifier
.fillMaxWidth()
.height(72.dp)
.clickable(onClick = onOpenDevice)
.padding(horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp)
) {
Text(text = item.name, style = MaterialTheme.typography.titleMedium)
Image(
painter = painterResource(id = R.drawable.ic_chip),
contentDescription = stringResource(id = R.string.content_device_icon),
modifier = Modifier.size(40.dp)
)
Spacer(modifier = Modifier.width(16.dp))
Column(modifier = Modifier.weight(1f, fill = true)) {
Text(
text = item.status,
style = MaterialTheme.typography.bodyMedium,
color = Color.Gray
text = item.name,
fontSize = 16.sp,
color = Color.Black
)
Text(
text = item.extra,
fontSize = 14.sp,
color = Black38
)
}
Spacer(modifier = Modifier.width(16.dp))
Image(
painter = painterResource(
id = if (isOnline) R.drawable.device_chip_online else R.drawable.device_chip_offline
),
contentDescription = item.status,
modifier = Modifier
.align(Alignment.Top)
.padding(top = 18.dp)
)
val trailingIcon = if (item.hasLocation) R.drawable.ic_go_to_location else R.drawable.ic_warning
IconButton(
onClick = {
if (item.hasLocation) {
onOpenLocation.invoke()
} else {
onAddLocation.invoke()
}
},
modifier = Modifier.size(48.dp)
) {
Icon(
painter = painterResource(id = trailingIcon),
contentDescription = null,
tint = Black38
)
Spacer(modifier = Modifier.height(8.dp))
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
AirMQButton(
text = stringResource(id = R.string.button_open),
onClick = onOpenDevice,
style = AirMQButtonStyle.Contained,
modifier = Modifier.weight(1f)
)
AirMQButton(
text = if (item.hasLocation) {
stringResource(id = R.string.button_view_on_map)
} else {
stringResource(id = R.string.manage_set_location)
},
onClick = onOpenLocation,
style = AirMQButtonStyle.Outlined,
modifier = Modifier.weight(1f)
)
}
}
}
}
@@ -339,9 +381,7 @@ private fun ManageScreenAnonymousPreview() {
userEmail = "Your preferences are not being synced, please sign in",
devicesLabel = "Sign in to add devices"
),
onEvent = {},
onOpenWidgetConstructor = {},
onBackToDashboard = {}
onEvent = {}
)
}
}
@@ -353,17 +393,14 @@ private fun ManageScreenAuthorizedPreview() {
ManageScreenContent(
uiState = State(
isAuthorized = true,
userName = "Anton Betsun",
userEmail = "messbees@gmail.com",
devicesLabel = "My devices",
userName = "User",
userEmail = "user@example.com",
devices = listOf(
DeviceItem("1", "AirMQ #1", "Online", true),
DeviceItem("2", "AirMQ #2", "Offline", false)
DeviceItem("1", "AirMQ #1", "mobile", "Online", true),
DeviceItem("2", "AirMQ #2", "mobile", "Offline", false)
)
),
onEvent = {},
onOpenWidgetConstructor = {},
onBackToDashboard = {}
onEvent = {}
)
}
}

View File

@@ -4,6 +4,7 @@ object ManageScreenContract {
data class DeviceItem(
val id: String,
val name: String,
val extra: String,
val status: String,
val hasLocation: Boolean
)
@@ -22,6 +23,7 @@ object ManageScreenContract {
data object OpenLogin : Action
data class OpenDevice(val deviceId: String) : Action
data class OpenLocation(val deviceId: String) : Action
data class OpenAddLocation(val deviceId: String) : Action
}
sealed interface Event {
@@ -30,5 +32,6 @@ object ManageScreenContract {
data object SignInClicked : Event
data class DeviceClicked(val deviceId: String) : Event
data class DeviceLocationClicked(val deviceId: String) : Event
data class AddDeviceLocationClicked(val deviceId: String) : Event
}
}

View File

@@ -44,6 +44,7 @@ class ManageViewModel @Inject constructor(
Event.SignInClicked -> _actions.tryEmit(Action.OpenLogin)
is Event.DeviceClicked -> _actions.tryEmit(Action.OpenDevice(event.deviceId))
is Event.DeviceLocationClicked -> _actions.tryEmit(Action.OpenLocation(event.deviceId))
is Event.AddDeviceLocationClicked -> _actions.tryEmit(Action.OpenAddLocation(event.deviceId))
}
}
@@ -71,17 +72,19 @@ class ManageViewModel @Inject constructor(
isAuthorized = true,
userName = user.displayName ?: appContext.getString(R.string.text_anonymous_user),
userEmail = user.email ?: "",
devicesLabel = appContext.getString(R.string.text_your_devices),
devicesLabel = "",
devices = listOf(
DeviceItem(
id = "device-1",
name = appContext.getString(R.string.mock_device_name_42),
extra = "mobile",
status = appContext.getString(R.string.map_status_online),
hasLocation = true
),
DeviceItem(
id = "device-2",
name = appContext.getString(R.string.mock_device_name_17),
extra = "mobile",
status = appContext.getString(R.string.map_status_offline),
hasLocation = false
)

View File

@@ -152,8 +152,7 @@ fun AirMQNavGraph(modifier: Modifier = Modifier) {
onOpenSettings = { navController.navigate(AirMqRoutes.SETTINGS) },
onOpenLogin = { navController.navigate(AirMqRoutes.LOGIN) },
onOpenLocation = { navController.navigate(AirMqRoutes.LOCATION) },
onOpenWidgetConstructor = { navController.navigate(AirMqRoutes.WIDGET_CONSTRUCTOR) },
onBackToDashboard = { navController.navigate(AirMqRoutes.DASHBOARD) }
onOpenAddLocation = { /* TODO */ }
)
}
composable(

View File

@@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="52dp"
android:height="18dp"
android:viewportWidth="52"
android:viewportHeight="18">
<path
android:pathData="M43,18H9c-4.97,0 -9,-4.03 -9,-9v0c0,-4.97 4.03,-9 9,-9l34,0c4.95,0 9,4.05 9,9v0C52,13.97 47.97,18 43,18z"
android:fillColor="#F1AF00"/>
<path
android:pathData="M10.4,9.51c0,-0.62 0.12,-1.18 0.37,-1.68s0.58,-0.88 1.02,-1.15s0.93,-0.4 1.49,-0.4c0.86,0 1.56,0.3 2.09,0.9s0.8,1.39 0.8,2.38v0.08c0,0.62 -0.12,1.17 -0.35,1.66s-0.57,0.87 -1.01,1.15s-0.94,0.41 -1.51,0.41c-0.86,0 -1.56,-0.3 -2.09,-0.9s-0.8,-1.39 -0.8,-2.37V9.51zM11.49,9.64c0,0.7 0.16,1.27 0.49,1.69s0.76,0.64 1.31,0.64c0.55,0 0.99,-0.22 1.31,-0.65s0.49,-1.04 0.49,-1.81c0,-0.7 -0.17,-1.26 -0.5,-1.69s-0.77,-0.65 -1.32,-0.65c-0.54,0 -0.97,0.21 -1.29,0.64S11.49,8.85 11.49,9.64z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M18.06,12.74v-5.5h-1V6.4h1V5.75c0,-0.68 0.18,-1.21 0.54,-1.58s0.88,-0.56 1.54,-0.56c0.25,0 0.5,0.03 0.74,0.1l-0.06,0.87c-0.18,-0.04 -0.38,-0.05 -0.59,-0.05c-0.35,0 -0.62,0.1 -0.81,0.31s-0.29,0.5 -0.29,0.88V6.4h1.35v0.84h-1.35v5.5H18.06z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M22.23,12.74v-5.5h-1V6.4h1V5.75c0,-0.68 0.18,-1.21 0.54,-1.58s0.88,-0.56 1.54,-0.56c0.25,0 0.5,0.03 0.74,0.1L25,4.59c-0.18,-0.04 -0.38,-0.05 -0.59,-0.05c-0.35,0 -0.62,0.1 -0.81,0.31s-0.29,0.5 -0.29,0.88V6.4h1.35v0.84h-1.35v5.5H22.23z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M27.04,12.74h-1.08v-9h1.08V12.74z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M28.78,4.72c0,-0.18 0.05,-0.32 0.16,-0.45s0.27,-0.18 0.48,-0.18s0.37,0.06 0.48,0.18s0.16,0.27 0.16,0.45s-0.05,0.32 -0.16,0.44s-0.27,0.18 -0.48,0.18s-0.37,-0.06 -0.48,-0.18S28.78,4.89 28.78,4.72zM29.95,12.74h-1.08V6.4h1.08V12.74z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M32.71,6.4l0.04,0.8c0.48,-0.61 1.12,-0.91 1.9,-0.91c1.34,0 2.02,0.76 2.03,2.27v4.19h-1.08v-4.2c0,-0.46 -0.11,-0.79 -0.31,-1.01S34.75,7.2 34.32,7.2c-0.35,0 -0.66,0.09 -0.93,0.28s-0.47,0.43 -0.62,0.74v4.52h-1.08V6.4H32.71z"
android:fillColor="#FFFFFF"/>
<path
android:pathData="M40.94,12.85c-0.86,0 -1.56,-0.28 -2.1,-0.85s-0.81,-1.32 -0.81,-2.26v-0.2c0,-0.63 0.12,-1.19 0.36,-1.68s0.58,-0.88 1.01,-1.16s0.9,-0.42 1.4,-0.42c0.82,0 1.46,0.27 1.92,0.81s0.69,1.32 0.69,2.33v0.45h-4.29c0.02,0.63 0.2,1.13 0.55,1.51s0.79,0.58 1.33,0.58c0.38,0 0.71,-0.08 0.97,-0.23s0.5,-0.36 0.7,-0.62l0.66,0.52C42.8,12.45 42,12.85 40.94,12.85zM40.8,7.17c-0.44,0 -0.8,0.16 -1.1,0.48s-0.48,0.76 -0.55,1.34h3.18V8.91c-0.03,-0.55 -0.18,-0.98 -0.45,-1.28S41.26,7.17 40.8,7.17z"
android:fillColor="#FFFFFF"/>
</vector>

View File

@@ -0,0 +1,41 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="52dp"
android:height="18dp"
android:viewportWidth="52"
android:viewportHeight="18">
<path
android:pathData="M43.05,18H8.95C4.01,18 0,13.99 0,9.05l0,-0.09C0,4.01 4.01,0 8.95,0l34.09,0C47.97,0 52,4.03 52,8.95v0.09C52,13.99 47.99,18 43.05,18z"
android:strokeAlpha="0"
android:fillColor="#F1AF00"
android:fillAlpha="0"/>
<path
android:fillColor="#FF000000"
android:pathData="M10.4,9.51c0,-0.62 0.12,-1.18 0.37,-1.68s0.58,-0.88 1.02,-1.15s0.93,-0.4 1.49,-0.4c0.86,0 1.56,0.3 2.09,0.9s0.8,1.39 0.8,2.38v0.08c0,0.62 -0.12,1.17 -0.35,1.66s-0.57,0.87 -1.01,1.15s-0.94,0.41 -1.51,0.41c-0.86,0 -1.56,-0.3 -2.09,-0.9s-0.8,-1.39 -0.8,-2.37V9.51zM11.49,9.64c0,0.7 0.16,1.27 0.49,1.69s0.76,0.64 1.31,0.64c0.55,0 0.99,-0.22 1.31,-0.65s0.49,-1.04 0.49,-1.81c0,-0.7 -0.17,-1.26 -0.5,-1.69s-0.77,-0.65 -1.32,-0.65c-0.54,0 -0.97,0.21 -1.29,0.64S11.49,8.85 11.49,9.64z"
android:strokeAlpha="0.38"
android:fillAlpha="0.38"/>
<path
android:fillColor="#FF000000"
android:pathData="M18.56,6.4l0.04,0.8c0.48,-0.61 1.12,-0.91 1.9,-0.91c1.34,0 2.02,0.76 2.03,2.27v4.19h-1.08v-4.2c0,-0.46 -0.11,-0.79 -0.31,-1.01S20.59,7.2 20.16,7.2c-0.35,0 -0.66,0.09 -0.93,0.28s-0.47,0.43 -0.62,0.74v4.52h-1.08V6.4H18.56z"
android:strokeAlpha="0.38"
android:fillAlpha="0.38"/>
<path
android:fillColor="#FF000000"
android:pathData="M25.33,12.74h-1.08v-9h1.08V12.74z"
android:strokeAlpha="0.38"
android:fillAlpha="0.38"/>
<path
android:fillColor="#FF000000"
android:pathData="M27.07,4.72c0,-0.18 0.05,-0.32 0.16,-0.45s0.27,-0.18 0.48,-0.18s0.37,0.06 0.48,0.18s0.16,0.27 0.16,0.45s-0.05,0.32 -0.16,0.44s-0.27,0.18 -0.48,0.18s-0.37,-0.06 -0.48,-0.18S27.07,4.89 27.07,4.72zM28.24,12.74h-1.08V6.4h1.08V12.74z"
android:strokeAlpha="0.38"
android:fillAlpha="0.38"/>
<path
android:fillColor="#FF000000"
android:pathData="M31,6.4l0.04,0.8c0.48,-0.61 1.12,-0.91 1.9,-0.91c1.34,0 2.02,0.76 2.03,2.27v4.19h-1.08v-4.2c0,-0.46 -0.11,-0.79 -0.31,-1.01S33.04,7.2 32.61,7.2c-0.35,0 -0.66,0.09 -0.93,0.28s-0.47,0.43 -0.62,0.74v4.52h-1.08V6.4H31z"
android:strokeAlpha="0.38"
android:fillAlpha="0.38"/>
<path
android:fillColor="#FF000000"
android:pathData="M39.23,12.86c-0.86,0 -1.56,-0.28 -2.1,-0.85s-0.81,-1.32 -0.81,-2.26v-0.2c0,-0.63 0.12,-1.19 0.36,-1.68s0.58,-0.88 1.01,-1.16s0.9,-0.42 1.4,-0.42c0.82,0 1.46,0.27 1.92,0.81s0.69,1.32 0.69,2.33v0.45h-4.29c0.02,0.63 0.2,1.13 0.55,1.51s0.79,0.58 1.33,0.58c0.38,0 0.71,-0.08 0.97,-0.23s0.5,-0.36 0.7,-0.62l0.66,0.52C41.09,12.45 40.29,12.86 39.23,12.86zM39.09,7.17c-0.44,0 -0.8,0.16 -1.1,0.48s-0.48,0.76 -0.55,1.34h3.18V8.91c-0.03,-0.55 -0.18,-0.98 -0.45,-1.28S39.55,7.17 39.09,7.17z"
android:strokeAlpha="0.38"
android:fillAlpha="0.38"/>
</vector>

View File

@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#333333"
android:alpha="0.6">
<path
android:fillColor="@android:color/white"
android:pathData="M14,9h-4c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1h4c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1zM13,13h-2v-2h2v2zM21,10c0,-0.55 -0.45,-1 -1,-1h-1L19,7c0,-1.1 -0.9,-2 -2,-2h-2L15,4c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v1h-2L11,4c0,-0.55 -0.45,-1 -1,-1s-1,0.45 -1,1v1L7,5c-1.1,0 -2,0.9 -2,2v2L4,9c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h1v2L4,13c-0.55,0 -1,0.45 -1,1s0.45,1 1,1h1v2c0,1.1 0.9,2 2,2h2v1c0,0.55 0.45,1 1,1s1,-0.45 1,-1v-1h2v1c0,0.55 0.45,1 1,1s1,-0.45 1,-1v-1h2c1.1,0 2,-0.9 2,-2v-2h1c0.55,0 1,-0.45 1,-1s-0.45,-1 -1,-1h-1v-2h1c0.55,0 1,-0.45 1,-1zM16,17L8,17c-0.55,0 -1,-0.45 -1,-1L7,8c0,-0.55 0.45,-1 1,-1h8c0.55,0 1,0.45 1,1v8c0,0.55 -0.45,1 -1,1z"/>
</vector>

View 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="M12.01,0C7.24,0 3.38,3.86 3.38,8.63c0,5.14 5.45,12.23 7.69,14.93c0.49,0.59 1.39,0.59 1.89,0c2.23,-2.7 7.67,-9.79 7.67,-14.93C20.62,3.86 16.77,0 12.01,0zM15.45,9.43l-2.74,2.74c-0.28,0.28 -0.75,0.32 -1.06,0.07c-0.37,-0.32 -0.37,-0.84 -0.05,-1.15l1.44,-1.44h-4.2c-0.45,0 -0.82,-0.37 -0.82,-0.82C8.03,8.37 8.39,8 8.85,8h4.19l-1.4,-1.43c-0.3,-0.29 -0.29,-0.8 0,-1.1c0.3,-0.31 0.79,-0.3 1.09,0.01l2.74,2.8C15.75,8.58 15.75,9.11 15.45,9.43zM18.57,8.68c0,3.63 -2.94,6.57 -6.57,6.57S5.43,12.3 5.43,8.68S8.37,2.11 12,2.11S18.57,5.05 18.57,8.68z"/>
</vector>

View File

@@ -0,0 +1,11 @@
<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="M13.69,2.14C6.85,1.01 1.01,6.85 2.14,13.69c0.68,4.14 4.02,7.48 8.17,8.17c6.84,1.12 12.68,-4.71 11.55,-11.55C21.18,6.16 17.84,2.82 13.69,2.14zM12,13.21c-0.73,0 -1.33,-0.52 -1.33,-1.16V7.39c0,-0.64 0.6,-1.16 1.33,-1.16s1.33,0.52 1.33,1.16v4.66C13.33,12.69 12.73,13.21 12,13.21zM12,17.49L12,17.49c-0.73,0 -1.33,-0.59 -1.33,-1.33v0c0,-0.73 0.59,-1.33 1.33,-1.33h0c0.73,0 1.33,0.59 1.33,1.33v0C13.33,16.9 12.73,17.49 12,17.49z"
android:strokeAlpha="0.54"
android:fillAlpha="0.54"/>
</vector>

View File

@@ -270,8 +270,8 @@
<string name="widget_chart_constructor_title">Канструктар графіка</string>
<string name="widget_news_constructor_title">Канструктар навін</string>
<string name="back_to_news">Назад да навін</string>
<string name="mock_user_name">Anton Betsun</string>
<string name="mock_user_email">messbees@gmail.com</string>
<string name="mock_user_name">User</string>
<string name="mock_user_email">user@example.com</string>
<string name="mock_device_name_42">AirMQ #42</string>
<string name="mock_device_name_17">AirMQ #17</string>
</resources>

View File

@@ -270,8 +270,8 @@
<string name="widget_chart_constructor_title">Конструктор графика</string>
<string name="widget_news_constructor_title">Конструктор новостей</string>
<string name="back_to_news">Назад к новостям</string>
<string name="mock_user_name">Anton Betsun</string>
<string name="mock_user_email">messbees@gmail.com</string>
<string name="mock_user_name">User</string>
<string name="mock_user_email">user@example.com</string>
<string name="mock_device_name_42">AirMQ #42</string>
<string name="mock_device_name_17">AirMQ #17</string>
</resources>

View File

@@ -14,4 +14,6 @@
<color name="sensorRed">#FFF44336</color>
<color name="sensorPink">#FFEC407A</color>
<color name="sensorPurple">#FF8E24AA</color>
<color name="black38">#61000000</color>
<color name="windowBackground">#FAFAFA</color>
</resources>

View File

@@ -18,7 +18,7 @@
<string name="dialog_enable_wifi_title">Enable WiFi</string>
<string name="dialog_are_you_sure">Are you sure?</string>
<string name="dialog_about_title">About</string>
<string name="dialog_about_text" translatable="false">App by Anton Betsun \nDesign by Alexander Prokhorov</string>
<string name="dialog_about_text" translatable="false">App by AirMQ\nDesign by Alexander Prokhorov</string>
<string name="dialog_anonym_title">Continue anonymously?</string>
<string name="dialog_anonym_mesage">You will not be able to back up your preferences and add new AirMQ devices</string>
<string name="dialog_select_device_title">Select device</string>
@@ -326,8 +326,8 @@
<string name="widget_chart_constructor_title">Chart Constructor</string>
<string name="widget_news_constructor_title">News Constructor</string>
<string name="back_to_news">Back to News</string>
<string name="mock_user_name">Anton Betsun</string>
<string name="mock_user_email">messbees@gmail.com</string>
<string name="mock_user_name">User</string>
<string name="mock_user_email">user@example.com</string>
<string name="mock_device_name_42">AirMQ #42</string>
<string name="mock_device_name_17">AirMQ #17</string>
</resources>

View File

@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.AirMQ" parent="android:Theme.Material.Light.NoActionBar" />
<style name="Theme.AirMQ" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:windowBackground">@color/windowBackground</item>
<item name="android:colorBackground">@color/windowBackground</item>
</style>
</resources>