diff --git a/app/src/main/kotlin/org/db3/airmq/features/city/CityScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/city/CityScreen.kt index 70f415f..40b790a 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/city/CityScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/city/CityScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.city import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun CityScreen(onBackToDashboard: () -> Unit) { MockScreenScaffold( - title = "City", - subtitle = "Mock city management screen.", - actions = listOf(ScreenAction("Back to Dashboard", onBackToDashboard)) + title = stringResource(id = R.string.title_city), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(stringResource(id = R.string.back_to_dashboard), onBackToDashboard)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/constructor/ChartConstructorScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/constructor/ChartConstructorScreen.kt index 52f2f5a..0b60438 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/constructor/ChartConstructorScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/constructor/ChartConstructorScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.constructor import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun ChartConstructorScreen(onBackToWidgetConstructor: () -> Unit) { MockScreenScaffold( - title = "Chart Constructor", - subtitle = "Mock chart widget constructor variant.", - actions = listOf(ScreenAction("Back to Widget Constructor", onBackToWidgetConstructor)) + title = stringResource(id = R.string.widget_chart_constructor_title), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(stringResource(id = R.string.back_to_widget_constructor), onBackToWidgetConstructor)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/constructor/MapConstructorScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/constructor/MapConstructorScreen.kt index 93dd456..0ceb2b1 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/constructor/MapConstructorScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/constructor/MapConstructorScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.constructor import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun MapConstructorScreen(onBackToWidgetConstructor: () -> Unit) { MockScreenScaffold( - title = "Map Constructor", - subtitle = "Mock map widget constructor variant.", - actions = listOf(ScreenAction("Back to Widget Constructor", onBackToWidgetConstructor)) + title = stringResource(id = R.string.widget_map_constructor_title), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(stringResource(id = R.string.back_to_widget_constructor), onBackToWidgetConstructor)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/constructor/NewsConstructorScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/constructor/NewsConstructorScreen.kt index 319e852..f517f1f 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/constructor/NewsConstructorScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/constructor/NewsConstructorScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.constructor import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun NewsConstructorScreen(onBackToWidgetConstructor: () -> Unit) { MockScreenScaffold( - title = "News Constructor", - subtitle = "Mock news widget constructor variant.", - actions = listOf(ScreenAction("Back to Widget Constructor", onBackToWidgetConstructor)) + title = stringResource(id = R.string.widget_news_constructor_title), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(stringResource(id = R.string.back_to_widget_constructor), onBackToWidgetConstructor)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/constructor/SelectMapWidgetLocationScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/constructor/SelectMapWidgetLocationScreen.kt index 6b459c6..27758f1 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/constructor/SelectMapWidgetLocationScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/constructor/SelectMapWidgetLocationScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.constructor import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun SelectMapWidgetLocationScreen(onDone: () -> Unit) { MockScreenScaffold( - title = "Select Map Widget Location", - subtitle = "Mock map location picker for widget.", - actions = listOf(ScreenAction("Done", onDone)) + title = stringResource(id = R.string.widget_select_map_location), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(stringResource(id = R.string.toast_done), onDone)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/constructor/WidgetConstructorScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/constructor/WidgetConstructorScreen.kt index 82ed4f8..b45ecb2 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/constructor/WidgetConstructorScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/constructor/WidgetConstructorScreen.kt @@ -1,6 +1,8 @@ package org.db3.airmq.features.constructor import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @@ -13,14 +15,14 @@ fun WidgetConstructorScreen( onBackToManage: () -> Unit ) { MockScreenScaffold( - title = "Widget Constructor", - subtitle = "Select constructor variant.", + title = stringResource(id = R.string.title_widget_constructor), + subtitle = stringResource(id = R.string.coming_soon), actions = listOf( - ScreenAction("Select Map Widget Location", onOpenSelectMapWidgetLocation), - ScreenAction("Open Map Constructor", onOpenMapConstructor), - ScreenAction("Open Chart Constructor", onOpenChartConstructor), - ScreenAction("Open News Constructor", onOpenNewsConstructor), - ScreenAction("Back to Manage", onBackToManage) + ScreenAction(stringResource(id = R.string.widget_select_map_location), onOpenSelectMapWidgetLocation), + ScreenAction(stringResource(id = R.string.widget_open_map_constructor), onOpenMapConstructor), + ScreenAction(stringResource(id = R.string.widget_open_chart_constructor), onOpenChartConstructor), + ScreenAction(stringResource(id = R.string.widget_open_news_constructor), onOpenNewsConstructor), + ScreenAction(stringResource(id = R.string.back_to_manage), onBackToManage) ) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/dashboard/DashboardScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/dashboard/DashboardScreen.kt index 8565ec3..7425fb2 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/dashboard/DashboardScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/dashboard/DashboardScreen.kt @@ -1,6 +1,8 @@ package org.db3.airmq.features.dashboard import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @@ -14,15 +16,15 @@ fun DashboardScreen( onOpenWidgetConstructor: () -> Unit ) { MockScreenScaffold( - title = "Dashboard", - subtitle = "Bottom-tab equivalent: dashboard", + title = stringResource(id = R.string.title_dashboard), + subtitle = stringResource(id = R.string.dashboard_subtitle), actions = listOf( - ScreenAction("Open Map", onOpenMap), - ScreenAction("Open Manage", onOpenManage), - ScreenAction("Open City", onOpenCity), - ScreenAction("Open Device", onOpenDevice), - ScreenAction("Open News", onOpenNews), - ScreenAction("Open Widget Constructor", onOpenWidgetConstructor) + ScreenAction(stringResource(id = R.string.dashboard_open_map), onOpenMap), + ScreenAction(stringResource(id = R.string.dashboard_open_manage), onOpenManage), + ScreenAction(stringResource(id = R.string.dashboard_open_city), onOpenCity), + ScreenAction(stringResource(id = R.string.dashboard_open_device), onOpenDevice), + ScreenAction(stringResource(id = R.string.dashboard_open_news), onOpenNews), + ScreenAction(stringResource(id = R.string.manage_open_widget_constructor), onOpenWidgetConstructor) ) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/debug/DebugScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/debug/DebugScreen.kt index e075c42..65f66ba 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/debug/DebugScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/debug/DebugScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.debug import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun DebugScreen(onBackToSettings: () -> Unit) { MockScreenScaffold( - title = "Debug", - subtitle = "Debug-only tools placeholder.", - actions = listOf(ScreenAction("Back to Settings", onBackToSettings)) + title = stringResource(id = R.string.title_debug), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(stringResource(id = R.string.back_to_settings), onBackToSettings)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/device/DeviceScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/device/DeviceScreen.kt index e011321..9d92d35 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/device/DeviceScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/device/DeviceScreen.kt @@ -1,6 +1,8 @@ package org.db3.airmq.features.device import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @@ -11,11 +13,11 @@ fun DeviceScreen( onShowOnMap: () -> Unit ) { MockScreenScaffold( - title = "Device", - subtitle = "Mock deviceId: $deviceId", + title = stringResource(id = R.string.title_device), + subtitle = stringResource(id = R.string.coming_soon), actions = listOf( - ScreenAction("Select Location", onOpenLocation), - ScreenAction("Show on Map", onShowOnMap) + ScreenAction(stringResource(id = R.string.title_location), onOpenLocation), + ScreenAction(stringResource(id = R.string.button_view_on_map), onShowOnMap) ) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/entry/SplashScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/entry/SplashScreen.kt index bc0ee6c..7ee5048 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/entry/SplashScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/entry/SplashScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.entry import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun SplashScreen(onContinue: () -> Unit) { MockScreenScaffold( - title = "Splash", - subtitle = "Entry flow starting point.", - actions = listOf(ScreenAction(label = "Continue to Wizard", onClick = onContinue)) + title = stringResource(id = R.string.screen_splash_title), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(label = stringResource(id = R.string.screen_continue_to_wizard), onClick = onContinue)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/entry/WizardScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/entry/WizardScreen.kt index c101d41..92c50d7 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/entry/WizardScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/entry/WizardScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.entry import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun WizardScreen(onFinish: () -> Unit) { MockScreenScaffold( - title = "Wizard", - subtitle = "Mock onboarding/wizard flow.", - actions = listOf(ScreenAction(label = "Finish Wizard", onClick = onFinish)) + title = stringResource(id = R.string.screen_wizard_title), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(label = stringResource(id = R.string.screen_finish_wizard), onClick = onFinish)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/location/LocationScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/location/LocationScreen.kt index 19c9ae1..1f13d0a 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/location/LocationScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/location/LocationScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.location import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun LocationScreen(onBackToManage: () -> Unit) { MockScreenScaffold( - title = "Location", - subtitle = "Mock location picker/editor screen.", - actions = listOf(ScreenAction("Back to Manage", onBackToManage)) + title = stringResource(id = R.string.title_location), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(stringResource(id = R.string.back_to_manage), onBackToManage)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/login/LoginScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/login/LoginScreen.kt index a3030a2..50e3e13 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/login/LoginScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/login/LoginScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.login import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun LoginScreen(onLogInToManage: () -> Unit) { MockScreenScaffold( - title = "Login", - subtitle = "Mock account sign-in screen.", - actions = listOf(ScreenAction("Log In to Manage", onLogInToManage)) + title = stringResource(id = R.string.button_sign_in), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(stringResource(id = R.string.screen_log_in_to_manage), onLogInToManage)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/manage/ManageScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/manage/ManageScreen.kt index 97ce5fa..fba7fc5 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/manage/ManageScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/manage/ManageScreen.kt @@ -27,11 +27,13 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color +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.hilt.navigation.compose.hiltViewModel import kotlinx.coroutines.flow.collectLatest +import org.db3.airmq.R import org.db3.airmq.features.common.AirMqButton import org.db3.airmq.features.common.AirMqButtonStyle import org.db3.airmq.features.manage.ManageScreenContract.Action @@ -107,14 +109,14 @@ private fun ManageScreenContent( } Spacer(modifier = Modifier.height(8.dp)) AirMqButton( - text = "Open Widget Constructor", + text = stringResource(id = R.string.manage_open_widget_constructor), onClick = onOpenWidgetConstructor, modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp) ) AirMqButton( - text = "Back to Dashboard", + text = stringResource(id = R.string.back_to_dashboard), onClick = onBackToDashboard, style = AirMqButtonStyle.Outlined, modifier = Modifier @@ -147,12 +149,12 @@ private fun ProfileHeader( verticalAlignment = Alignment.CenterVertically ) { Text( - text = "Manage", + text = stringResource(id = R.string.title_manage), style = MaterialTheme.typography.headlineSmall, color = Color.White ) AirMqButton( - text = "Settings", + text = stringResource(id = R.string.title_settings), onClick = onSettingsClick, style = AirMqButtonStyle.Text ) @@ -205,7 +207,7 @@ private fun AnonymousContent( ) Spacer(modifier = Modifier.height(16.dp)) AirMqButton( - text = "Sign in", + text = stringResource(id = R.string.button_sign_in), onClick = onSignIn, style = AirMqButtonStyle.Gradient, modifier = Modifier.fillMaxWidth() @@ -229,7 +231,7 @@ private fun AuthorizedContent( Text(text = devicesLabel, style = MaterialTheme.typography.titleMedium) Spacer(modifier = Modifier.height(12.dp)) AirMqButton( - text = "Setup", + text = stringResource(id = R.string.button_setup), onClick = onOpenSetup, style = AirMqButtonStyle.Gradient, modifier = Modifier.fillMaxWidth() @@ -272,13 +274,17 @@ private fun DeviceRow( Spacer(modifier = Modifier.height(8.dp)) Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) { AirMqButton( - text = "Open", + text = stringResource(id = R.string.button_open), onClick = onOpenDevice, style = AirMqButtonStyle.Contained, modifier = Modifier.weight(1f) ) AirMqButton( - text = if (item.hasLocation) "Show on map" else "Set location", + 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) diff --git a/app/src/main/kotlin/org/db3/airmq/features/manage/ManageScreenContract.kt b/app/src/main/kotlin/org/db3/airmq/features/manage/ManageScreenContract.kt index 8a052fb..ec4b885 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/manage/ManageScreenContract.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/manage/ManageScreenContract.kt @@ -16,9 +16,9 @@ object ManageScreenContract { data class State( val userMode: UserMode = UserMode.ANONYMOUS, - val userName: String = "Anonymous", - val userEmail: String = "Please sign in to access your devices.", - val devicesLabel: String = "Sign in to see your devices", + val userName: String = "", + val userEmail: String = "", + val devicesLabel: String = "", val devices: List = emptyList() ) diff --git a/app/src/main/kotlin/org/db3/airmq/features/manage/ManageViewModel.kt b/app/src/main/kotlin/org/db3/airmq/features/manage/ManageViewModel.kt index 73220ba..241f8ce 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/manage/ManageViewModel.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/manage/ManageViewModel.kt @@ -1,7 +1,9 @@ package org.db3.airmq.features.manage +import android.content.Context import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow @@ -9,6 +11,7 @@ import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import org.db3.airmq.R import org.db3.airmq.features.manage.ManageScreenContract.Action import org.db3.airmq.features.manage.ManageScreenContract.DeviceItem import org.db3.airmq.features.manage.ManageScreenContract.Event @@ -16,7 +19,9 @@ import org.db3.airmq.features.manage.ManageScreenContract.State import org.db3.airmq.features.manage.ManageScreenContract.UserMode @HiltViewModel -class ManageViewModel @Inject constructor() : ViewModel() { +class ManageViewModel @Inject constructor( + @ApplicationContext private val appContext: Context +) : ViewModel() { // Temporary migration stub: keep screen in anonymous mode. private val forceAnonymous = true @@ -45,24 +50,27 @@ class ManageViewModel @Inject constructor() : ViewModel() { } return when (mode) { UserMode.ANONYMOUS -> State( - userMode = UserMode.ANONYMOUS + userMode = UserMode.ANONYMOUS, + userName = appContext.getString(R.string.text_anonymous_user), + userEmail = appContext.getString(R.string.text_please_sign_in), + devicesLabel = appContext.getString(R.string.text_sign_in_small) ) UserMode.AUTHORIZED -> State( userMode = UserMode.AUTHORIZED, - userName = "Anton Betsun", - userEmail = "messbees@gmail.com", - devicesLabel = "My devices", + userName = appContext.getString(R.string.mock_user_name), + userEmail = appContext.getString(R.string.mock_user_email), + devicesLabel = appContext.getString(R.string.text_your_devices), devices = listOf( DeviceItem( id = "device-1", - name = "AirMQ #42", - status = "Online", + name = appContext.getString(R.string.mock_device_name_42), + status = appContext.getString(R.string.map_status_online), hasLocation = true ), DeviceItem( id = "device-2", - name = "AirMQ #17", - status = "Offline", + name = appContext.getString(R.string.mock_device_name_17), + status = appContext.getString(R.string.map_status_offline), hasLocation = false ) ) diff --git a/app/src/main/kotlin/org/db3/airmq/features/map/MapScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/map/MapScreen.kt index f7bb873..323010e 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/map/MapScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/map/MapScreen.kt @@ -67,7 +67,7 @@ fun MapScreen( // Stub for future navigation integration. Toast.makeText( context, - "Open device ${action.deviceId} is not wired yet", + context.getString(R.string.map_open_device_not_wired, action.deviceId), Toast.LENGTH_SHORT ).show() } @@ -110,7 +110,7 @@ private fun MapScreenContent( onHelpClick = { Toast.makeText( context, - context.getString(R.string.map_sensor_help_title), + context.getString(R.string.text_what_does_it_mean_title), Toast.LENGTH_SHORT ).show() }, @@ -209,7 +209,11 @@ private fun AirMQMap( val marker = Marker(map).apply { position = GeoPoint(item.latitude, item.longitude) title = listOfNotNull(item.title, item.city).joinToString(" - ") - subDescription = if (item.isOnline) "Online" else "Offline" + subDescription = if (item.isOnline) { + context.getString(R.string.map_status_online) + } else { + context.getString(R.string.map_status_offline) + } setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) icon = createMarkerIcon(map, item) setOnMarkerClickListener { _, _ -> diff --git a/app/src/main/kotlin/org/db3/airmq/features/map/MapScreenContract.kt b/app/src/main/kotlin/org/db3/airmq/features/map/MapScreenContract.kt index 6d1e6b5..c178d46 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/map/MapScreenContract.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/map/MapScreenContract.kt @@ -31,7 +31,7 @@ object MapScreenContract { val name: String, val status: String, val selectedRange: TimeRange = TimeRange.DAY, - val displayedDateRange: String = "Today", + val displayedDateRange: String = "", val selectedSensor: DeviceSensorType = DeviceSensorType.TEMPERATURE ) diff --git a/app/src/main/kotlin/org/db3/airmq/features/map/MapUiComponents.kt b/app/src/main/kotlin/org/db3/airmq/features/map/MapUiComponents.kt index 188b605..e5f42a4 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/map/MapUiComponents.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/map/MapUiComponents.kt @@ -61,8 +61,8 @@ fun MapTopControls( var isExpanded by remember(initiallyExpanded) { mutableStateOf(initiallyExpanded) } val arrowRotation = if (isExpanded) 180f else 0f val selectedLabel = when (selectedSensor) { - SensorType.DUST -> stringResource(id = R.string.map_sensor_air_quality) - SensorType.RADIOACTIVITY -> stringResource(id = R.string.map_sensor_radioactivity) + SensorType.DUST -> stringResource(id = R.string.text_air_quality) + SensorType.RADIOACTIVITY -> stringResource(id = R.string.sensor_radioactivity) } Box( @@ -117,7 +117,7 @@ fun MapTopControls( Column(modifier = Modifier.padding(top = 8.dp, bottom = 10.dp)) { SensorTypeItem( iconRes = R.drawable.ic_dust, - titleRes = R.string.map_sensor_air_quality, + titleRes = R.string.text_air_quality, selected = selectedSensor == SensorType.DUST, onClick = { onSensorSelected(SensorType.DUST) @@ -127,7 +127,7 @@ fun MapTopControls( HorizontalDivider(color = Color(0x1F000000)) SensorTypeItem( iconRes = R.drawable.ic_radiation, - titleRes = R.string.map_sensor_radioactivity, + titleRes = R.string.sensor_radioactivity, selected = selectedSensor == SensorType.RADIOACTIVITY, onClick = { onSensorSelected(SensorType.RADIOACTIVITY) @@ -135,7 +135,7 @@ fun MapTopControls( } ) Text( - text = stringResource(id = R.string.map_sensor_help_link), + text = stringResource(id = R.string.text_what_does_it_mean), modifier = Modifier .align(Alignment.CenterHorizontally) .padding(top = 12.dp) @@ -223,12 +223,12 @@ fun MapFloatingActions( ) { RoundIconButton( iconRes = R.drawable.ic_map_search, - contentDescription = stringResource(id = R.string.map_search_action_content_desc), + contentDescription = stringResource(id = R.string.content_search), onClick = onSearchClick ) RoundIconButton( iconRes = R.drawable.ic_map_my_location, - contentDescription = stringResource(id = R.string.map_my_location_content_desc), + contentDescription = stringResource(id = R.string.content_my_location), onClick = onMyLocationClick ) } @@ -280,7 +280,7 @@ fun MapSearchOverlay( verticalAlignment = Alignment.CenterVertically ) { AirMqButton( - text = stringResource(id = R.string.map_back), + text = stringResource(id = R.string.content_back), onClick = onClose, style = AirMqButtonStyle.Outlined ) @@ -357,7 +357,7 @@ fun MapDevicePanel( verticalAlignment = Alignment.CenterVertically ) { AirMqButton( - text = stringResource(id = R.string.map_close), + text = stringResource(id = R.string.button_close), onClick = onClose, style = AirMqButtonStyle.Text ) @@ -373,7 +373,7 @@ fun MapDevicePanel( ) } AirMqButton( - text = stringResource(id = R.string.map_open_device), + text = stringResource(id = R.string.button_open), onClick = onOpenDevice, style = AirMqButtonStyle.Outlined ) @@ -436,22 +436,22 @@ private fun TimeRangeRow( FilterChip( selected = selected == TimeRange.HOUR, onClick = { onSelected(TimeRange.HOUR) }, - label = { Text(stringResource(id = R.string.map_filter_hour)) } + label = { Text(stringResource(id = R.string.filter_hour)) } ) FilterChip( selected = selected == TimeRange.DAY, onClick = { onSelected(TimeRange.DAY) }, - label = { Text(stringResource(id = R.string.map_filter_day)) } + label = { Text(stringResource(id = R.string.filter_day)) } ) FilterChip( selected = selected == TimeRange.WEEK, onClick = { onSelected(TimeRange.WEEK) }, - label = { Text(stringResource(id = R.string.map_filter_week)) } + label = { Text(stringResource(id = R.string.filter_week)) } ) FilterChip( selected = selected == TimeRange.MONTH, onClick = { onSelected(TimeRange.MONTH) }, - label = { Text(stringResource(id = R.string.map_filter_month)) } + label = { Text(stringResource(id = R.string.filter_month)) } ) } } @@ -468,17 +468,17 @@ private fun DeviceSensorRow( FilterChip( selected = selectedSensor == DeviceSensorType.TEMPERATURE, onClick = { onSelected(DeviceSensorType.TEMPERATURE) }, - label = { Text(stringResource(id = R.string.map_device_sensor_temperature)) } + label = { Text(stringResource(id = R.string.sensor_temperature)) } ) FilterChip( selected = selectedSensor == DeviceSensorType.DUST, onClick = { onSelected(DeviceSensorType.DUST) }, - label = { Text(stringResource(id = R.string.map_device_sensor_dust)) } + label = { Text(stringResource(id = R.string.sensor_dust)) } ) FilterChip( selected = selectedSensor == DeviceSensorType.RADIOACTIVITY, onClick = { onSelected(DeviceSensorType.RADIOACTIVITY) }, - label = { Text(stringResource(id = R.string.map_device_sensor_radioactivity)) } + label = { Text(stringResource(id = R.string.sensor_radioactivity)) } ) } } diff --git a/app/src/main/kotlin/org/db3/airmq/features/map/MapViewModel.kt b/app/src/main/kotlin/org/db3/airmq/features/map/MapViewModel.kt index 2d87284..0d2c2d8 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/map/MapViewModel.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/map/MapViewModel.kt @@ -1,8 +1,10 @@ package org.db3.airmq.features.map +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow @@ -12,6 +14,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import org.db3.airmq.R import org.db3.airmq.features.map.MapScreenContract.Action import org.db3.airmq.features.map.MapScreenContract.DevicePanelState import org.db3.airmq.features.map.MapScreenContract.DeviceSensorType @@ -27,6 +30,7 @@ import org.db3.airmq.sdk.settings.SettingsService @HiltViewModel class MapViewModel @Inject constructor( + @ApplicationContext private val appContext: Context, private val mapService: MapService, private val settingsService: SettingsService ) : ViewModel() { @@ -81,7 +85,7 @@ class MapViewModel @Inject constructor( } is Event.MyLocationClicked -> { - _actions.tryEmit(Action.ShowToast("My location logic will be added later")) + _actions.tryEmit(Action.ShowToast(appContext.getString(R.string.map_my_location_coming_soon))) } is Event.TopSensorSelected -> { @@ -121,14 +125,24 @@ class MapViewModel @Inject constructor( is Event.DateBackClicked -> { val panelData = _uiState.value.devicePanelState ?: return _uiState.value = _uiState.value.copy( - devicePanelState = panelData.copy(displayedDateRange = "Previous ${rangeLabel(panelData.selectedRange)}") + devicePanelState = panelData.copy( + displayedDateRange = appContext.getString( + R.string.map_previous_period, + rangeLabel(panelData.selectedRange) + ) + ) ) } Event.DateForwardClicked -> { val panelData = _uiState.value.devicePanelState ?: return _uiState.value = _uiState.value.copy( - devicePanelState = panelData.copy(displayedDateRange = "Next ${rangeLabel(panelData.selectedRange)}") + devicePanelState = panelData.copy( + displayedDateRange = appContext.getString( + R.string.map_next_period, + rangeLabel(panelData.selectedRange) + ) + ) ) } is Event.DeviceSensorSelected -> { @@ -162,7 +176,11 @@ class MapViewModel @Inject constructor( }, onFailure = { throwable -> domainItems = emptyList() - _actions.tryEmit(Action.ShowToast(throwable.message ?: "Failed to load map items")) + _actions.tryEmit( + Action.ShowToast( + throwable.message ?: appContext.getString(R.string.map_failed_to_load_items) + ) + ) val searchPanelState = _uiState.value.searchPanelState _uiState.value.copy( isLoading = false, @@ -186,16 +204,16 @@ class MapViewModel @Inject constructor( SearchResult( id = item.id, title = item.title, - subtitle = item.city ?: "No city" + subtitle = item.city ?: appContext.getString(R.string.map_no_city) ) } } private fun rangeLabel(range: TimeRange): String = when (range) { - TimeRange.HOUR -> "Last hour" - TimeRange.DAY -> "Today" - TimeRange.WEEK -> "This week" - TimeRange.MONTH -> "This month" + TimeRange.HOUR -> appContext.getString(R.string.map_time_last_hour) + TimeRange.DAY -> appContext.getString(R.string.map_time_today) + TimeRange.WEEK -> appContext.getString(R.string.map_time_this_week) + TimeRange.MONTH -> appContext.getString(R.string.map_time_this_month) } private fun remapMarkers() { @@ -231,7 +249,11 @@ class MapViewModel @Inject constructor( return DevicePanelState( id = id, name = title, - status = if (isOnline) "online" else "offline", + status = if (isOnline) { + appContext.getString(R.string.map_status_online) + } else { + appContext.getString(R.string.map_status_offline) + }, selectedRange = TimeRange.DAY, displayedDateRange = rangeLabel(TimeRange.DAY), selectedSensor = defaultSensor diff --git a/app/src/main/kotlin/org/db3/airmq/features/navigation/AirMQNavGraph.kt b/app/src/main/kotlin/org/db3/airmq/features/navigation/AirMQNavGraph.kt index d1a7763..74b0023 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/navigation/AirMQNavGraph.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/navigation/AirMQNavGraph.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.navigation.NavType import androidx.navigation.NavGraph.Companion.findStartDestination @@ -52,9 +53,9 @@ import org.db3.airmq.ui.theme.LegacyNavUnselected fun AirMQNavGraph(modifier: Modifier = Modifier) { val navController = rememberNavController() val tabItems = listOf( - BottomTabItem(route = AirMqRoutes.MAP, label = "Map", iconRes = R.drawable.ic_map), - BottomTabItem(route = AirMqRoutes.DASHBOARD, label = "Dashboard", iconRes = R.drawable.ic_dashboard), - BottomTabItem(route = AirMqRoutes.MANAGE, label = "Manage", iconRes = R.drawable.ic_manage_active) + BottomTabItem(route = AirMqRoutes.MAP, label = stringResource(id = R.string.title_map), iconRes = R.drawable.ic_map), + BottomTabItem(route = AirMqRoutes.DASHBOARD, label = stringResource(id = R.string.title_dashboard), iconRes = R.drawable.ic_dashboard), + BottomTabItem(route = AirMqRoutes.MANAGE, label = stringResource(id = R.string.title_manage), iconRes = R.drawable.ic_manage_active) ) val tabRoutes = tabItems.map { it.route }.toSet() val navBackStackEntry by navController.currentBackStackEntryAsState() diff --git a/app/src/main/kotlin/org/db3/airmq/features/news/NewsDetailScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/news/NewsDetailScreen.kt index 8665c79..2acc34e 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/news/NewsDetailScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/news/NewsDetailScreen.kt @@ -1,14 +1,16 @@ package org.db3.airmq.features.news import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @Composable fun NewsDetailScreen(newsId: String, onBackToNews: () -> Unit) { MockScreenScaffold( - title = "News Detail", - subtitle = "Mock newsId: $newsId", - actions = listOf(ScreenAction("Back to News", onBackToNews)) + title = stringResource(id = R.string.news_detail_title), + subtitle = stringResource(id = R.string.coming_soon), + actions = listOf(ScreenAction(stringResource(id = R.string.back_to_news), onBackToNews)) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/news/NewsScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/news/NewsScreen.kt index f221bcb..1ab31fd 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/news/NewsScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/news/NewsScreen.kt @@ -1,6 +1,8 @@ package org.db3.airmq.features.news import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @@ -10,11 +12,11 @@ fun NewsScreen( onBackToDashboard: () -> Unit ) { MockScreenScaffold( - title = "News", - subtitle = "Mock news list screen.", + title = stringResource(id = R.string.text_widget_news), + subtitle = stringResource(id = R.string.coming_soon), actions = listOf( - ScreenAction("Open News Detail", onOpenNewsDetail), - ScreenAction("Back to Dashboard", onBackToDashboard) + ScreenAction(stringResource(id = R.string.news_open_detail), onOpenNewsDetail), + ScreenAction(stringResource(id = R.string.back_to_dashboard), onBackToDashboard) ) ) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsScreen.kt index 46e968b..8835cf6 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsScreen.kt @@ -31,10 +31,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import kotlinx.coroutines.flow.collectLatest +import org.db3.airmq.R import org.db3.airmq.features.settings.SettingsScreenContract.Action import org.db3.airmq.features.settings.SettingsScreenContract.Event import org.db3.airmq.features.settings.SettingsScreenContract.State @@ -99,7 +101,7 @@ private fun SettingsScreenContent( verticalArrangement = Arrangement.spacedBy(2.dp) ) { Text( - text = "Application", + text = stringResource(id = R.string.pref_application_header), style = MaterialTheme.typography.labelLarge, color = Color(0xFF607D8B), modifier = Modifier.padding(start = headerTextStart, top = 4.dp, bottom = 4.dp) @@ -107,7 +109,7 @@ private fun SettingsScreenContent( PreferenceRow( iconName = "ic_pref_city", iconFallbackRes = android.R.drawable.ic_menu_myplaces, - title = "City in dashboard", + title = stringResource(id = R.string.pref_city_title), summary = uiState.city, iconSize = iconSize, iconTextGap = iconTextGap, @@ -116,7 +118,7 @@ private fun SettingsScreenContent( PreferenceCheckRow( iconName = "ic_pref_notifications", iconFallbackRes = android.R.drawable.ic_dialog_info, - title = "Notify when device becomes offline", + title = stringResource(id = R.string.pref_notifications_title), checked = uiState.deviceStatusNotificationsEnabled, iconSize = iconSize, iconTextGap = iconTextGap, @@ -125,7 +127,7 @@ private fun SettingsScreenContent( PreferenceCheckRow( iconName = "ic_pref_offline_devices", iconFallbackRes = android.R.drawable.ic_menu_mylocation, - title = "Show offline devices", + title = stringResource(id = R.string.pref_offline_devices_title), checked = uiState.offlineDevicesVisible, iconSize = iconSize, iconTextGap = iconTextGap, @@ -134,7 +136,7 @@ private fun SettingsScreenContent( PreferenceRow( iconName = "ic_pref_info", iconFallbackRes = android.R.drawable.ic_dialog_info, - title = "About", + title = stringResource(id = R.string.pref_about), iconSize = iconSize, iconTextGap = iconTextGap, onClick = { onEvent(Event.AboutClicked) } @@ -143,7 +145,7 @@ private fun SettingsScreenContent( PreferenceRow( iconName = "ic_pref_debug", iconFallbackRes = android.R.drawable.ic_lock_power_off, - title = "Log out", + title = stringResource(id = R.string.pref_logout_title), iconSize = iconSize, iconTextGap = iconTextGap, onClick = { onEvent(Event.LogOutClicked) } diff --git a/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsScreenContract.kt b/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsScreenContract.kt index 0ae087e..8aa4957 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsScreenContract.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsScreenContract.kt @@ -9,7 +9,7 @@ object SettingsScreenContract { data class State( val userMode: UserMode = UserMode.ANONYMOUS, - val city: String = "Minsk", + val city: String = "", val deviceStatusNotificationsEnabled: Boolean = true, val offlineDevicesVisible: Boolean = false, val advancedEnabled: Boolean = false, diff --git a/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsViewModel.kt b/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsViewModel.kt index 1c5e028..86100d0 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsViewModel.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/settings/SettingsViewModel.kt @@ -1,8 +1,10 @@ package org.db3.airmq.features.settings +import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableSharedFlow @@ -12,6 +14,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.launch +import org.db3.airmq.R import org.db3.airmq.features.settings.SettingsScreenContract.Action import org.db3.airmq.features.settings.SettingsScreenContract.Event import org.db3.airmq.features.settings.SettingsScreenContract.State @@ -20,6 +23,7 @@ import org.db3.airmq.sdk.settings.SettingsService @HiltViewModel class SettingsViewModel @Inject constructor( + @ApplicationContext private val appContext: Context, private val settingsService: SettingsService ) : ViewModel() { @@ -38,8 +42,8 @@ class SettingsViewModel @Inject constructor( fun onEvent(event: Event) { when (event) { - Event.CityClicked -> _actions.tryEmit(Action.ShowMessage("Coming Soon")) - Event.AboutClicked -> _actions.tryEmit(Action.ShowMessage("Coming Soon")) + Event.CityClicked -> _actions.tryEmit(Action.ShowMessage(appContext.getString(R.string.coming_soon))) + Event.AboutClicked -> _actions.tryEmit(Action.ShowMessage(appContext.getString(R.string.coming_soon))) Event.DebugClicked -> _actions.tryEmit(Action.OpenDebug) Event.LogOutClicked -> _actions.tryEmit(Action.LogOutToManage) is Event.DeviceStatusNotificationsChanged -> { @@ -65,7 +69,7 @@ class SettingsViewModel @Inject constructor( private fun loadSettings() { viewModelScope.launch(Dispatchers.IO) { - val city = settingsService.getCity() ?: "Minsk" + val city = settingsService.getCity() ?: appContext.getString(R.string.city_minsk) val deviceStatus = settingsService.getDeviceStatusNotificationsEnabled() val offlineVisible = settingsService.getOfflineDevicesVisible() val advanced = settingsService.getAdvancedEnabled() @@ -91,7 +95,10 @@ class SettingsViewModel @Inject constructor( if (result.isFailure) { _uiState.value = previous _actions.tryEmit( - Action.ShowMessage(result.exceptionOrNull()?.message ?: "Failed to save settings") + Action.ShowMessage( + result.exceptionOrNull()?.message + ?: appContext.getString(R.string.settings_save_failed) + ) ) } } diff --git a/app/src/main/kotlin/org/db3/airmq/features/setup/SetupScreen.kt b/app/src/main/kotlin/org/db3/airmq/features/setup/SetupScreen.kt index d165cf4..ecb5d09 100644 --- a/app/src/main/kotlin/org/db3/airmq/features/setup/SetupScreen.kt +++ b/app/src/main/kotlin/org/db3/airmq/features/setup/SetupScreen.kt @@ -1,6 +1,8 @@ package org.db3.airmq.features.setup import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import org.db3.airmq.R import org.db3.airmq.features.common.MockScreenScaffold import org.db3.airmq.features.common.ScreenAction @@ -10,11 +12,11 @@ fun SetupScreen( onCancelSetup: () -> Unit ) { MockScreenScaffold( - title = "Setup", - subtitle = "Mock setup flow for device onboarding.", + title = stringResource(id = R.string.button_setup), + subtitle = stringResource(id = R.string.coming_soon), actions = listOf( - ScreenAction("Finish Setup", onFinishSetup), - ScreenAction("Cancel Setup", onCancelSetup) + ScreenAction(stringResource(id = R.string.button_finish), onFinishSetup), + ScreenAction(stringResource(id = R.string.button_cancel), onCancelSetup) ) ) } diff --git a/app/src/main/res/values-be-rBY/strings.xml b/app/src/main/res/values-be-rBY/strings.xml new file mode 100644 index 0000000..1f16dfa --- /dev/null +++ b/app/src/main/res/values-be-rBY/strings.xml @@ -0,0 +1,277 @@ + + + Карта + Дашборд + Кіраванне + Налады + Прылада + Месцазнаходжанне + Падлучыцеся да Інтэрнэту + Вы ўпэўненыя? + Выбраць прыладу + Працягнуць ананімна? + Памылка + Новая прылада паспяхова дададзена! + Вашы прылады + Ананімны карыстальнік + Пароль Wifi мусіць змяшчаць мінімум 8 сімвалаў + Вы не пазначылі імя + Вы не выбралі мадэль + ОК + Працягнуць + Выбраць + Адмяніць + Палітыка канфідэнцыйнасці + Увайсці з дапамогай Google + Увайсці з дапамогай Facebook + Пошук прылады + Завяршыць + Працягнуць ананімна + Выйсці з усталявання + Перарваць + Чаканне… + Дадаць прыладу + Падлучэнне… + Пароль + Імя прылады + Імя + Мадэль + Налады + Тэмпература + Ціск + Цвёрдыя часціцы + Радыеактыўнасць + Акаўнт + Прылада + Скапіявана + Уключыце Wi-Fi + Выбраць сетку Wi-Fi + Вы ўпэўненыя, што хочаце перарваць усталяванне? Верагодна, наступным разам перад усталяваннем прыйдзецца скінуць прыладу AirMQ + Перарваць усталяванне + Кіраванне + Аб праграме + Вы не зможаце захоўваць налады і дадаваць новыя прылады AirMQ + Увайсці ў AirMQ + Памылка падчас усталявання: + Што новага + Выпраўленні + Уключыце ці перазагрузіце прыладу AirMQ, пасля чаго яна стане бачнай на працягу 90 секунд. Потым націсніце кнопку \"Выбраць прыладу\". На некаторых прыладах для карэктнага злучэння з прыладай AirMQ патрэбны ўключаны GPS. + Увядзіце імя прылады і выберыце яго мадэль + Выберыце сетку Wi-Fi, да якой хочаце падлучыць вашу прыладу + Шаг %d из 3 + Усталяванне новай прылады + Умовамі і палажэннямі + Налады не сінхранізуюцца, калі ласка, увайдзіце ў сістэму + Увайдзіце ў сістэму для дадавання прылад + Вы не выбралі сетку Wi-Fi + Увайсці + Зарэгістраваць + Капіяваць лог + Сетка Wi-Fi + Выява карыстальніка + Іконка прылады + Іконка сеткі + Лагатып AirMQ + Вільготнасць + Выйсці + Прэфікс кропкі доступу AirMQ + Пароль па змаўчанні + Версіі файлаў канфігурацыі + Аб праграме + Паведамляць, калі прылада становіцца неактыўнай + Ваша прылада адключылася ад сеткі! + %s цяпер неактыўны. Націсніце на гэта апавяшчэнне, каб адчыніць канфігурацыю прылады + Памылка аўтарызацыі Google + Патрабуецца рэгістрацыя + Уваходзячы ў сістэму, вы згаджаецеся з нашымі %1$s и %2$s + Што гэта значыць? + Якасць паветра + Гадзіна + Дзень + Тыдзень + Месяц + Бязрамачны інтэрфэйс + Памылка + Прылада не адказвае. Паспрабуйце выключыць мабільны інтэрнэт і паўтарыць. + Устаноўка… + Пачаць устаноўку + Імя + ID прылады + IP-Адрас + Версія прашыўкі + Месцазнаходжанне + Прылада не зарэгістравана + Бачнасць + Апублікавана + Схавана + Зарэгіструйце прыладу, каб мяняць бачнасць + Перайменаваць + Абнавіць + Скінуць + Схаваць + Апублікаваць + Перазагрузіць + Скінуць + Адчыніць на карце + Новае імя + Перайменаваць + Імя занадта кароткае! + Памылка + Перазагрузка… + Скід… + Паказваць недаступныя прылады + Адчыніць + Уключыць пашыраныя налады + Пашыраныя налады + Праграма + Уключыць пашыраную канфігурацыю прылады + Версія канфігурацыі + Аптайм + Іконка датчыка + Няма дадзеных + Выдаліць віджэты + Вы ўпэўненыя, што хочаце выдаліць гэты віджэт? + Дадаць віджэт + Мінск + Бараўляны + Наваполацк + Магілёў + Гродна + Гомель + Полацк + Барысаў + Нарач + Салігорск + Брэст + Бабруйск + Горад у дашбордзе + Выберыце горад + Увайдзіце ў сістэму, каб дадаваць віджэты + Учора + Зараз + Гэта прылада была выдалена + Гадзіну таму + Тыдзень таму + Месяц таму + Дадаць віджэт + Сетка WiFi + Запасная сетка WiFi + д + г + х + Перамясціць уверх + Перамясціць уніз + Наладзіць + Дадаць на працоўны стол + Прыбраць + Віджэт выдалены + Адмяніць + Загрузка… + Назад + Дадаванне віджэта + Выберыце від/выгляд віджэта + Месцазнаходжанне не выбрана + Паказваць хітмэп + Паказваць прылады + Паказваць усё + Карта + Прылада + Графік + Выберыце датчык + Выберыце прамежак часу + У вас няма дададзеных прылад + Апошнія навіны + Навіны + Змяніць + Няма прылад + Горад + Вызначаць аўтаматычна + Няма вашага горада? + Няправільны горад? + Гэтая опцыя вызначае, якія дадзеныя будуць адлюстроўвацца ў дашбордзе. Калі горад не выбраны, будзе ўсталяваны стандартны варыянт. + "Калі вы не бачыце ваш горад, значыць, у ім няма прылад " + Увесці ўручную + Вітаем вас у AirMQ + Даведайся чым дыхае твой горад + Неабходны дазвол: + • Месцазнаходжанне + Месцазнаходжанне неабходна для прадастаўлення дакладных дадзеных пра паветра ў вашым рэгіёне + Дазволіць + Не зараз + Прапусціць + Далей + Спасылка + Запасная сетка WiFi + Гатова + Прылада адключылася + Дасылаць дадзеныя ў сэрвіс narodmon.ru + Narodmon.ru + Дасылаць дадзеныя ў сэрвіс sensor.community + Sensor.community + Якасць атмасфернага паветра + Узровень радыеактыўнасці + "Датчыкі радыеактыўнасці зроблены на базе лічыльніка Гейгера-Мюлера і вымяраюць фонавае гама-радыеактыўнае выпрамяненне ў месцы ўстаноўкі. Значэнні на індыкатарах і графіках адпавядаюць магутнасці эквівалентнай дозы радыеактыўнага выпрамянення, якая характарызуе ступень уздзеяння гама-выпрамянення на арганізм. Магутнасць эквівалентнай дозы вымяраецца ў зівертах у гадзіну (Зв/г), для зручнасці ўведзена адзінка мілізіверт у гадзіну (мЗв/г) " + "Датчыкі, якія прымяняюцца ў нашым праекце, збіраюць інфармацыю пра становішча вонкавага навакольнага паветра, надвор’я, а таксама фонавага радыеактыўнага выпрамянення. Адным з важных паказчыкаў якасці паветра, якія непасрэдна ўплываюць на здароўе чалавека, з’яўляецца наяўнасць цвёрдых узважаных часціц і аэразоляў (Particulate Matter, PM), якія мы прызвычаіліся называць пылам. Часціцы могуць з’яўляцца ў паветры як у рэзультаце прыродных і стыхійных з’яў (пылавыя буры, пажары, марскі саляны пыл), так і ў вынікудзейнасці чалавека - прамысловасці, транспарта, хатняга ацяплення, спальвання смецця. Вымярэнне колькасці цвёрдых часціц адбываецца па трох катэгорыях: памерах да 1, 2.5 і 10 мкм. Часцей за ўсё выкарыстоўваюць паказчык канцэнтрацыі цвёрдых часціц памерам да 2.5 мкм, РМ2.5, які паказвае масу частак пылу на адзінку аб’ёма паветра і вымяраецца ў мкГ/м3. Для зручнасці індыкацыі ступені ўздзеяння цвёрдых часціц у атмасферы на арганізм чалавека прымяняецца наступная колеравая шкала. Для ацэнкі ўзяты сярэднія значэнні канцэнтрацыі часціц PM2.5 за апошнія 24 гадзіны. " + Што вымяраюць датчыкі AirMQ? + Даступна аднаўленне + Ваша прылада \"%s\" гатова к аднаўленню + Загрузка… + Вы павінны быць у адной сеткі WiFi з вашай прыладай. + Выбраць прыладу + + Пошук + Маё месцазнаходжанне + Закрыць + Імя прылады ці горад + Пакуль няма вынікаў + < + > + Заглушка графіка + Адкрыццё прылады %1$s пакуль не падключана + Анлайн + Афлайн + Логіка майго месцазнаходжання будзе дададзена пазней + Папярэдні %1$s + Наступны %1$s + Не ўдалося загрузіць элементы карты + Няма горада + Апошняя гадзіна + Сёння + Гэты тыдзень + Гэты месяц + Не ўдалося захаваць налады + Адладка + Адкрыць канструктар віджэтаў + Назад да дашборда + Задаць месцазнаходжанне + Эквівалент ніжняй укладкі: дашборд + Адкрыць карту + Адкрыць кіраванне + Адкрыць горад + Адкрыць прыладу + Адкрыць навіны + Застаўка + Працягнуць у майстар + Майстар + Завяршыць майстар + Тэставы deviceId: %1$s + Назад да налад + Назад да кіравання + Увайсці ў кіраванне + Адкрыць дэталі навіны + Дэталі навіны + Тэставы newsId: %1$s + Выбраць лакацыю віджэта карты + Адкрыць канструктар карты + Адкрыць канструктар графіка + Адкрыць канструктар навін + Назад да канструктара віджэтаў + Канструктар карты + Канструктар графіка + Канструктар навін + Назад да навін + Anton Betsun + messbees@gmail.com + AirMQ #42 + AirMQ #17 + \ No newline at end of file diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml new file mode 100644 index 0000000..a647e69 --- /dev/null +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -0,0 +1,277 @@ + + + Карта + Дашборд + Управление + Настройки + Устройство + Местоположение + Подключитесть к Интернету + Вы уверены? + Выбрать устройство + Продолжить анонимно? + Ошибка + Новое устройство успешно добавлено! + Ваши устройства + Анонимный пользователь + Пароль Wifi должен содержать минимум 8 символов + Вы не указали имя + Вы не выбрали модель + ОК + Продолжить + Выбрать + Отменить + Политикой конфиденциальности + Войти с помощью Google + Войти с помощью Facebook + Поиск устройства + Завершить + Продолжить анонимно + Выйти из установки + Прервать + Ожидание… + Добавить устройство + Подключение… + Пароль + Имя устройства + Имя + Модель + Настройки + Температура + Давление + Твердые частицы + Радиоактивность + Аккаунт + Устройство + Скопировано + Включите Wi-Fi + Выбрать сеть Wi-Fi + Вы уверены, что хотите прервать установку? Вероятно, в следующий раз перед установкой придется сбросить устройство AirMQ + Прервать установку + Управление + О приложении + Вы не сможете сохранять настройки и добавлять новые устройства AirMQ + Войти в AirMQ + Ошибка во время установки: + Что нового + Исправления + Включите или перезагрузите устройство AirMQ, после чего оно станет обнаруживаемым в течение 90 секунд. Затем нажмите кнопку \"Выбрать устройство\". На некоторых устройствах для корректного соединения с устройством необходим включенный GPS. + Введите имя устройства и выберите его модель + Выберите сеть Wi-Fi, к которой хотите подключить ваше устройство + Шаг %d из 3 + Установка нового устройства + Условиями и положениями + Настройки не синхронизируются, пожалуйста, войдите в систему + Войдите в систему для добавления устройств + Вы не выбрали сеть Wi-Fi + Войти + Зарегистрировать + Скопировать лог + Сеть Wi-Fi + Картинка пользователя + Иконка устройства + Иконка сети + Логотип AirMQ + Влажность + Выйти + Префикс точки доступа AirMQ + Пароль по умолчанию + Версии файлов конфигурации + О приложении + Уведомлять, когда устройство становится неактивно + Ваше устройство отключилось от сети! + %s теперь неактивен. Нажмите на это уведомление, чтобы открыть конфигурацию устройства + Ошибка авторизации Google + Требуется регистрация + Входя в систему, вы соглашаетесь с нашими %1$s и %2$s + Что это значит? + Качество воздуха + Час + День + Неделя + Месяц + Безрамочный интерфейс + Ошибка + Устройство не отвечает. Попробуйте выключить мобильный интернет и повторить. + Установка… + Начать установку + Имя + ID устройства + IP-Адрес + Версия прошивки + Местоположение + Устройство не зарегистрировано + Видимость + Опубликовано + Скрыто + Зарегистрируйте устройство, чтобы менять видимость + Переименовать + Обновить + Сбросить + Скрыть + Опубликовать + Перезагрузить + Сбросить + Открыть на карте + Новое имя + Переименовать + Имя слишком короткое! + Ошибка + Перезагрузка… + Сброс… + Показывать недоступные устройства + Открыть + Включить продвинутые настройки + Продвинутые настройки + Приложение + Включить расширенную конфигурацию устройства + Версия конфигурации + Аптайм + Иконка датчика + Нет данных + Удалить виджет + Вы уверены, что хотите удалить этот виджет? + Добавить виджет + Минск + Боровляны + Новополоцк + Могилев + Гродно + Гомель + Полоцк + Борисов + Нарочь + Солигорск + Брест + Бобруйск + Город в дашборде + Выберите город + Войдите в систему, чтобы добавлять виджеты + Вчера + Сейчас + Это устройство было удалено + Час назад + Неделю назад + Месяц назад + Добавить виджет + Сеть WiFi + Запасная сеть WiFi + д + ч + м + Переместить вверх + Переместить вниз + Настроить + Добавить на рабочий стол + Убрать + Виджет удален + Отменить + Загрузка… + Назад + Добавление виджета + Выберите вид виджета + Местоположение не выбрано + Показывать хитмэп + Показывать устройства + Показывать все + Карта + Устройство + График + Выберите датчик + Выберите временной промежуток + У вас нет добавленных устройств + Последние новости + Новости + Изменить + Нет устройств + Город + Определять автоматически + Нет вашего города? + Неправильный город? + Эта опция определяет, какие данные будут отображаться в дашборде. Если город не выбран, установится стандартный вариант. + "Если вы не видете ваш город, значит в нем нет устройств " + Ввести вручную + Добро пожаловать в AirMQ + Узнай чем дышит твой город + Необходимо разрешение: + • Местоположение + Местоположение необходимо для предоставления точных данных о воздухе в вашем регионе + Разрешить + Не сейчас + Пропустить + Далее + Ссылка + Запасная сеть WiFi + Готово + Устройство отключилось + Отправлять данные в сервис narodmon.ru + Narodmon.ru + Отправлять данные в сервис sensor.community + Sensor.community + Качество атмосферного воздуха + Уровень радиоактивности + "Датчики радиоактивности сделаны на базе счетчика Гейгера-Мюллера и измеряют фоновое гамма-радиоактивное излучение в месте установки. Значения на индикаторах и графиках соответствуют мощности эквивалентной дозы радиоактивного излучения, которая характеризует степень воздействия гамма-излучения на организм. Мощность эквивалентной дозы измеряется в зивертах/час (Зв/ч), для удобства отображения принята единица миллизиверт/час (мЗв/ч) " + "Датчики, применяемые в нашем проекте, собирают информацию о состоянии наружного окружающего воздуха, погоды, а также фонового радиоактивного излучения. Одним из важных показателей качества воздуха, непосредственно влияющих на здоровье человека, является содержание твердых взвешенных частиц и аэрозолей (Particulate Matter, PM), которые мы привыкли называть пылью. Частицы могут появляться в воздухе как в результате природных и стихийных явлений (пылевые бури, пожары, морская соляная пыль), так и в результате деятельности человека - промышленности, транспорта, домашнего отопления, сжигания мусора. Измерение количества твердых частиц производится по трем категориям: размером до 1, 2.5 и 10 мкм. Наиболее часто используемым показателем является массовая концентрация твердых частиц размером до 2.5 мкм, PM2.5, которая указывает на массу пылевых частиц в единице объема воздуха и измеряется в мкГ/м3. Для удобства индикации степени воздействия твердых частиц в атмосфере на организм человека применяется следующая цветовая шкала. Для оценки взяты средние значения концентрации частиц PM2.5 за последние 24 часа. " + Что измеряют датчики AirMQ? + Доступно обновление + Ваше устройство \"%s\" готово к обновлению + Загрузка… + Вы должны быть в одной сети WiFi с вашим устройством. + Выбрать устройство + + Поиск + Мое местоположение + Закрыть + Имя устройства или город + Результатов пока нет + < + > + Заглушка графика + Открытие устройства %1$s пока не подключено + Онлайн + Офлайн + Логика моего местоположения будет добавлена позже + Предыдущий %1$s + Следующий %1$s + Не удалось загрузить элементы карты + Нет города + Последний час + Сегодня + Эта неделя + Этот месяц + Не удалось сохранить настройки + Отладка + Открыть конструктор виджетов + Назад к дашборду + Задать местоположение + Эквивалент нижней вкладки: дашборд + Открыть карту + Открыть управление + Открыть город + Открыть устройство + Открыть новости + Заставка + Продолжить в мастер + Мастер + Завершить мастер + Тестовый deviceId: %1$s + Назад к настройкам + Назад к управлению + Войти в управление + Открыть детали новости + Детали новости + Тестовый newsId: %1$s + Выбрать локацию виджета карты + Открыть конструктор карты + Открыть конструктор графика + Открыть конструктор новостей + Назад к конструктору виджетов + Конструктор карты + Конструктор графика + Конструктор новостей + Назад к новостям + Anton Betsun + messbees@gmail.com + AirMQ #42 + AirMQ #17 + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c19d9e1..57cebe1 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,25 +1,333 @@ - - AirMQ - Dust - Air quality - Radioactivity - What does this mean? - What do AirMQ sensors measure? - Search - My location - Back + + AirMQ + Map + Dashboard + Manage + Settings + Config + Device + Select location + + + + + Connect to Internet + Exit config + Are you sure you want to exit setup? You may have to reset your AirMQ device after that. + Choose Wi-Fi network + Enable WiFi + Are you sure? + About + App by Anton Betsun \nDesign by Alexander Prokhorov + Continue anonymously? + You will not be able to back up your preferences and add new AirMQ devices + Select device + + + Coming soon + Sign in to AirMQ + AirMQ + Basic + "Error during setup: " + Error + What\'s new + Nothing to show + Fixes + Uploading config… + Config uploaded. + Uploading rules… + Rules uploaded. + Setting name… + Name set. + Setting password… + Password set. + Setting SSID… + SSID set. + Saving WiFi configuration… + Saved. + Checking connection… + Device connected to %1$s at %2$s. + Waiting for Internet… + Connected to Internet, finishing… + Disconnected! + Turn on or reboot your device, after that it will be discoverable for 90 seconds. When you are ready, press \'LOOK FOR A DEVICE\' button. On some Android devices enabled GPS service may be requied to continue. + Choose a name for your device and specify its model before connecting it to WiFi + Select the WiFi network to which you want to connect your device + Step %d of 3 + Successfully added a new device! + Setting up a new device + Mobile + Solar + Geiger + By signing in you confirm that you agree with our %1$s and %2$s + Privacy Policy + Terms & conditions + Your devices + Anonymous user + Your preferences are not being synced, please sign in + Sign in to add devices + What does this mean? + + + Wifi password must be at least 8 characters long + You didn\'t select name + You didn\'t select model + You didn\'t select wifi network + + Sign in + OK + Continue + Select + Cancel + Register + Sign in with Google + Sign in with Facebook + Copy log + Look for a device + Finish + Continue anonymously + Exit setup + Abort + Wait + Add new device + Connecting… + + + Password + Device name + Name + Model + WiFi network + + + User Picture + Device icon + Network icon + AirMQ logo + Settings + + + Temperature + Humidity + Pressure + Particulate matter + CO2 + VOC + Radioactivity + + + Hour + Day + Week + Month + + + Account + Log out + Device + AirMQ Hotspot Prefix + Default WPA key + Config versions + About + Notify when device becomes offline + + + Your device became offline! + %s is now offline. Click this to open device config page + + + + + Google authorisation failed + Copied + Device requires registration + + 21688843933-n04t4s1h7rjad12tdj1pc9j6kajgh2ka.apps.googleusercontent.com + 356744979027664 + fb356744979027664 + Air quality + Borderless layout + Error + Can\'t get response from device. Try to disable mobile data. + Setup… + Start setup + Name + Device ID + IP Address + Firmware version + Location + Device is not registered + %s, %s + Visibility + Public + Private + Register device to change visibility + Rename + Update + Unregister + Hide + Publish + Reboot + Reset + View on map + New name + Rename + Name is too short! + Error + Rebooting… + Resetting… + Show offline devices + Open + Enable advanced options + Advanced + Application + Enable extended device config + Config version + Uptime + Sensor icon + No data + Delete widget + Are you sure you want to delete this widget? + Add widget + Minsk + Barauliany + Navapolack + Mahilioŭ + Hrodna + Homieĺ + Polack + Barysaŭ + Narač + Salihorsk + Brest + Babrujsk + City in dashboard + Select city + Log in to add widgets + Yesterday + Now + This device was deleted + Hour ago + Week ago + Month ago + Add widget + WiFi network + Secondary WiFi network + d + h + m + Move up + Move down + Configure + Add to Home screen + Remove + Widget removed + Undo + Loading… + Back + New widget + Choose widget layout + Location is not specified + Display heatmap + Display sensors + Display both + Map + Device + Chart + Choose sensor + Choose timeframe + You do not have configured devices + Latest news + News + Change + City + Detect automatically + Don\'t see your city? + Wrong city? + If you don’t see your city, there are not enough sensors there to provide reliable data just yet. Instead, data from the closest available city will be displayed when automatic detection is enabled + This setting affects what data is displayed in the dashboard. If no city is selected, default value will be set. + Enter manually + Welcome to AirMQ + Learn what your city breathes + Permission requied: + • Location + Location permission is needed to provide accurate data about air quality in your area + Allow + Not now + Skip + Next + Link + Secondary WiFi + Done + Device disconnected + You need to be in the same WiFi network with your device. + Send data to narodmon.ru + Narodmon.ru + Send data to sensor.community + Sensor.community + Качество атмосферного воздуха + Уровень радиоактивности + "Датчики радиоактивности сделаны на базе счетчика Гейгера-Мюллера и измеряют фоновое гамма-радиоактивное излучение в месте установки. Значения на индикаторах и графиках соответствуют мощности эквивалентной дозы радиоактивного излучения, которая характеризует степень воздействия гамма-излучения на организм. Мощность эквивалентной дозы измеряется в зивертах/час (Зв/ч), для удобства отображения принята единица миллизиверт/час (мЗв/ч) " + "Датчики, применяемые в нашем проекте, собирают информацию о состоянии наружного окружающего воздуха, погоды, а также фонового радиоактивного излучения. Одним из важных показателей качества воздуха, непосредственно влияющих на здоровье человека, является содержание твердых взвешенных частиц и аэрозолей (Particulate Matter, PM), которые мы привыкли называть пылью. Частицы могут появляться в воздухе как в результате природных и стихийных явлений (пылевые бури, пожары, морская соляная пыль), так и в результате деятельности человека - промышленности, транспорта, домашнего отопления, сжигания мусора. Измерение количества твердых частиц производится по трем категориям: размером до 1, 2.5 и 10 мкм. Наиболее часто используемым показателем является массовая концентрация твердых частиц размером до 2.5 мкм, PM2.5, которая указывает на массу пылевых частиц в единице объема воздуха и измеряется в мкГ/м3. Для удобства индикации степени воздействия твердых частиц в атмосфере на организм человека применяется следующая цветовая шкала. Для оценки взяты средние значения концентрации частиц PM2.5 за последние 24 часа. " + Что измеряют датчики AirMQ? + Update is available + Your device \"%s\" is ready to update + Uploading… + Select device + + Search + My location + Close Device name or city No results yet - Close - Open < > Chart placeholder - Hour - Day - Week - Month - Temperature - Dust - Radioactivity + Open device %1$s is not wired yet + Online + Offline + My location logic will be added later + Previous %1$s + Next %1$s + Failed to load map items + No city + Last hour + Today + This week + This month + Failed to save settings + Debug + Open Widget Constructor + Back to Dashboard + Set location + Bottom-tab equivalent: dashboard + Open Map + Open Manage + Open City + Open Device + Open News + Splash + Continue to Wizard + Wizard + Finish Wizard + Mock deviceId: %1$s + Back to Settings + Back to Manage + Log In to Manage + Open News Detail + News Detail + Mock newsId: %1$s + Select Map Widget Location + Open Map Constructor + Open Chart Constructor + Open News Constructor + Back to Widget Constructor + Map Constructor + Chart Constructor + News Constructor + Back to News + Anton Betsun + messbees@gmail.com + AirMQ #42 + AirMQ #17 \ No newline at end of file