aboutsummaryrefslogtreecommitdiff
path: root/androidApp/src/main/java/mx/trackermap/TrackerMap/android
diff options
context:
space:
mode:
Diffstat (limited to 'androidApp/src/main/java/mx/trackermap/TrackerMap/android')
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/TrackerApp.kt4
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt43
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginFragment.kt23
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/session/LoginViewModel.kt20
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/shared/MarkerTransformations.kt19
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsActivity.kt5
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt14
7 files changed, 107 insertions, 21 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")