diff options
author | Iván Ávalos <avalos@disroot.org> | 2023-09-26 19:36:27 -0600 |
---|---|---|
committer | Iván Ávalos <avalos@disroot.org> | 2023-09-26 19:36:27 -0600 |
commit | 3e72d9b02e864f4f79ae494c67418031b7625659 (patch) | |
tree | 3d6d7ae3997b9372d50932e0e489dced12e5b07c /androidApp | |
parent | 8765d82d3ad055945c6221e4f46bc38d903bf58d (diff) | |
parent | 3022b877d0cf9b7546c80953237c7bca5a4afa50 (diff) | |
download | etbsa-trackermap-mobile-3e72d9b02e864f4f79ae494c67418031b7625659.tar.gz etbsa-trackermap-mobile-3e72d9b02e864f4f79ae494c67418031b7625659.tar.bz2 etbsa-trackermap-mobile-3e72d9b02e864f4f79ae494c67418031b7625659.zip |
Merge branch 'main' of https://git.sr.ht/~avalos/trackermap-mobile
Diffstat (limited to 'androidApp')
21 files changed, 450 insertions, 141 deletions
diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/TrackerApp.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/TrackerApp.kt index aa92c91..1bb921d 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/TrackerApp.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/TrackerApp.kt @@ -28,6 +28,7 @@ import mx.trackermap.TrackerMap.android.units.UnitsViewModel import mx.trackermap.TrackerMap.client.apis.* import mx.trackermap.TrackerMap.client.infrastructure.SessionManager import mx.trackermap.TrackerMap.controllers.* +import mx.trackermap.TrackerMap.controllers.NetworkController import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger import org.koin.androidx.viewmodel.dsl.viewModel @@ -56,7 +57,8 @@ open class TrackerApp : Application() { factory { ReportsApi(get()) } factory { GeofencesApi(get()) } - factory { SessionController(get(), get()) } + factory { NetworkController(applicationContext) } + factory { SessionController(get(), get(), get()) } factory { UnitsController(get(), get()) } factory { GeofencesController(get()) } factory { ReportController(get(), get()) } diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt index 4915c49..2be9bb4 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt @@ -40,6 +40,7 @@ import mx.trackermap.TrackerMap.client.models.Geofence import mx.trackermap.TrackerMap.client.models.MapLayer import mx.trackermap.TrackerMap.client.models.Marker import mx.trackermap.TrackerMap.utils.MapCalculus +import kotlin.math.atan2 typealias SetupCallback = () -> Unit typealias MarkerCallback = (Int?) -> Unit @@ -186,11 +187,14 @@ open class MapFragment : GlobeMapFragment() { val colorReport = ContextCompat.getColor(requireContext(), R.color.colorReport) val colorLabel = ContextCompat.getColor(requireContext(), R.color.colorMarkerLabel) val colorLabelOutline = ContextCompat.getColor(requireContext(), R.color.colorMarkerLabelOutline) - val vectorWidth = context?.resources?.getDimensionPixelSize(R.dimen.report_label_width)?.toFloat() + val vectorWidth = requireContext().resources.getDimensionPixelSize(R.dimen.report_label_width).toFloat() + + val markerSize = requireContext().resources.getDimensionPixelSize(R.dimen.marker_size).toDouble() + val vertexSize = requireContext().resources.getDimensionPixelSize(R.dimen.vertex_size).toDouble() val vectorInfo = VectorInfo() - vectorInfo.setColor(colorReport) - vectorInfo.setLineWidth(vectorWidth ?: 20f) + vectorInfo.color = colorReport + vectorInfo.lineWidth = vectorWidth val labelInfo = LabelInfo() labelInfo.typeface = Typeface.DEFAULT_BOLD @@ -210,17 +214,17 @@ open class MapFragment : GlobeMapFragment() { when (i) { 0 -> getIcon(Marker.Type.REPORT_START) markers.size - 1 -> getIcon(Marker.Type.REPORT_END) - else -> getIcon(Marker.Type.REPORT_POSITION) + else -> getIconForDirection(points[i], points[i + 1]) } } else getIcon(marker.type) screenMarker.size = if (isReport) { // For reports, position, start and end, size must be different when (i) { - 0 -> Point2d(144.0, 144.0) - markers.size - 1 -> Point2d(144.0, 144.0) - else -> Point2d(82.0, 82.0) + 0 -> Point2d(markerSize, markerSize) + markers.size - 1 -> Point2d(markerSize, markerSize) + else -> Point2d(vertexSize, vertexSize) } - } else Point2d(144.0, 144.0) + } else Point2d(markerSize, markerSize) screenMarker.userObject = marker.id screenMarker.selectable = true @@ -292,11 +296,11 @@ open class MapFragment : GlobeMapFragment() { val colorFill = ContextCompat.getColor(requireContext(), R.color.colorGeofence) val colorLabel = ContextCompat.getColor(requireContext(), R.color.colorGeofenceLabel) val colorLabelOutline = ContextCompat.getColor(requireContext(), R.color.colorMarkerLabelOutline) - val vectorWidth = context?.resources?.getDimensionPixelSize(R.dimen.geofence_label_width)?.toFloat() + val vectorWidth = requireContext().resources.getDimensionPixelSize(R.dimen.geofence_label_width).toFloat() val vectorInfo = VectorInfo() - vectorInfo.setColor(colorFill) - vectorInfo.setLineWidth(vectorWidth ?: 4f) + vectorInfo.color = colorFill + vectorInfo.lineWidth = vectorWidth val labelInfo = LabelInfo() labelInfo.typeface = Typeface.DEFAULT_BOLD @@ -334,7 +338,7 @@ open class MapFragment : GlobeMapFragment() { } } } - } catch (e: SFException) {} + } catch (_: SFException) {} } } @@ -427,9 +431,22 @@ open class MapFragment : GlobeMapFragment() { } private fun getIcon(markerType: Marker.Type): Bitmap { + val markerSize = requireContext().resources.getDimensionPixelSize(R.dimen.marker_size) return ResourcesCompat.getDrawable( requireActivity().resources, MarkerTransformations.markerTypeToResourceId(markerType), - requireActivity().theme)!!.toBitmap(144, 144) + requireActivity().theme)!!.toBitmap(markerSize, markerSize) + } + + private fun getIconForDirection(a: Point2d, b: Point2d): Bitmap { + val vertexSize = requireContext().resources.getDimensionPixelSize(R.dimen.vertex_size) + val vectorX = b.x - a.x + val vectorY = b.y - a.y + val angleRad = atan2(vectorY, vectorX) + return ResourcesCompat.getDrawable( + requireActivity().resources, + MarkerTransformations.angleToResourceId(angleRad), + requireActivity().theme + )!!.toBitmap(vertexSize, vertexSize) } }
\ No newline at end of file diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginFragment.kt index 6a30789..8879349 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginFragment.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginFragment.kt @@ -67,7 +67,6 @@ class LoginFragment : Fragment() { getString(R.string.default_server_url) ) ?: getString(R.string.default_server_url) ) - loginViewModel.restoreSession() } override fun onStart() { @@ -100,6 +99,26 @@ class LoginFragment : Fragment() { } private fun setupObservers() { + loginViewModel.networkAvailable.observe(viewLifecycleOwner) { available -> + Log.d("LoginFragment", "available = $available, session = ${loginViewModel.hasSession}") + + binding.offlineBanner.root.visibility = + if (available == true) View.GONE else View.VISIBLE + + binding.infoLoading.root.visibility = when (available) { + null -> View.VISIBLE + true -> if (loginViewModel.hasSession) { + loginViewModel.restoreSession() + View.VISIBLE + } else View.GONE + false -> if (loginViewModel.hasSession) { + View.VISIBLE + } else { + View.GONE + } + } + } + loginViewModel.loginState.observe(viewLifecycleOwner) { result -> Log.d("LoginFragment", result.toString()) when (result) { @@ -164,4 +183,4 @@ class LoginFragment : Fragment() { const val PREFERENCE_SERVER_URL = "server_url" const val PREFERENCE_TOKEN = "token" } -}
\ No newline at end of file +} diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginViewModel.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginViewModel.kt index bcee2ec..434ac44 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginViewModel.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginViewModel.kt @@ -17,32 +17,46 @@ */ package mx.trackermap.TrackerMap.android.session +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.zhuinden.eventemitter.EventEmitter import com.zhuinden.eventemitter.EventSource import kotlinx.coroutines.DelicateCoroutinesApi -import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import mx.trackermap.TrackerMap.client.models.SessionBody +import mx.trackermap.TrackerMap.controllers.NetworkController import mx.trackermap.TrackerMap.controllers.SessionController import org.koin.core.component.KoinComponent import org.koin.core.component.inject @DelicateCoroutinesApi class LoginViewModel : ViewModel(), KoinComponent { - + private val networkController: NetworkController by inject() private val sessionController: SessionController by inject() + private val _networkAvailable = MutableLiveData<Boolean?>(null) + val networkAvailable: LiveData<Boolean?> = _networkAvailable private val loginStateEmitter = EventEmitter<SessionController.LoginState>() val loginState: EventSource<SessionController.LoginState> = loginStateEmitter + val hasSession: Boolean get() = sessionController.hasSession init { viewModelScope.launch { + setupNetworkObserver() + } + viewModelScope.launch { setupLoginStateObserver() } } + private suspend fun setupNetworkObserver() { + networkController.networkAvailable.collect { + _networkAvailable.postValue(it) + } + } + private suspend fun setupLoginStateObserver() { sessionController.loginStateFlow.collect { it?.let { @@ -52,7 +66,7 @@ class LoginViewModel : ViewModel(), KoinComponent { } fun restoreSession() { - sessionController.restoreSession() + sessionController.getSession() } fun login(email: String, password: String, url: String, token: String?) { diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/shared/MarkerTransformations.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/shared/MarkerTransformations.kt index 4dd1ea7..e99b8e6 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/shared/MarkerTransformations.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/shared/MarkerTransformations.kt @@ -19,6 +19,7 @@ package mx.trackermap.TrackerMap.android.shared import mx.trackermap.TrackerMap.android.R import mx.trackermap.TrackerMap.client.models.Marker +import kotlin.math.PI object MarkerTransformations { fun markerTypeToResourceId(markerType: Marker.Type): Int { @@ -52,7 +53,7 @@ object MarkerTransformations { } } - fun markerTypeToStringId(markerType: Marker.Type): Int { + private fun markerTypeToStringId(markerType: Marker.Type): Int { return when (markerType) { Marker.Type.ANIMAL -> R.string.unit_category_animal Marker.Type.BACKHOE -> R.string.unit_category_backhoe @@ -87,4 +88,20 @@ object MarkerTransformations { fun categoryToStringId(category: String?): Int { return markerTypeToStringId(Marker.categoryToMarkerType(category)) } + + private const val STEP = PI / 8 + + @OptIn(ExperimentalStdlibApi::class) + fun angleToResourceId(rad: Double): Int = when (rad) { + in 0.0 ..< STEP -> R.drawable.angle_0 + in STEP ..< STEP * 3 -> R.drawable.angle_45 + in STEP * 3 ..< STEP * 5 -> R.drawable.angle_90 + in STEP * 5 ..< STEP * 7 -> R.drawable.angle_135 + in STEP * 7 ..< STEP * 9 -> R.drawable.angle_180 + in STEP * 9 ..< STEP * 11 -> R.drawable.angle_225 + in STEP * 11 ..< STEP * 13 -> R.drawable.angle_270 + in STEP * 13 ..< STEP * 15 -> R.drawable.angle_315 + in STEP * 15 ..< STEP * 16 -> R.drawable.angle_0 + else -> angleToResourceId(PI * 2 + rad) + } }
\ No newline at end of file diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt index 8a161c3..443a205 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt @@ -28,7 +28,6 @@ import android.view.View import android.view.inputmethod.InputMethodManager import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.widget.TooltipCompat -import androidx.core.widget.doAfterTextChanged import androidx.fragment.app.commit import kotlinx.coroutines.DelicateCoroutinesApi import mx.trackermap.TrackerMap.android.R @@ -150,6 +149,10 @@ class UnitsActivity : AppCompatActivity() { } private fun setupObservers() { + unitsViewModel.networkAvailable.observe(this) { available -> + binding.offlineBanner.root.visibility = + if (available == true) View.GONE else View.VISIBLE + } unitsViewModel.unitsDisplayMode.observe(this) { displayMode -> binding.displayModeToggle.setImageResource( when (displayMode) { diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt index b5ed78d..05962e9 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt @@ -27,6 +27,7 @@ import mx.trackermap.TrackerMap.client.models.Geofence import mx.trackermap.TrackerMap.client.models.MapLayer import mx.trackermap.TrackerMap.client.models.UnitInformation import mx.trackermap.TrackerMap.controllers.GeofencesController +import mx.trackermap.TrackerMap.controllers.NetworkController import mx.trackermap.TrackerMap.controllers.UnitsController import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -48,9 +49,11 @@ class UnitsViewModel( val animated: Boolean, ) + private val networkController: NetworkController by inject() private val unitsController: UnitsController by inject() private val geofenceController: GeofencesController by inject() + private var _networkAvailable = MutableLiveData<Boolean?>() private var _searchQuery = savedStateHandle.getLiveData("searchQuery", "") private var _unitsDisplayMode = MutableLiveData(UnitsDisplayMode.MAP) private var _units = MutableLiveData<List<UnitInformation>>() @@ -61,6 +64,7 @@ class UnitsViewModel( private var _geofences = MutableLiveData<Map<Int, Geofence>>() private val _camera = MutableLiveData<Camera?>() + val networkAvailable: LiveData<Boolean?> get() = _networkAvailable val searchQuery: LiveData<String> get() = _searchQuery val unitsDisplayMode: LiveData<UnitsDisplayMode> get() = _unitsDisplayMode val units: LiveData<List<UnitInformation>> get() = _units @@ -75,6 +79,9 @@ class UnitsViewModel( Log.d("UnitsViewModel", "Initializing Units View Model") unitsController.fetchUnits(viewModelScope) viewModelScope.launch { + setupNetworkObserver() + } + viewModelScope.launch { setupUnitsObserver() } viewModelScope.launch { @@ -86,6 +93,13 @@ class UnitsViewModel( } } + private suspend fun setupNetworkObserver() { + networkController.networkAvailable.collect { available -> + Log.d("UnitsViewModel", "Collecting network state") + _networkAvailable.value = available + } + } + private suspend fun setupUnitsObserver() { (unitsController.displayedUnitsFlow as StateFlow<List<UnitInformation>>).collect { units -> Log.d("UnitsViewModel", "Collecting units") diff --git a/androidApp/src/main/res/drawable/angle_0.xml b/androidApp/src/main/res/drawable/angle_0.xml new file mode 100644 index 0000000..53957d9 --- /dev/null +++ b/androidApp/src/main/res/drawable/angle_0.xml @@ -0,0 +1,22 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="6.35" + android:viewportHeight="6.35"> + <path + android:pathData="M3.1751,6.106A2.9308,2.9308 0,0 1,0.2443 3.1752,2.9308 2.9308,0 0,1 3.1751,0.2444 2.9308,2.9308 0,0 1,6.1059 3.1752,2.9308 2.9308,0 0,1 3.1751,6.106Z" + android:strokeWidth="1.00012" + android:fillColor="#008000" + android:strokeLineCap="square"/> + <path + android:pathData="M6.3501,3.176C6.3501,1.4254 4.9249,0.0002 3.1743,0.0002c-1.7506,0 -3.1738,1.4252 -3.1738,3.1758 -0,1.7506 1.4232,3.1738 3.1738,3.1738C4.9249,6.3498 6.3501,4.9266 6.3501,3.176ZM5.8618,3.176c-0,1.4866 -1.2009,2.6855 -2.6875,2.6855 -1.4866,0 -2.6855,-1.1989 -2.6855,-2.6855 -0,-1.4866 1.1989,-2.6875 2.6855,-2.6875 1.4866,0 2.6875,1.2009 2.6875,2.6875z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeLineCap="square"/> + <path + android:pathData="M5.8618,3.1757 L2.9355,1.4857V2.5831L0.2443,2.5826c0,0 -0,0.3951 -0,0.5926 0,0.1972 0,0.5917 0,0.5917l2.6912,0.0005v1.0974z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeColor="#1a1a1a" + android:strokeLineCap="square"/> +</vector> diff --git a/androidApp/src/main/res/drawable/angle_135.xml b/androidApp/src/main/res/drawable/angle_135.xml new file mode 100644 index 0000000..cd1b88e --- /dev/null +++ b/androidApp/src/main/res/drawable/angle_135.xml @@ -0,0 +1,22 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="6.35" + android:viewportHeight="6.35"> + <path + android:pathData="M5.2479,1.1026A2.9308,2.9308 90,0 1,5.2479 5.2474,2.9308 2.9308,90 0,1 1.1032,5.2474 2.9308,2.9308 0,0 1,1.1032 1.1026,2.9308 2.9308,0 0,1 5.2479,1.1026Z" + android:strokeWidth="1.00012" + android:fillColor="#008000" + android:strokeLineCap="square"/> + <path + android:pathData="M0.9311,0.9294C-0.3068,2.1673 -0.3068,4.1828 0.9311,5.4206c1.2379,1.2379 3.252,1.2365 4.4899,-0.0014 1.2379,-1.2379 1.2379,-3.2506 0,-4.4885C4.183,-0.3071 2.1689,-0.3085 0.9311,0.9294ZM1.2763,1.2747c1.0512,-1.0512 2.7481,-1.0498 3.7993,0.0014 1.0512,1.0512 1.0512,2.7467 -0,3.7979 -1.0512,1.0512 -2.7481,1.0526 -3.7993,0.0014 -1.0512,-1.0512 -1.0512,-2.7495 0,-3.8007z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeLineCap="square"/> + <path + android:pathData="M1.2761,1.2749 L2.1503,4.5391 2.9263,3.7631 4.8289,5.6664c0,0 0.2793,-0.2793 0.419,-0.419C5.3874,5.1079 5.6663,4.829 5.6663,4.829L3.7637,2.9257 4.5396,2.1498Z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeColor="#1a1a1a" + android:strokeLineCap="square"/> +</vector> diff --git a/androidApp/src/main/res/drawable/angle_180.xml b/androidApp/src/main/res/drawable/angle_180.xml new file mode 100644 index 0000000..f3fca08 --- /dev/null +++ b/androidApp/src/main/res/drawable/angle_180.xml @@ -0,0 +1,22 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="6.35" + android:viewportHeight="6.35"> + <path + android:pathData="M3.1755,0.244A2.9308,2.9308 0,0 1,6.1063 3.1748,2.9308 2.9308,0 0,1 3.1755,6.1056 2.9308,2.9308 0,0 1,0.2447 3.1748,2.9308 2.9308,0 0,1 3.1755,0.244Z" + android:strokeWidth="1.00012" + android:fillColor="#008000" + android:strokeLineCap="square"/> + <path + android:pathData="M0.0005,3.174C0.0005,4.9246 1.4257,6.3498 3.1763,6.3498c1.7506,-0 3.1738,-1.4252 3.1738,-3.1758 0,-1.7506 -1.4232,-3.1738 -3.1738,-3.1738C1.4257,0.0002 0.0005,1.4234 0.0005,3.174ZM0.4888,3.174c0,-1.4866 1.2009,-2.6855 2.6875,-2.6855 1.4866,-0 2.6855,1.1989 2.6855,2.6855 0,1.4866 -1.1989,2.6875 -2.6855,2.6875 -1.4866,-0 -2.6875,-1.2009 -2.6875,-2.6875z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeLineCap="square"/> + <path + android:pathData="M0.4888,3.1744 L3.4151,4.8643V3.7669l2.6912,0.0005c0,0 0,-0.3951 0,-0.5926 0,-0.1972 0,-0.5917 0,-0.5917L3.4151,2.5827V1.4853Z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeColor="#1a1a1a" + android:strokeLineCap="square"/> +</vector> diff --git a/androidApp/src/main/res/drawable/angle_225.xml b/androidApp/src/main/res/drawable/angle_225.xml new file mode 100644 index 0000000..bd7c512 --- /dev/null +++ b/androidApp/src/main/res/drawable/angle_225.xml @@ -0,0 +1,22 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="6.35" + android:viewportHeight="6.35"> + <path + android:pathData="M1.1036,1.1031A2.9308,2.9308 90,0 1,5.2484 1.1031,2.9308 2.9308,90 0,1 5.2484,5.2478 2.9308,2.9308 0,0 1,1.1036 5.2478,2.9308 2.9308,0 0,1 1.1036,1.1031Z" + android:strokeWidth="1.00012" + android:fillColor="#008000" + android:strokeLineCap="square"/> + <path + android:pathData="M0.9304,5.4199C2.1682,6.6578 4.1837,6.6578 5.4216,5.4199c1.2379,-1.2379 1.2365,-3.252 -0.0014,-4.4899 -1.2379,-1.2379 -3.2506,-1.2379 -4.4885,-0C-0.3061,2.1679 -0.3075,4.1821 0.9304,5.4199ZM1.2756,5.0747c-1.0512,-1.0512 -1.0498,-2.7481 0.0014,-3.7993 1.0512,-1.0512 2.7467,-1.0512 3.7979,0 1.0512,1.0512 1.0526,2.7481 0.0014,3.7993 -1.0512,1.0512 -2.7495,1.0512 -3.8007,-0z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeLineCap="square"/> + <path + android:pathData="M1.2759,5.0749 L4.54,4.2007 3.7641,3.4247 5.6674,1.5221c0,0 -0.2793,-0.2793 -0.419,-0.419C5.1089,0.9636 4.83,0.6847 4.83,0.6847L2.9267,2.5873 2.1508,1.8114Z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeColor="#1a1a1a" + android:strokeLineCap="square"/> +</vector> diff --git a/androidApp/src/main/res/drawable/angle_270.xml b/androidApp/src/main/res/drawable/angle_270.xml new file mode 100644 index 0000000..19db1cf --- /dev/null +++ b/androidApp/src/main/res/drawable/angle_270.xml @@ -0,0 +1,22 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="6.35" + android:viewportHeight="6.35"> + <path + android:pathData="M0.2455,3.1753A2.9308,2.9308 0,0 1,3.1763 0.2445,2.9308 2.9308,0 0,1 6.107,3.1753 2.9308,2.9308 0,0 1,3.1763 6.1061,2.9308 2.9308,0 0,1 0.2455,3.1753Z" + android:strokeWidth="1.00012" + android:fillColor="#008000" + android:strokeLineCap="square"/> + <path + android:pathData="M3.1755,6.3503C4.9261,6.3503 6.3513,4.9251 6.3513,3.1745c-0,-1.7506 -1.4252,-3.1738 -3.1758,-3.1738 -1.7506,-0 -3.1738,1.4232 -3.1738,3.1738C0.0017,4.9251 1.4249,6.3503 3.1755,6.3503ZM3.1755,5.862c-1.4866,-0 -2.6855,-1.2009 -2.6855,-2.6875 -0,-1.4866 1.1989,-2.6855 2.6855,-2.6855 1.4866,-0 2.6875,1.1989 2.6875,2.6855 -0,1.4866 -1.2009,2.6875 -2.6875,2.6875z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeLineCap="square"/> + <path + android:pathData="m3.1758,5.862 l1.6899,-2.9263 -1.0974,0 0.0005,-2.6912c0,0 -0.3951,0 -0.5926,-0 -0.1972,0 -0.5917,0 -0.5917,0l-0.0005,2.6912 -1.0974,0z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeColor="#1a1a1a" + android:strokeLineCap="square"/> +</vector> diff --git a/androidApp/src/main/res/drawable/angle_315.xml b/androidApp/src/main/res/drawable/angle_315.xml new file mode 100644 index 0000000..3186db0 --- /dev/null +++ b/androidApp/src/main/res/drawable/angle_315.xml @@ -0,0 +1,22 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="6.35" + android:viewportHeight="6.35"> + <path + android:pathData="M1.1038,5.2479A2.9308,2.9308 90,0 1,1.1038 1.1031,2.9308 2.9308,0 0,1 5.2486,1.1031 2.9308,2.9308 0,0 1,5.2486 5.2479,2.9308 2.9308,90 0,1 1.1038,5.2479Z" + android:strokeWidth="1.00012" + android:fillColor="#008000" + android:strokeLineCap="square"/> + <path + android:pathData="M5.4207,5.4211C6.6586,4.1832 6.6586,2.1677 5.4207,0.9299c-1.2379,-1.2379 -3.252,-1.2365 -4.4899,0.0014 -1.2379,1.2379 -1.2379,3.2506 0,4.4885C2.1687,6.6576 4.1828,6.659 5.4207,5.4211ZM5.0754,5.0758c-1.0512,1.0512 -2.7481,1.0498 -3.7993,-0.0014 -1.0512,-1.0512 -1.0512,-2.7467 -0,-3.7979 1.0512,-1.0512 2.7481,-1.0526 3.7993,-0.0014 1.0512,1.0512 1.0512,2.7495 0,3.8007z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeLineCap="square"/> + <path + android:pathData="M5.0757,5.0756 L4.2014,1.8114 3.4255,2.5874 1.5228,0.6841c0,0 -0.2793,0.2793 -0.419,0.419C0.9644,1.2426 0.6855,1.5215 0.6855,1.5215L2.5881,3.4248 1.8121,4.2007Z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeColor="#1a1a1a" + android:strokeLineCap="square"/> +</vector> diff --git a/androidApp/src/main/res/drawable/angle_45.xml b/androidApp/src/main/res/drawable/angle_45.xml new file mode 100644 index 0000000..9d8b5a9 --- /dev/null +++ b/androidApp/src/main/res/drawable/angle_45.xml @@ -0,0 +1,22 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="6.35" + android:viewportHeight="6.35"> + <path + android:pathData="M5.2467,5.2476A2.9308,2.9308 90,0 1,1.1019 5.2476,2.9308 2.9308,0 0,1 1.1019,1.1029 2.9308,2.9308 0,0 1,5.2467 1.1029,2.9308 2.9308,90 0,1 5.2467,5.2476Z" + android:strokeWidth="1.00012" + android:fillColor="#008000" + android:strokeLineCap="square"/> + <path + android:pathData="M5.4199,0.9308C4.1821,-0.3071 2.1666,-0.3071 0.9287,0.9308c-1.2379,1.2379 -1.2365,3.252 0.0014,4.4899 1.2379,1.2379 3.2506,1.2379 4.4885,-0C6.6564,4.1828 6.6578,2.1686 5.4199,0.9308ZM5.0747,1.276c1.0512,1.0512 1.0498,2.7481 -0.0014,3.7993 -1.0512,1.0512 -2.7467,1.0512 -3.7979,0 -1.0512,-1.0512 -1.0526,-2.7481 -0.0014,-3.7993 1.0512,-1.0512 2.7495,-1.0512 3.8007,-0z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeLineCap="square"/> + <path + android:pathData="M5.0744,1.2758 L1.8103,2.1501 2.5862,2.926 0.8061,4.7067 1.2251,5.1257 1.6435,5.5441 3.4236,3.7634 4.1995,4.5393Z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeColor="#1a1a1a" + android:strokeLineCap="square"/> +</vector> diff --git a/androidApp/src/main/res/drawable/angle_90.xml b/androidApp/src/main/res/drawable/angle_90.xml new file mode 100644 index 0000000..44dae03 --- /dev/null +++ b/androidApp/src/main/res/drawable/angle_90.xml @@ -0,0 +1,22 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="6.35" + android:viewportHeight="6.35"> + <path + android:pathData="M6.1058,3.175A2.9308,2.9308 0,0 1,3.175 6.1058,2.9308 2.9308,0 0,1 0.2442,3.175 2.9308,2.9308 0,0 1,3.175 0.2442,2.9308 2.9308,0 0,1 6.1058,3.175Z" + android:strokeWidth="1.00012499" + android:fillColor="#008000" + android:strokeLineCap="square"/> + <path + android:pathData="M3.1758,0C1.4252,0 0,1.4252 0,3.1758c0,1.7506 1.4252,3.1738 3.1758,3.1738 1.7506,0 3.1738,-1.4232 3.1738,-3.1738C6.3496,1.4252 4.9264,0 3.1758,0ZM3.1758,0.4883c1.4866,0 2.6855,1.2009 2.6855,2.6875 0,1.4866 -1.1989,2.6855 -2.6855,2.6855 -1.4866,0 -2.6875,-1.1989 -2.6875,-2.6855 0,-1.4866 1.2009,-2.6875 2.6875,-2.6875z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeLineCap="square"/> + <path + android:pathData="M3.1755,0.4883 L1.4855,3.4146H2.5829V5.9667H3.1755,3.7671V3.4146h1.0974z" + android:strokeWidth="0" + android:fillColor="#1a1a1a" + android:strokeColor="#1a1a1a" + android:strokeLineCap="square"/> +</vector> diff --git a/androidApp/src/main/res/layout/login.xml b/androidApp/src/main/res/layout/login.xml index 1de3f12..ec3925c 100644 --- a/androidApp/src/main/res/layout/login.xml +++ b/androidApp/src/main/res/layout/login.xml @@ -1,132 +1,151 @@ <?xml version="1.0" encoding="utf-8"?> -<FrameLayout - xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - android:name="mx.trackermap.TrackerMap.android.session.LoginFragment" - android:layout_width="match_parent" - android:layout_height="match_parent"> - - <androidx.constraintlayout.widget.ConstraintLayout +<androidx.constraintlayout.widget.ConstraintLayout + android:name="mx.trackermap.TrackerMap.android.session.LoginFragment" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> - <androidx.appcompat.widget.AppCompatImageView - android:id="@+id/bannerImage" - android:layout_width="0dp" - android:layout_height="@dimen/about_logo_height" - app:layout_constraintWidth_percent="0.5" - app:layout_constraintDimensionRatio="1" + <include + android:id="@+id/offlineBanner" + layout="@layout/offline_banner" app:layout_constraintTop_toTopOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" - android:layout_marginHorizontal="@dimen/card_margin" - app:srcCompat="@drawable/about_logo" /> - - <androidx.cardview.widget.CardView - android:layout_width="0dp" - android:layout_height="wrap_content" - app:cardCornerRadius="@dimen/card_border_radius" - app:cardElevation="@dimen/card_elevation" - app:cardUseCompatPadding="true" - app:layout_constraintTop_toBottomOf="@id/bannerImage" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - android:layout_marginHorizontal="@dimen/card_margin" - android:layout_marginBottom="16dp"> + android:visibility="gone" + tools:visibility="visible" /> - <androidx.constraintlayout.widget.ConstraintLayout - android:layout_width="match_parent" + <FrameLayout + android:layout_width="match_parent" + android:layout_height="0dp" + app:layout_constraintTop_toBottomOf="@id/offlineBanner" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent"> + + <androidx.constraintlayout.widget.ConstraintLayout + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <androidx.appcompat.widget.AppCompatImageView + android:id="@+id/bannerImage" + android:layout_width="0dp" + android:layout_height="@dimen/about_logo_height" + app:layout_constraintWidth_percent="0.5" + app:layout_constraintDimensionRatio="1" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + android:layout_marginHorizontal="@dimen/card_margin" + app:srcCompat="@drawable/about_logo" /> + + <androidx.cardview.widget.CardView + android:layout_width="0dp" android:layout_height="wrap_content" - android:paddingVertical="@dimen/card_large_padding" - android:paddingHorizontal="@dimen/card_padding"> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/usernameInputLayout" - style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" + app:cardCornerRadius="@dimen/card_border_radius" + app:cardElevation="@dimen/card_elevation" + app:cardUseCompatPadding="true" + app:layout_constraintTop_toBottomOf="@id/bannerImage" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + android:layout_marginHorizontal="@dimen/card_margin" + android:layout_marginBottom="16dp"> + + <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:hint="@string/login_username" - app:layout_constraintBottom_toTopOf="@+id/passwordInputLayout" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/usernameEditText" + android:paddingVertical="@dimen/card_large_padding" + android:paddingHorizontal="@dimen/card_padding"> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/usernameInputLayout" + style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" android:layout_width="match_parent" android:layout_height="wrap_content" - android:inputType="textEmailAddress"/> - - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/passwordInputLayout" - style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/login_password" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/usernameInputLayout" - app:layout_constraintBottom_toTopOf="@id/urlInputLayout" - android:layout_marginTop="@dimen/fields_spacing"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/passwordEditText" + android:hint="@string/login_username" + app:layout_constraintBottom_toTopOf="@+id/passwordInputLayout" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/usernameEditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="textEmailAddress"/> + + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/passwordInputLayout" + style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" android:layout_width="match_parent" android:layout_height="wrap_content" - android:inputType="textPassword"/> - - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.textfield.TextInputLayout - android:id="@+id/urlInputLayout" - style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:hint="@string/login_url" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintHorizontal_bias="0.5" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@+id/passwordInputLayout" - app:layout_constraintBottom_toTopOf="@id/signinButton" - android:layout_marginTop="@dimen/fields_spacing" - android:visibility="gone"> - - <com.google.android.material.textfield.TextInputEditText - android:id="@+id/urlEditText" + android:hint="@string/login_password" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/usernameInputLayout" + app:layout_constraintBottom_toTopOf="@id/urlInputLayout" + android:layout_marginTop="@dimen/fields_spacing"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/passwordEditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="textPassword"/> + + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.textfield.TextInputLayout + android:id="@+id/urlInputLayout" + style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox" android:layout_width="match_parent" android:layout_height="wrap_content" - android:inputType="textUri"/> - - </com.google.android.material.textfield.TextInputLayout> - - <com.google.android.material.button.MaterialButton - android:id="@+id/signinButton" - android:layout_width="0dp" - android:layout_height="wrap_content" - app:layout_constraintWidth_percent="0.5" - app:layout_constraintTop_toBottomOf="@id/urlInputLayout" - app:layout_constraintStart_toStartOf="parent" - app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintBottom_toBottomOf="parent" - app:layout_constraintHorizontal_bias="0.5" - android:layout_marginTop="@dimen/fields_large_spacing" - android:text="@string/login_login"/> - - </androidx.constraintlayout.widget.ConstraintLayout> - - </androidx.cardview.widget.CardView> - - </androidx.constraintlayout.widget.ConstraintLayout> - - <include - android:id="@+id/infoLoading" - android:layout_width="match_parent" - android:layout_height="match_parent" - layout="@layout/loading_indicator" - android:visibility="gone"/> - -</FrameLayout>
\ No newline at end of file + android:hint="@string/login_url" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.5" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/passwordInputLayout" + app:layout_constraintBottom_toTopOf="@id/signinButton" + android:layout_marginTop="@dimen/fields_spacing" + android:visibility="gone"> + + <com.google.android.material.textfield.TextInputEditText + android:id="@+id/urlEditText" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType="textUri"/> + + </com.google.android.material.textfield.TextInputLayout> + + <com.google.android.material.button.MaterialButton + android:id="@+id/signinButton" + android:layout_width="0dp" + android:layout_height="wrap_content" + app:layout_constraintWidth_percent="0.5" + app:layout_constraintTop_toBottomOf="@id/urlInputLayout" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintHorizontal_bias="0.5" + android:layout_marginTop="@dimen/fields_large_spacing" + android:text="@string/login_login"/> + + </androidx.constraintlayout.widget.ConstraintLayout> + + </androidx.cardview.widget.CardView> + + </androidx.constraintlayout.widget.ConstraintLayout> + + <include + android:id="@+id/infoLoading" + android:layout_width="match_parent" + android:layout_height="match_parent" + layout="@layout/loading_indicator" + android:visibility="gone"/> + + </FrameLayout> +</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/androidApp/src/main/res/layout/offline_banner.xml b/androidApp/src/main/res/layout/offline_banner.xml new file mode 100644 index 0000000..6710d08 --- /dev/null +++ b/androidApp/src/main/res/layout/offline_banner.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="10dp" + android:background="@color/colorAccent"> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:textAlignment="center" + android:text="@string/login_no_internet" + android:textColor="@android:color/white" /> + +</FrameLayout>
\ No newline at end of file diff --git a/androidApp/src/main/res/layout/units_activity.xml b/androidApp/src/main/res/layout/units_activity.xml index 60a77db..8f46516 100644 --- a/androidApp/src/main/res/layout/units_activity.xml +++ b/androidApp/src/main/res/layout/units_activity.xml @@ -15,7 +15,16 @@ app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent"/> + app:layout_constraintTop_toTopOf="parent" /> + + <include + android:id="@+id/offlineBanner" + layout="@layout/offline_banner" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintEnd_toEndOf="parent" + android:visibility="gone" + tools:visibility="visible" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/displayModeToggle" @@ -28,7 +37,7 @@ app:elevation="@dimen/fab_elevation" app:fabSize="mini" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toTopOf="parent" + app:layout_constraintTop_toBottomOf="@id/offlineBanner" tools:ignore="ContentDescription" /> <com.google.android.material.card.MaterialCardView @@ -83,7 +92,7 @@ app:elevation="@dimen/fab_elevation" app:fabSize="mini" app:layout_constraintEnd_toStartOf="@id/userButton" - app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toBottomOf="@id/offlineBanner" /> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/userButton" @@ -98,6 +107,6 @@ app:elevation="@dimen/fab_elevation" app:fabSize="mini" app:layout_constraintEnd_toEndOf="parent" - app:layout_constraintTop_toTopOf="parent"/> + app:layout_constraintTop_toBottomOf="@id/offlineBanner" /> </androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file diff --git a/androidApp/src/main/res/values-es-rMX/strings.xml b/androidApp/src/main/res/values-es-rMX/strings.xml index 53e74a2..2b9994a 100644 --- a/androidApp/src/main/res/values-es-rMX/strings.xml +++ b/androidApp/src/main/res/values-es-rMX/strings.xml @@ -19,6 +19,7 @@ <string name="login_password_missing">Falta la contraseña</string> <string name="login_url_missing">Falta la URL del servidor</string> <string name="login_login_failed">Falló el inicio de sesión</string> + <string name="login_no_internet">No hay conexión a internet</string> <!-- UnitsActivity --> <string name="toggle_list">Cambiar a lista de dispositivos</string> diff --git a/androidApp/src/main/res/values/dimen.xml b/androidApp/src/main/res/values/dimen.xml index 8c8c5f5..4afaf23 100644 --- a/androidApp/src/main/res/values/dimen.xml +++ b/androidApp/src/main/res/values/dimen.xml @@ -29,10 +29,12 @@ <dimen name="nav_height">64dp</dimen> <!-- Map --> + <dimen name="marker_size">55dp</dimen> + <dimen name="vertex_size">24dp</dimen> <dimen name="marker_label_text_size">11sp</dimen> <dimen name="geofence_label_text_size">11sp</dimen> <dimen name="geofence_label_width">4dp</dimen> - <dimen name="report_label_width">10dp</dimen> + <dimen name="report_label_width">12dp</dimen> <dimen name="attribution_text_size">11sp</dimen> <!-- Reports bottom sheet --> diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml index 3177ad6..3dacd7a 100644 --- a/androidApp/src/main/res/values/strings.xml +++ b/androidApp/src/main/res/values/strings.xml @@ -33,6 +33,7 @@ <string name="login_password_missing">Password is missing</string> <string name="login_url_missing">Server URL is missing</string> <string name="login_login_failed">Login failed</string> + <string name="login_no_internet">No internet connection</string> <!-- UnitsActivity --> <string name="toggle_list">Switch to device list</string> |