diff options
Diffstat (limited to 'androidApp/src/main')
6 files changed, 255 insertions, 86 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 16cad74..1563534 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/TrackerApp.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/TrackerApp.kt @@ -1,16 +1,14 @@ package mx.trackermap.TrackerMap.android import android.app.Application +import kotlinx.coroutines.DelicateCoroutinesApi import mx.trackermap.TrackerMap.android.details.commands.UnitCommandsViewModel import mx.trackermap.TrackerMap.android.details.information.UnitInformationViewModel import mx.trackermap.TrackerMap.android.details.reports.UnitReportsViewModel import mx.trackermap.TrackerMap.android.session.LoginViewModel import mx.trackermap.TrackerMap.android.units.UnitsViewModel -import mx.trackermap.TrackerMap.client.apis.CommandsApi -import mx.trackermap.TrackerMap.client.apis.DevicesApi -import mx.trackermap.TrackerMap.client.apis.PositionsApi -import mx.trackermap.TrackerMap.client.apis.ReportsApi -import mx.trackermap.TrackerMap.client.apis.SessionApi +import mx.trackermap.TrackerMap.client.apis.* +import mx.trackermap.TrackerMap.controllers.GeofencesController import mx.trackermap.TrackerMap.controllers.UnitsController import org.koin.android.ext.koin.androidContext import org.koin.android.ext.koin.androidLogger @@ -19,6 +17,7 @@ import org.koin.core.context.startKoin import org.koin.core.logger.Level import org.koin.dsl.module +@DelicateCoroutinesApi class TrackerApp : Application() { override fun onCreate() { @@ -31,8 +30,10 @@ class TrackerApp : Application() { single { PositionsApi(get()) } single { CommandsApi(get()) } single { ReportsApi(get()) } + single { GeofencesApi(get()) } single { UnitsController(get(), get()) } + single { GeofencesController(get()) } viewModel { LoginViewModel(get(), get()) } viewModel { UnitInformationViewModel(get()) } diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt index bbd1fd7..33b0dfc 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt @@ -8,18 +8,24 @@ import android.view.ViewGroup import android.widget.PopupMenu import android.widget.TableRow import android.widget.TextView +import androidx.core.view.setMargins import androidx.fragment.app.Fragment import androidx.fragment.app.commit +import kotlinx.coroutines.DelicateCoroutinesApi import mx.trackermap.TrackerMap.android.R import mx.trackermap.TrackerMap.android.databinding.UnitDetailsReportsBinding import mx.trackermap.TrackerMap.android.details.UnitDetailsAdapter import mx.trackermap.TrackerMap.android.map.MapFragment import mx.trackermap.TrackerMap.android.map.MarkerTransformations import mx.trackermap.TrackerMap.client.models.Event +import mx.trackermap.TrackerMap.client.models.EventInformation import mx.trackermap.TrackerMap.client.models.Position import mx.trackermap.TrackerMap.client.models.Stop +import mx.trackermap.TrackerMap.utils.Formatter import org.koin.androidx.viewmodel.ext.android.viewModel +import kotlin.math.max +@DelicateCoroutinesApi class UnitReportsFragment : Fragment() { private var _binding: UnitDetailsReportsBinding? = null @@ -42,8 +48,8 @@ class UnitReportsFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - unitReportsViewModel.deviceId.value = - arguments?.getInt(UnitDetailsAdapter.DEVICE_ID_ARG) ?: 0 + unitReportsViewModel.setDeviceId( + arguments?.getInt(UnitDetailsAdapter.DEVICE_ID_ARG) ?: 0) initializeMap() setupEvents() setupObservers() @@ -62,21 +68,23 @@ class UnitReportsFragment : Fragment() { private fun setupEvents() { binding.reportType.setOnPositionChangedListener { position -> - unitReportsViewModel.reportType.value = when (position) { - 0 -> UnitReportsViewModel.ReportType.POSITIONS - 1 -> UnitReportsViewModel.ReportType.EVENTS - else -> UnitReportsViewModel.ReportType.STOPS - } + unitReportsViewModel.setReportType( + when (position) { + 0 -> UnitReportsViewModel.ReportType.POSITIONS + 1 -> UnitReportsViewModel.ReportType.EVENTS + else -> UnitReportsViewModel.ReportType.STOPS + } + ) } binding.periodButton.setOnClickListener { showPeriodPopUp(it) } - unitReportsViewModel.reportPeriod.value = UnitReportsViewModel.ReportPeriod.DAY - unitReportsViewModel.reportType.value = UnitReportsViewModel.ReportType.POSITIONS + unitReportsViewModel.setReportPeriod(UnitReportsViewModel.ReportPeriod.DAY) + unitReportsViewModel.setReportType(UnitReportsViewModel.ReportType.POSITIONS) } private fun setupObservers() { - unitReportsViewModel.report.observe(this) { report -> + unitReportsViewModel.report.observe(viewLifecycleOwner) { report -> Log.d("UnitReportsFragment", "Report available: $report") when (report) { @@ -96,7 +104,7 @@ class UnitReportsFragment : Fragment() { } } - unitReportsViewModel.reportPeriod.observe(this) { period -> + unitReportsViewModel.reportPeriod.observe(viewLifecycleOwner) { period -> Log.d("UnitReportsFragment", "Period changed: $period") if (period == null) { return@observe @@ -110,17 +118,23 @@ class UnitReportsFragment : Fragment() { } ) } + + unitReportsViewModel.geofences.observe(this) { geofences -> + Log.d("UnitReportsFragment", "Success $geofences") + } } private fun showPeriodPopUp(view: View) { val popOver = PopupMenu(context, view) popOver.menuInflater.inflate(R.menu.report_period_options, popOver.menu) popOver.setOnMenuItemClickListener { item -> - unitReportsViewModel.reportPeriod.value = when (item.itemId) { - R.id.dayOption -> UnitReportsViewModel.ReportPeriod.DAY - R.id.weekOption -> UnitReportsViewModel.ReportPeriod.WEEK - else -> UnitReportsViewModel.ReportPeriod.MONTH - } + unitReportsViewModel.setReportPeriod( + when (item.itemId) { + R.id.dayOption -> UnitReportsViewModel.ReportPeriod.DAY + R.id.weekOption -> UnitReportsViewModel.ReportPeriod.WEEK + else -> UnitReportsViewModel.ReportPeriod.MONTH + } + ) true } popOver.show() @@ -137,25 +151,69 @@ class UnitReportsFragment : Fragment() { ) } - private fun display(events: Array<Event>) { + private fun display(events: Array<EventInformation>) { Log.d("UnitReportsFragment", "Displaying events: $events") binding.eventsScroll.visibility = View.VISIBLE binding.reportsMapContainer.visibility = View.GONE - binding.eventsTable.removeViews(1, Math.max(0, binding.eventsTable.childCount - 1)) + binding.eventsTable.removeViews(1, max(0, binding.eventsTable.childCount - 1)) val context = context!! events.forEach { event -> + val layoutParams = TableRow.LayoutParams() + layoutParams.setMargins( + resources.getDimensionPixelSize(R.dimen.padding) + ) val row = TableRow(context) - val eventText = TextView(context) val datetimeText = TextView(context) + val eventText = TextView(context) + val geofenceText = TextView(context) + val addressText = TextView(context) + + datetimeText.layoutParams = layoutParams + eventText.layoutParams = layoutParams + geofenceText.layoutParams = layoutParams + addressText.layoutParams = layoutParams - row.addView(eventText) row.addView(datetimeText) + row.addView(eventText) + row.addView(geofenceText) + row.addView(addressText) binding.eventsTable.addView(row) - eventText.text = event.type - datetimeText.text = event.eventTime + event.event.eventTime?.let { it -> + datetimeText.text = Formatter.formatDate(it) + } + event.event.type?.let { + eventText.text = getString(when (it) { + "deviceOnline" -> R.string.event_device_online + "deviceUnknown" -> R.string.event_device_unknown + "deviceOffline" -> R.string.event_device_offline + "deviceInactive" -> R.string.event_device_inactive + "deviceMoving" -> R.string.event_device_moving + "deviceStopped" -> R.string.event_device_stopped + "deviceOverspeed" -> R.string.event_device_overspeed + "deviceFuelDrop" -> R.string.event_device_fuel_drop + "commandResult" -> R.string.event_command_result + "geofenceEnter" -> R.string.event_geofence_enter + "geofenceExit" -> R.string.event_geofence_exit + "alarm" -> R.string.event_alarm + "ignitionOn" -> R.string.event_ignition_on + "ignitionOff" -> R.string.event_ignition_off + "maintenance" -> R.string.event_maintenance + "textMessage" -> R.string.event_text_message + "driverChanged" -> R.string.event_driver_changed + else -> R.string.event_unknown + }) + } + event.event.geofenceId?.let { + val geofence = unitReportsViewModel.getGeofence(it) + Log.d("UnitReportsFragment", "Getting geofence $geofence") + geofenceText.text = geofence?.name + } + event.position?.let { + addressText.text = it.address + } } } diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsViewModel.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsViewModel.kt index 98b8a34..48486e1 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsViewModel.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsViewModel.kt @@ -1,22 +1,20 @@ package mx.trackermap.TrackerMap.android.details.reports import android.util.Log -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.ViewModel -import androidx.lifecycle.asFlow -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.* +import kotlinx.coroutines.DelicateCoroutinesApi import java.text.SimpleDateFormat -import java.util.Calendar -import java.util.Date import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import mx.trackermap.TrackerMap.client.apis.ReportsApi -import mx.trackermap.TrackerMap.client.models.Event -import mx.trackermap.TrackerMap.client.models.Position -import mx.trackermap.TrackerMap.client.models.Stop +import mx.trackermap.TrackerMap.client.models.* +import mx.trackermap.TrackerMap.controllers.GeofencesController import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.util.* +import java.util.Calendar +@DelicateCoroutinesApi class UnitReportsViewModel( private val reportsApi: ReportsApi, savedStateHandle: SavedStateHandle @@ -24,7 +22,7 @@ class UnitReportsViewModel( sealed class Report { class PositionsReport(val positions: Array<Position>) : Report() - class EventsReport(val events: Array<Event>) : Report() + class EventsReport(val events: Array<EventInformation>) : Report() class StopsReport(val stops: Array<Stop>) : Report() object LoadingReport: Report() } @@ -37,13 +35,22 @@ class UnitReportsViewModel( DAY, WEEK, MONTH } - var deviceId = savedStateHandle.getLiveData("deviceId", 0) - val reportType: MutableLiveData<ReportType> = savedStateHandle.getLiveData("reportType", null) - val reportPeriod: MutableLiveData<ReportPeriod> = + private val geofenceController: GeofencesController by inject() + + private var _deviceId = savedStateHandle.getLiveData("deviceId", 0) + private val _reportType: MutableLiveData<ReportType> = savedStateHandle.getLiveData("reportType", null) + private val _reportPeriod: MutableLiveData<ReportPeriod> = savedStateHandle.getLiveData("reportPeriod", null) - val report: MutableLiveData<Report> = MutableLiveData() + private val _report: MutableLiveData<Report> = MutableLiveData() + private val _geofences: MutableLiveData<Map<Int, Geofence>> = MutableLiveData() - val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'") + val deviceId: LiveData<Int> get() = _deviceId + val reportType: LiveData<ReportType> get() = _reportType + val reportPeriod: LiveData<ReportPeriod> get() = _reportPeriod + val report: LiveData<Report> get() = _report + val geofences: LiveData<Map<Int, Geofence>> get() = _geofences + + private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()) init { viewModelScope.launch { @@ -52,47 +59,49 @@ class UnitReportsViewModel( viewModelScope.launch { setupPeriodObserver() } + viewModelScope.launch { + setupGeofenceObserver() + } } private suspend fun setupTypeObserver() { - reportType.asFlow().collect { + _reportType.asFlow().collect { fetchReport() } } private suspend fun setupPeriodObserver() { - reportPeriod.asFlow().collect { + _reportPeriod.asFlow().collect { fetchReport() } } - private fun fetchReport() { - if (reportType.value == null || reportPeriod.value == null) { - return + private suspend fun setupGeofenceObserver() { + geofenceController.geofencesFlow.collect { + _geofences.postValue(it) } + } - val (currentDate, previousDate) = getDates() - Log.d("UnitReportsVM", "Current report type: ${reportType.value.toString()}") - Log.d("UnitReportsVM", "Current report period: ${reportPeriod.value.toString()}") - Log.d("UnitReportsVM", "Current date:${dateFormat.format(currentDate)}") - Log.d("UnitReportsVM", "Previous date:${dateFormat.format(previousDate)}") + fun setDeviceId (id: Int) { + _deviceId.value = id + } - report.postValue(Report.LoadingReport) - viewModelScope.launch { - when (reportType.value!!) { - ReportType.POSITIONS -> fetchPositions(previousDate, currentDate) - ReportType.EVENTS -> fetchEvents(previousDate, currentDate) - ReportType.STOPS -> fetchStops(previousDate, currentDate) - } - } + fun setReportType(reportType: ReportType) { + _reportType.value = reportType + } + + fun setReportPeriod(reportPeriod: ReportPeriod) { + _reportPeriod.value = reportPeriod } + fun getGeofence(id: Int): Geofence? = _geofences.value?.get(id) + private fun getDates(): Pair<Date, Date> { val calendar = Calendar.getInstance() val currentDate = calendar.time calendar.add( - Calendar.DATE, when (reportPeriod.value!!) { + Calendar.DATE, when (_reportPeriod.value!!) { ReportPeriod.DAY -> -1 ReportPeriod.WEEK -> -7 ReportPeriod.MONTH -> -30 @@ -103,30 +112,66 @@ class UnitReportsViewModel( return Pair(currentDate, previousDate) } + private fun fetchReport() { + if (_reportType.value == null || _reportPeriod.value == null) { + return + } + + val (currentDate, previousDate) = getDates() + Log.d("UnitReportsVM", "Current report type: ${_reportType.value.toString()}") + Log.d("UnitReportsVM", "Current report period: ${_reportPeriod.value.toString()}") + Log.d("UnitReportsVM", "Current date:${dateFormat.format(currentDate)}") + Log.d("UnitReportsVM", "Previous date:${dateFormat.format(previousDate)}") + + _report.postValue(Report.LoadingReport) + viewModelScope.launch { + when (_reportType.value!!) { + ReportType.POSITIONS -> fetchPositions(previousDate, currentDate) + ReportType.EVENTS -> fetchEvents(previousDate, currentDate) + ReportType.STOPS -> fetchStops(previousDate, currentDate) + } + } + } + private suspend fun fetchPositions(from: Date, to: Date) { Log.d("UnitReportsVM", "Fetching positions") val result = reportsApi.reportsRouteGet( dateFormat.format(from), dateFormat.format(to), - deviceId.value!! + _deviceId.value!! ) Log.d("UnitReportsVM", "Positions report: $result") - report.postValue(Report.PositionsReport(result)) + _report.postValue(Report.PositionsReport(result)) } private suspend fun fetchEvents(from: Date, to: Date) { Log.d("UnitReportsVM", "Fetching events") - val result = reportsApi.reportsEventsGet( + val positionsResult = reportsApi.reportsRouteGet( dateFormat.format(from), dateFormat.format(to), - deviceId.value!! + _deviceId.value!! ) + val eventsResult = reportsApi.reportsEventsGet( + dateFormat.format(from), + dateFormat.format(to), + _deviceId.value!! + ) + + val result = mutableListOf<EventInformation>() + + eventsResult.forEach { event -> + result.add(EventInformation( + event = event, + position = positionsResult.find { it.id == event.positionId } + )) + } + Log.d("UnitReportsVM", "Events report: $result") - report.postValue(Report.EventsReport(result)) + _report.postValue(Report.EventsReport(result.toTypedArray())) } private suspend fun fetchStops(from: Date, to: Date) { @@ -135,10 +180,10 @@ class UnitReportsViewModel( val result = reportsApi.reportsStopsGet( dateFormat.format(from), dateFormat.format(to), - deviceId.value!! + _deviceId.value!! ) Log.d("UnitReportsVM", "Stops report: $result") - report.postValue(Report.StopsReport(result)) + _report.postValue(Report.StopsReport(result)) } }
\ No newline at end of file diff --git a/androidApp/src/main/res/layout/unit_details_reports.xml b/androidApp/src/main/res/layout/unit_details_reports.xml index 52a3926..1eaf68c 100644 --- a/androidApp/src/main/res/layout/unit_details_reports.xml +++ b/androidApp/src/main/res/layout/unit_details_reports.xml @@ -28,29 +28,50 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent"> - <TableLayout - android:id="@+id/eventsTable" + <HorizontalScrollView android:layout_width="match_parent" - android:layout_height="wrap_content" - android:isScrollContainer="true" - android:scrollbars="vertical" - android:stretchColumns="*"> + android:layout_height="wrap_content"> + + <TableLayout + android:id="@+id/eventsTable" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:isScrollContainer="true" + android:scrollbars="vertical" + android:stretchColumns="*"> + + <TableRow android:background="@color/colorPrimary"> + + <TextView + android:paddingHorizontal="@dimen/padding" + android:text="@string/table_datetime" + android:textColor="@color/background" + android:padding="@dimen/padding" /> + + <TextView + android:paddingHorizontal="@dimen/padding" + android:text="@string/table_event" + android:textColor="@color/background" + android:padding="@dimen/padding" /> + + <TextView + android:paddingHorizontal="@dimen/padding" + android:text="@string/table_geofence" + android:textColor="@color/background" + android:padding="@dimen/padding" /> - <TableRow android:background="@color/colorPrimary"> + <TextView + android:paddingHorizontal="@dimen/padding" + android:text="@string/table_address" + android:textColor="@color/background" + android:padding="@dimen/padding" /> - <TextView - android:paddingHorizontal="@dimen/padding" - android:text="@string/table_event" - android:textColor="@color/background" /> + </TableRow> - <TextView - android:paddingHorizontal="@dimen/padding" - android:text="@string/table_datetime" - android:textColor="@color/background" /> + </TableLayout> - </TableRow> + </HorizontalScrollView> - </TableLayout> </ScrollView> <LinearLayout diff --git a/androidApp/src/main/res/values-es-rMX/strings.xml b/androidApp/src/main/res/values-es-rMX/strings.xml index 7397599..9429494 100644 --- a/androidApp/src/main/res/values-es-rMX/strings.xml +++ b/androidApp/src/main/res/values-es-rMX/strings.xml @@ -50,6 +50,28 @@ <string name="month_period">Mes</string> <string name="period">Periodo</string> <string name="select_period">Seleccionar</string> + <string name="table_event">Evento</string> <string name="table_datetime">Fecha y hora</string> + <string name="table_geofence">Geocerca</string> + <string name="table_address">Dirección</string> + + <string name="event_device_online">Unidad en línea</string> + <string name="event_device_unknown">Unidad en estado desconocido</string> + <string name="event_device_offline">Unidad fuera de línea</string> + <string name="event_device_inactive">Unidad inactiva</string> + <string name="event_device_moving">Unidad en movimiento</string> + <string name="event_device_stopped">Unidad detenida</string> + <string name="event_device_overspeed">Excedido el límite de velocidad</string> + <string name="event_device_fuel_drop">Pérdida de combustible</string> + <string name="event_command_result">Resultado del comando</string> + <string name="event_geofence_enter">Entrada en la geocerca</string> + <string name="event_geofence_exit">Salida de la geocerca</string> + <string name="event_alarm">Alarma</string> + <string name="event_ignition_on">Ignición encendida</string> + <string name="event_ignition_off">Ignición apagada</string> + <string name="event_maintenance">Se requiere mantenimiento</string> + <string name="event_text_message">Mensaje de texto recibido</string> + <string name="event_driver_changed">El conductor ha cambiado</string> + <string name="event_unknown">Evento desconocido</string> </resources>
\ No newline at end of file diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml index 93f088a..0c6a29e 100644 --- a/androidApp/src/main/res/values/strings.xml +++ b/androidApp/src/main/res/values/strings.xml @@ -58,6 +58,28 @@ <string name="month_period">Month</string> <string name="period">Period</string> <string name="select_period">Select</string> + <string name="table_event">Event</string> <string name="table_datetime">Datetime</string> + <string name="table_geofence">Geofence</string> + <string name="table_address">Address</string> + + <string name="event_device_online">Status online</string> + <string name="event_device_unknown">Status unknown</string> + <string name="event_device_offline">Status offline</string> + <string name="event_device_inactive">Device inactive</string> + <string name="event_device_moving">Device moving</string> + <string name="event_device_stopped">Device stopped</string> + <string name="event_device_overspeed">Speed limit exceeded</string> + <string name="event_device_fuel_drop">Fuel drop</string> + <string name="event_command_result">Command result</string> + <string name="event_geofence_enter">Geofence entered</string> + <string name="event_geofence_exit">Geofence exited</string> + <string name="event_alarm">Alarm</string> + <string name="event_ignition_on">Ignition on</string> + <string name="event_ignition_off">Ignition off</string> + <string name="event_maintenance">Maintenance required</string> + <string name="event_text_message">Text message received</string> + <string name="event_driver_changed">Driver changed</string> + <string name="event_unknown">Unknown event</string> </resources> |