aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIván Ávalos <avalos@disroot.org>2022-04-18 23:10:29 -0500
committerIván Ávalos <avalos@disroot.org>2022-04-18 23:10:29 -0500
commit65bf515eb2cb95a0e13cf71788e60bf6b16f5fcd (patch)
treedcc65a1f090f5afa93e6bc7c6e6c178dc10eaae2
parent591eb35abf77aa23e5b897c92eb9ff588a968f16 (diff)
parentfc3e2a6e316cb8acd6aac0553c92f9dbc6b669b4 (diff)
downloadetbsa-trackermap-mobile-65bf515eb2cb95a0e13cf71788e60bf6b16f5fcd.tar.gz
etbsa-trackermap-mobile-65bf515eb2cb95a0e13cf71788e60bf6b16f5fcd.tar.bz2
etbsa-trackermap-mobile-65bf515eb2cb95a0e13cf71788e60bf6b16f5fcd.zip
Merge branch 'main' of lraspi:/secret/git/repos/etbsa-trackermap-mobile
-rw-r--r--androidApp/build.gradle.kts4
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/information/UnitInformationFragment.kt2
-rw-r--r--androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt153
-rw-r--r--androidApp/src/main/res/drawable/icon_down.xml10
-rw-r--r--androidApp/src/main/res/drawable/icon_up.xml10
-rw-r--r--androidApp/src/main/res/layout/unit_details_reports.xml413
-rw-r--r--androidApp/src/main/res/menu/report_period_options.xml4
-rw-r--r--androidApp/src/main/res/values-es-rMX/strings.xml6
-rw-r--r--androidApp/src/main/res/values/dimen.xml6
-rw-r--r--androidApp/src/main/res/values/strings.xml6
-rw-r--r--androidApp/src/main/res/values/styles.xml13
-rw-r--r--iosApp/iosApp/Details/Reports/UnitReportsView.swift133
-rw-r--r--iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift50
-rw-r--r--iosApp/iosApp/en.lproj/Localizable.strings3
-rw-r--r--iosApp/iosApp/es-419.lproj/Localizable.strings3
-rw-r--r--shared/src/androidMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt23
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/ReportController.kt15
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt9
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/Formatter.kt5
-rw-r--r--shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/ReportDates.kt169
-rw-r--r--shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt22
21 files changed, 736 insertions, 323 deletions
diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts
index 0ae667e..1ef895a 100644
--- a/androidApp/build.gradle.kts
+++ b/androidApp/build.gradle.kts
@@ -11,8 +11,8 @@ android {
applicationId = "net.etbsa.etbsa"
minSdk = 21
targetSdk = 31
- versionCode = 1300
- versionName = "1.2"
+ versionCode = 1401
+ versionName = "1.3.1"
ndk {
abiFilters.clear()
abiFilters += listOf("armeabi-v7a", "arm64-v8a")
diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/information/UnitInformationFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/information/UnitInformationFragment.kt
index e731587..fb0a0bc 100644
--- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/information/UnitInformationFragment.kt
+++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/information/UnitInformationFragment.kt
@@ -115,7 +115,7 @@ class UnitInformationFragment : Fragment() {
}
private fun displayInformation(unit: UnitInformation) {
- val context = context!!
+ val context = requireContext()
val details: MutableList<Pair<String, String>> = mutableListOf()
unit.device.contact?.let { contact ->
details.add(getString(R.string.unit_info_contact) to contact)
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 60ad531..6cc36bd 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
@@ -18,6 +18,8 @@
package mx.trackermap.TrackerMap.android.details.reports
import android.app.Activity
+import android.app.DatePickerDialog
+import android.app.TimePickerDialog
import android.content.Intent
import android.os.Build
import android.os.Bundle
@@ -27,14 +29,16 @@ import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
-import android.widget.PopupMenu
-import android.widget.TableRow
-import android.widget.TextView
+import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.view.setMargins
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
+import com.google.android.material.bottomsheet.BottomSheetBehavior
+import com.google.android.material.card.MaterialCardView
+import io.ktor.utils.io.*
import kotlinx.coroutines.DelicateCoroutinesApi
+import kotlinx.datetime.LocalDateTime
import mx.trackermap.TrackerMap.android.R
import mx.trackermap.TrackerMap.android.databinding.UnitDetailsReportsBinding
import mx.trackermap.TrackerMap.android.details.UnitDetailsAdapter
@@ -47,6 +51,7 @@ import mx.trackermap.TrackerMap.utils.Formatter
import mx.trackermap.TrackerMap.utils.ReportDates
import org.koin.androidx.viewmodel.ext.android.viewModel
import java.io.*
+import java.util.*
import kotlin.math.max
import kotlin.time.ExperimentalTime
@@ -59,12 +64,22 @@ class UnitReportsFragment : Fragment() {
private val unitReportsViewModel: UnitReportsViewModel by viewModel()
private lateinit var mapFragment: MapWrapperFragment
+ private lateinit var bottomSheetBehavior: BottomSheetBehavior<MaterialCardView>
private var reportFile: ReportController.Report.XlsxReport? = null
private var exportAction: UnitReportsViewModel.ExportAction? = null
+ private lateinit var fromPickedDatetime: LocalDateTime
+ private lateinit var fromDatePicker: DatePickerDialog
+ private lateinit var fromTimePicker: TimePickerDialog
+
+ private lateinit var toPickedDatetime: LocalDateTime
+ private lateinit var toDatePicker: DatePickerDialog
+ private lateinit var toTimePicker: TimePickerDialog
+
private companion object {
- const val XLSX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
+ const val XLSX_MIME_TYPE =
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
}
override fun onCreateView(
@@ -80,7 +95,12 @@ class UnitReportsFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)
unitReportsViewModel.setDeviceId(
- arguments?.getInt(UnitDetailsAdapter.DEVICE_ID_ARG) ?: 0)
+ arguments?.getInt(UnitDetailsAdapter.DEVICE_ID_ARG) ?: 0
+ )
+
+ bottomSheetBehavior = BottomSheetBehavior.from(binding.bottomSheet)
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
+
setupEvents()
initializeMap()
}
@@ -129,6 +149,12 @@ class UnitReportsFragment : Fragment() {
binding.periodButton.setOnClickListener {
showPeriodPopUp(it)
}
+ binding.fromEditText.setOnClickListener {
+ fromDatePicker.show()
+ }
+ binding.toEditText.setOnClickListener {
+ toDatePicker.show()
+ }
binding.exportButton.setOnClickListener {
exportAction = UnitReportsViewModel.ExportAction.ACTION_SAVE
unitReportsViewModel.fetchReportXlsx()
@@ -137,7 +163,27 @@ class UnitReportsFragment : Fragment() {
exportAction = UnitReportsViewModel.ExportAction.ACTION_SHARE
unitReportsViewModel.fetchReportXlsx()
}
- unitReportsViewModel.setReportPeriod(ReportDates.ReportPeriod.TODAY)
+ binding.toggleSheet.setOnClickListener {
+ if (bottomSheetBehavior.state == BottomSheetBehavior.STATE_EXPANDED) {
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
+ } else {
+ bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
+ }
+ }
+ bottomSheetBehavior.addBottomSheetCallback(object: BottomSheetBehavior.BottomSheetCallback() {
+ override fun onStateChanged(bottomSheet: View, newState: Int) {
+ if (newState == BottomSheetBehavior.STATE_EXPANDED) {
+ binding.toggleSheet.setImageResource(R.drawable.icon_down)
+ binding.toggleSheet.contentDescription = getString(R.string.shared_slide_down)
+ } else {
+ binding.toggleSheet.setImageResource(R.drawable.icon_up)
+ binding.toggleSheet.contentDescription = getString(R.string.shared_slide_up)
+ }
+ }
+
+ override fun onSlide(bottomSheet: View, slideOffset: Float) {}
+ })
+ unitReportsViewModel.setReportPeriod(ReportDates.ReportPeriod.Today())
unitReportsViewModel.setReportType(ReportController.ReportType.POSITIONS)
}
@@ -149,7 +195,11 @@ class UnitReportsFragment : Fragment() {
when (report) {
is ReportController.Report.PositionsReport -> {
mapFragment.display(unitReportsViewModel.geofences.value!!)
- mapFragment.display(report.positions.toTypedArray(), isReport = true, center = true)
+ mapFragment.display(
+ report.positions.toTypedArray(),
+ isReport = true,
+ center = true
+ )
showMap(true)
}
is ReportController.Report.EventsReport -> {
@@ -181,15 +231,68 @@ class UnitReportsFragment : Fragment() {
binding.periodButton.text = context?.getString(
when (period) {
- ReportDates.ReportPeriod.TODAY -> R.string.period_today
- ReportDates.ReportPeriod.LAST_24 -> R.string.period_last_24
- ReportDates.ReportPeriod.YESTERDAY -> R.string.period_yesterday
- ReportDates.ReportPeriod.THIS_WEEK -> R.string.period_this_week
- ReportDates.ReportPeriod.LAST_7 -> R.string.period_last_7
- ReportDates.ReportPeriod.THIS_MONTH -> R.string.period_this_month
- ReportDates.ReportPeriod.LAST_30 -> R.string.period_last_30
+ is ReportDates.ReportPeriod.Today -> R.string.period_today
+ is ReportDates.ReportPeriod.Last24 -> R.string.period_last_24
+ is ReportDates.ReportPeriod.Yesterday -> R.string.period_yesterday
+ is ReportDates.ReportPeriod.ThisWeek -> R.string.period_this_week
+ is ReportDates.ReportPeriod.Last7 -> R.string.period_last_7
+ is ReportDates.ReportPeriod.ThisMonth -> R.string.period_this_month
+ is ReportDates.ReportPeriod.Last30 -> R.string.period_last_30
+ is ReportDates.ReportPeriod.Custom -> R.string.period_custom
}
)
+
+ binding.rangeLayout.visibility =
+ if (period is ReportDates.ReportPeriod.Custom) View.VISIBLE else View.GONE
+
+ (period as? ReportDates.ReportPeriod.Custom)?.let {
+ val (from, to) = it.getStringDates()
+ binding.fromEditText.setText(Formatter.formatDate(from))
+ binding.toEditText.setText(Formatter.formatDate(to))
+ }
+
+ /**
+ * Initialize date and time pickers
+ */
+
+ val (from, to) = period.getObjectDates()
+
+ fromDatePicker = DatePickerDialog(requireContext(), { p0, p1, p2, p3 ->
+ fromPickedDatetime = LocalDateTime(p1, p2 + 1, p3, from.hour, from.minute)
+ fromTimePicker.show()
+ }, from.year, from.monthNumber, from.dayOfMonth)
+ fromDatePicker.datePicker.maxDate = Calendar.getInstance().timeInMillis
+
+ fromTimePicker = TimePickerDialog(requireContext(), { p0, p1, p2 ->
+ fromPickedDatetime = LocalDateTime(
+ fromPickedDatetime.year,
+ fromPickedDatetime.monthNumber,
+ fromPickedDatetime.dayOfMonth,
+ p1, p2
+ )
+ (period as? ReportDates.ReportPeriod.Custom)?.let {
+ unitReportsViewModel.setReportPeriod(it.withFrom(fromPickedDatetime))
+ }
+ }, from.hour, from.minute, false)
+
+ toDatePicker = DatePickerDialog(requireContext(), { p0, p1, p2, p3 ->
+ toPickedDatetime = LocalDateTime(p1, p2 + 1, p3, from.hour, from.minute)
+ toTimePicker.show()
+ }, to.year, to.monthNumber, to.dayOfMonth)
+ toDatePicker.datePicker.maxDate = Calendar.getInstance().timeInMillis
+
+ toTimePicker = TimePickerDialog(requireContext(), { p0, p1, p2 ->
+ toPickedDatetime = LocalDateTime(
+ toPickedDatetime.year,
+ toPickedDatetime.monthNumber,
+ toPickedDatetime.dayOfMonth,
+ p1, p2
+ )
+ (period as? ReportDates.ReportPeriod.Custom)?.let {
+ unitReportsViewModel.setReportPeriod(it.withTo(toPickedDatetime))
+ }
+ }, to.hour, to.minute, false)
+
}
unitReportsViewModel.geofences.observe(viewLifecycleOwner) { geofences ->
@@ -210,14 +313,15 @@ class UnitReportsFragment : Fragment() {
popOver.setOnMenuItemClickListener { item ->
unitReportsViewModel.setReportPeriod(
when (item.itemId) {
- R.id.optionToday -> ReportDates.ReportPeriod.TODAY
- R.id.optionLast24 -> ReportDates.ReportPeriod.LAST_24
- R.id.optionYesterday -> ReportDates.ReportPeriod.YESTERDAY
- R.id.optionWeek -> ReportDates.ReportPeriod.THIS_WEEK
- R.id.optionLast7 -> ReportDates.ReportPeriod.LAST_7
- R.id.optionMonth -> ReportDates.ReportPeriod.THIS_MONTH
- R.id.optionLast30 -> ReportDates.ReportPeriod.LAST_30
- else -> ReportDates.ReportPeriod.TODAY
+ R.id.optionToday -> ReportDates.ReportPeriod.Today()
+ R.id.optionLast24 -> ReportDates.ReportPeriod.Last24()
+ R.id.optionYesterday -> ReportDates.ReportPeriod.Yesterday()
+ R.id.optionWeek -> ReportDates.ReportPeriod.ThisWeek()
+ R.id.optionLast7 -> ReportDates.ReportPeriod.Last7()
+ R.id.optionMonth -> ReportDates.ReportPeriod.ThisMonth()
+ R.id.optionLast30 -> ReportDates.ReportPeriod.Last30()
+ R.id.optionCustom -> ReportDates.ReportPeriod.Custom()
+ else -> ReportDates.ReportPeriod.Today()
}
)
true
@@ -278,9 +382,10 @@ class UnitReportsFragment : Fragment() {
EventInformation.Type.MAINTENANCE -> R.string.event_maintenance
EventInformation.Type.TEXT_MESSAGE -> R.string.event_text_message
EventInformation.Type.DRIVER_CHANGED -> R.string.event_driver_changed
- EventInformation.Type.UNKNOWN -> R.string.event_unknown
+ EventInformation.Type.UNKNOWN -> R.string.event_unknown
else -> R.string.event_unknown
- })
+ }
+ )
}
event.geofence?.let {
geofenceText.text = it.name
diff --git a/androidApp/src/main/res/drawable/icon_down.xml b/androidApp/src/main/res/drawable/icon_down.xml
new file mode 100644
index 0000000..884bee1
--- /dev/null
+++ b/androidApp/src/main/res/drawable/icon_down.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M7.41,8.59L12,13.17l4.59,-4.58L18,10l-6,6 -6,-6 1.41,-1.41z"/>
+</vector>
diff --git a/androidApp/src/main/res/drawable/icon_up.xml b/androidApp/src/main/res/drawable/icon_up.xml
new file mode 100644
index 0000000..9b15755
--- /dev/null
+++ b/androidApp/src/main/res/drawable/icon_up.xml
@@ -0,0 +1,10 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M7.41,15.41L12,10.83l4.59,4.58L18,14l-6,-6 -6,6z"/>
+</vector>
diff --git a/androidApp/src/main/res/layout/unit_details_reports.xml b/androidApp/src/main/res/layout/unit_details_reports.xml
index 903c2c3..c29e4b0 100644
--- a/androidApp/src/main/res/layout/unit_details_reports.xml
+++ b/androidApp/src/main/res/layout/unit_details_reports.xml
@@ -1,194 +1,297 @@
<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout
+<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <androidx.fragment.app.FragmentContainerView
- android:id="@+id/reportsMapContainer"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginBottom="@dimen/margin"
- app:layout_constraintBottom_toTopOf="@id/periodSection"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent"
- android:visibility="gone"/>
-
- <ScrollView
- android:id="@+id/eventsScroll"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginBottom="@dimen/margin"
- android:visibility="gone"
- app:layout_constraintBottom_toTopOf="@id/periodSection"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="parent">
-
- <HorizontalScrollView
- android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="match_parent"
+ xmlns:tools="http://schemas.android.com/tools">
- <TableLayout
- android:id="@+id/eventsTable"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:isScrollContainer="true"
- android:scrollbars="vertical"
- android:stretchColumns="*">
+ <!-- Main content -->
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
- <TableRow android:background="@color/colorPrimary">
+ <androidx.fragment.app.FragmentContainerView
+ android:id="@+id/reportsMapContainer"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginBottom="@dimen/margin"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ android:visibility="gone"/>
+
+ <ScrollView
+ android:id="@+id/eventsScroll"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginBottom="@dimen/bottom_sheet_peak"
+ android:visibility="visible"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent">
+
+ <HorizontalScrollView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
- <TextView
- android:paddingHorizontal="@dimen/padding"
- android:text="@string/table_datetime"
- android:textColor="@color/background"
- android:padding="@dimen/padding" />
+ <TableLayout
+ android:id="@+id/eventsTable"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:isScrollContainer="true"
+ android:scrollbars="vertical"
+ android:stretchColumns="*">
- <TextView
- android:paddingHorizontal="@dimen/padding"
- android:text="@string/table_event"
- android:textColor="@color/background"
- android:padding="@dimen/padding" />
+ <TableRow android:background="@color/colorPrimary">
- <TextView
- android:paddingHorizontal="@dimen/padding"
- android:text="@string/table_geofence"
- android:textColor="@color/background"
- android:padding="@dimen/padding" />
+ <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_address"
- 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" />
- </TableRow>
+ <TextView
+ android:paddingHorizontal="@dimen/padding"
+ android:text="@string/table_geofence"
+ android:textColor="@color/background"
+ android:padding="@dimen/padding" />
- </TableLayout>
+ <TextView
+ android:paddingHorizontal="@dimen/padding"
+ android:text="@string/table_address"
+ android:textColor="@color/background"
+ android:padding="@dimen/padding" />
- </HorizontalScrollView>
+ </TableRow>
- </ScrollView>
+ </TableLayout>
- <LinearLayout
- android:id="@+id/periodSection"
+ </HorizontalScrollView>
+
+ </ScrollView>
+
+ <include
+ android:id="@+id/reportLoading"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ layout="@layout/loading_indicator"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:visibility="gone"/>
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+ <!-- Bottom sheet -->
+ <com.google.android.material.card.MaterialCardView
+ android:id="@+id/bottomSheet"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_margin="@dimen/margin"
+ app:contentPadding="@dimen/card_padding"
android:orientation="vertical"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent">
+ app:behavior_hideable="false"
+ app:behavior_peekHeight="@dimen/bottom_sheet_peak"
+ app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
+ style="@style/BottomSheetCardStyle">
<LinearLayout
+ android:id="@+id/periodSection"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:orientation="vertical">
- <TextView
- android:layout_width="0dp"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_vertical"
- android:text="@string/period"
- android:textColor="@color/colorPrimaryDark"
- android:textSize="22sp" />
-
- <com.google.android.material.button.MaterialButton
- android:id="@+id/periodButton"
- android:layout_width="wrap_content"
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/fields_spacing"
- android:text="@string/select_period"
- android:textColor="@color/colorPrimaryDark"
- app:backgroundTint="@color/darkBackground" />
+ android:layout_marginBottom="@dimen/margin"
+ android:orientation="horizontal">
- </LinearLayout>
+ <!-- Report type -->
+ <com.addisonelliott.segmentedbutton.SegmentedButtonGroup
+ android:id="@+id/reportType"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/fields_spacing"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/toggleSheet"
+ app:borderWidth="1dp"
+ app:dividerPadding="10dp"
+ app:dividerWidth="1dp"
+ app:position="0"
+ app:radius="30dp"
+ app:ripple="true"
+ app:selectedBackground="@color/colorPrimary">
- <com.addisonelliott.segmentedbutton.SegmentedButtonGroup
- android:id="@+id/reportType"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/margin"
- app:borderWidth="1dp"
- app:dividerPadding="10dp"
- app:dividerWidth="1dp"
- app:position="0"
- app:radius="30dp"
- app:ripple="true"
- app:selectedBackground="@color/colorPrimary">
-
- <com.addisonelliott.segmentedbutton.SegmentedButton
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:padding="10dp"
- app:drawableGravity="top"
- app:selectedTextColor="@color/darkBackground"
- app:text="@string/positions"
- app:textColor="@color/colorPrimaryDark" />
-
- <com.addisonelliott.segmentedbutton.SegmentedButton
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:padding="10dp"
- app:drawableGravity="top"
- app:selectedTextColor="@color/darkBackground"
- app:text="@string/events"
- app:textColor="@color/colorPrimaryDark" />
-
- <com.addisonelliott.segmentedbutton.SegmentedButton
- android:layout_width="0dp"
+ <com.addisonelliott.segmentedbutton.SegmentedButton
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:padding="10dp"
+ app:drawableGravity="top"
+ app:selectedTextColor="@color/darkBackground"
+ app:text="@string/positions"
+ app:textColor="@color/colorPrimaryDark" />
+
+ <com.addisonelliott.segmentedbutton.SegmentedButton
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:padding="10dp"
+ app:drawableGravity="top"
+ app:selectedTextColor="@color/darkBackground"
+ app:text="@string/events"
+ app:textColor="@color/colorPrimaryDark" />
+
+ <com.addisonelliott.segmentedbutton.SegmentedButton
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:padding="10dp"
+ app:drawableGravity="top"
+ app:selectedTextColor="@color/darkBackground"
+ app:text="@string/stops"
+ app:textColor="@color/colorPrimaryDark" />
+
+ </com.addisonelliott.segmentedbutton.SegmentedButtonGroup>
+
+ <ImageButton
+ android:id="@+id/toggleSheet"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:src="@drawable/icon_down"
+ android:contentDescription="@string/shared_slide_down"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:background="?selectableItemBackgroundBorderless" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+
+ <!-- Report period -->
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:padding="10dp"
- app:drawableGravity="top"
- app:selectedTextColor="@color/darkBackground"
- app:text="@string/stops"
- app:textColor="@color/colorPrimaryDark" />
+ android:orientation="horizontal">
- </com.addisonelliott.segmentedbutton.SegmentedButtonGroup>
+ <TextView
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_weight="1"
+ android:gravity="center_vertical"
+ android:text="@string/period"
+ android:textColor="@color/colorPrimaryDark"
+ android:textSize="22sp" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal"
- android:layout_marginTop="@dimen/margin">
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/periodButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/select_period"
+ android:textColor="@color/colorPrimaryDark"
+ app:backgroundTint="@color/darkBackground"
+ style="@style/Widget.AppCompat.Button.Small" />
+ </LinearLayout>
- <com.google.android.material.button.MaterialButton
- android:id="@+id/exportButton"
+ <!-- Custom period ranges -->
+ <LinearLayout
+ android:id="@+id/rangeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/export_report"
- android:layout_weight="1"
- android:layout_marginEnd="@dimen/fields_spacing"
- style="?android:buttonStyleSmall"/>
+ android:gravity="center_vertical"
+ android:weightSum="2"
+ android:orientation="horizontal">
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/fromInputLayout"
+ style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/fields_spacing"
+ android:hint="@string/period_from"
+ android:layout_weight="1">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/fromEditText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/datetime_field"
+ android:inputType="none"
+ android:clickable="false"
+ android:focusable="false"
+ tools:text="2022-04-11 00:00"
+ />
- <com.google.android.material.button.MaterialButton
- android:id="@+id/shareButton"
+ </com.google.android.material.textfield.TextInputLayout>
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/period_separator" />
+
+ <com.google.android.material.textfield.TextInputLayout
+ android:id="@+id/toInputLayout"
+ style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.Dense"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:hint="@string/period_to"
+ android:layout_marginStart="@dimen/fields_spacing"
+ android:layout_weight="1">
+
+ <com.google.android.material.textfield.TextInputEditText
+ android:id="@+id/toEditText"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:textSize="@dimen/datetime_field"
+ android:inputType="none"
+ android:clickable="false"
+ android:focusable="false"
+ tools:text="2022-04-11 00:39"
+ />
+
+ </com.google.android.material.textfield.TextInputLayout>
+
+ </LinearLayout>
+
+ <!-- Action buttons -->
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/share_report"
- style="?android:buttonStyleSmall"/>
+ android:orientation="horizontal"
+ android:layout_marginTop="@dimen/margin">
- </LinearLayout>
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/exportButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/export_report"
+ android:layout_weight="1"
+ android:layout_marginEnd="@dimen/fields_spacing"
+ style="?android:buttonStyleSmall"/>
+
+ <com.google.android.material.button.MaterialButton
+ android:id="@+id/shareButton"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:text="@string/share_report"
+ style="?android:buttonStyleSmall"/>
- </LinearLayout>
+ </LinearLayout>
+
+ </LinearLayout>
- <include
- android:id="@+id/reportLoading"
- android:layout_width="0dp"
- android:layout_height="0dp"
- layout="@layout/loading_indicator"
- app:layout_constraintTop_toTopOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toTopOf="@id/periodSection"
- android:visibility="gone"/>
+ </com.google.android.material.card.MaterialCardView>
-</androidx.constraintlayout.widget.ConstraintLayout> \ No newline at end of file
+</androidx.coordinatorlayout.widget.CoordinatorLayout> \ No newline at end of file
diff --git a/androidApp/src/main/res/menu/report_period_options.xml b/androidApp/src/main/res/menu/report_period_options.xml
index 60fb58c..a8fdbad 100644
--- a/androidApp/src/main/res/menu/report_period_options.xml
+++ b/androidApp/src/main/res/menu/report_period_options.xml
@@ -30,4 +30,8 @@
android:id="@+id/optionLast30"
android:title="@string/period_last_30" />
+ <item
+ android:id="@+id/optionCustom"
+ android:title="@string/period_custom" />
+
</menu> \ 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 eeb7149..26e0cad 100644
--- a/androidApp/src/main/res/values-es-rMX/strings.xml
+++ b/androidApp/src/main/res/values-es-rMX/strings.xml
@@ -6,6 +6,8 @@
<string name="shared_close">Cerrar</string>
<string name="shared_send">Enviar</string>
<string name="shared_loading">Cargando</string>
+ <string name="shared_slide_up">Deslizar hacia arriba</string>
+ <string name="shared_slide_down">Deslizar hacia abajo</string>
<!-- LoginActivity -->
<string name="login_username">Nombre de usuario</string>
@@ -113,6 +115,10 @@
<string name="period_last_7">Últimos 7d</string>
<string name="period_this_month">Mes</string>
<string name="period_last_30">Últimos 30d</string>
+ <string name="period_custom">Personalizado</string>
+ <string name="period_separator">&#8212;</string>
+ <string name="period_from">Inicio</string>
+ <string name="period_to">Fin</string>
<string name="table_event">Evento</string>
<string name="table_datetime">Fecha y hora</string>
diff --git a/androidApp/src/main/res/values/dimen.xml b/androidApp/src/main/res/values/dimen.xml
index 92d5242..8c8c5f5 100644
--- a/androidApp/src/main/res/values/dimen.xml
+++ b/androidApp/src/main/res/values/dimen.xml
@@ -35,6 +35,12 @@
<dimen name="report_label_width">10dp</dimen>
<dimen name="attribution_text_size">11sp</dimen>
+ <!-- Reports bottom sheet -->
+ <dimen name="bottom_sheet_peak">80dp</dimen>
+
+ <!-- Reports datetime field -->
+ <dimen name="datetime_field">12sp</dimen>
+
<!-- User Information -->
<dimen name="fields_spacing">8dp</dimen>
<dimen name="fields_large_spacing">16dp</dimen>
diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml
index 865368e..c349cd8 100644
--- a/androidApp/src/main/res/values/strings.xml
+++ b/androidApp/src/main/res/values/strings.xml
@@ -20,6 +20,8 @@
<string name="shared_close">Close</string>
<string name="shared_send">Send</string>
<string name="shared_loading">Loading</string>
+ <string name="shared_slide_up">Slide up</string>
+ <string name="shared_slide_down">Slide down</string>
<!-- LoginActivity -->
<string name="login_username">Username</string>
@@ -127,6 +129,10 @@
<string name="period_last_7">Last 7d</string>
<string name="period_this_month">Month</string>
<string name="period_last_30">Last 30d</string>
+ <string name="period_custom">Custom</string>
+ <string name="period_separator">&#8212;</string>
+ <string name="period_from">Start</string>
+ <string name="period_to">End</string>
<string name="table_event">Event</string>
<string name="table_datetime">Datetime</string>
diff --git a/androidApp/src/main/res/values/styles.xml b/androidApp/src/main/res/values/styles.xml
index dc40450..982d4dc 100644
--- a/androidApp/src/main/res/values/styles.xml
+++ b/androidApp/src/main/res/values/styles.xml
@@ -12,4 +12,17 @@
<item name="android:textStyle">bold</item>
</style>
+ <style name="BottomSheetCardStyle" parent="Widget.MaterialComponents.CardView">
+ <item name="shapeAppearanceOverlay">@style/ShapeAppearanceOverlay_BottomSheetCard</item>
+ <item name="cardElevation">@dimen/card_elevation</item>
+ </style>
+
+ <style name="ShapeAppearanceOverlay_BottomSheetCard">
+ <item name="cornerFamily">rounded</item>
+ <item name="cornerSizeTopRight">@dimen/card_border_radius</item>
+ <item name="cornerSizeTopLeft">@dimen/card_border_radius</item>
+ <item name="cornerSizeBottomRight">0dp</item>
+ <item name="cornerSizeBottomLeft">0dp</item>
+ </style>
+
</resources> \ No newline at end of file
diff --git a/iosApp/iosApp/Details/Reports/UnitReportsView.swift b/iosApp/iosApp/Details/Reports/UnitReportsView.swift
index e189835..7fd45a1 100644
--- a/iosApp/iosApp/Details/Reports/UnitReportsView.swift
+++ b/iosApp/iosApp/Details/Reports/UnitReportsView.swift
@@ -31,6 +31,9 @@ struct UnitReportsView: View {
self.unit = unit
self._unitReportsViewModel = StateObject(
wrappedValue: UnitReportsViewModel(deviceId: unit.device.id))
+ // Source: https://stackoverflow.com/a/63607411
+ UITableView.appearance().contentInset.top = -35
+ UITableView.appearance().contentInset.bottom = -25
}
private var eventsConfig: TablerListConfig<EventInformation> {
@@ -111,76 +114,76 @@ struct UnitReportsView: View {
isReport: true)
}
- VStack {
- HStack {
- Text("report-period")
- Spacer()
- Picker(selection: $unitReportsViewModel.reportPeriod) {
- Text("period-today").tag(ReportDates.ReportPeriod.today)
- Text("period-last-24").tag(ReportDates.ReportPeriod.last24)
- Text("period-yesterday").tag(ReportDates.ReportPeriod.yesterday)
- Text("period-this-week").tag(ReportDates.ReportPeriod.thisWeek)
- Text("period-last-7").tag(ReportDates.ReportPeriod.last7)
- Text("period-this-month").tag(ReportDates.ReportPeriod.thisMonth)
- Text("period-last-30").tag(ReportDates.ReportPeriod.last30)
- } label: {
- switch unitReportsViewModel.reportPeriod {
- case .today:
- Text("period-today")
- case .last24:
- Text("period-last-24")
- case .yesterday:
- Text("period-yesterday")
- case .thisWeek:
- Text("period-this-week")
- case .last7:
- Text("period-last-7")
- case .thisMonth:
- Text("period-this-month")
- case .last30:
- Text("period-last-30")
- default:
- Text("period-today")
+ // MARK: - Report type
+ Picker(selection: $unitReportsViewModel.reportType) {
+ Text("report-positions").tag(ReportController.ReportType.positions)
+ Text("report-events").tag(ReportController.ReportType.events)
+ Text("report-stops").tag(ReportController.ReportType.stops)
+ } label: {
+ EmptyView()
+ }
+ .pickerStyle(.segmented)
+ .padding()
+
+ Form {
+ // MARK: - Report period
+ Section {
+ HStack {
+ Text("report-period")
+ Spacer()
+ Picker(selection: $unitReportsViewModel.periodType, label: Text("report-period")) {
+ Text("period-today").tag(ReportDates.PeriodTypes.today)
+ Text("period-last-24").tag(ReportDates.PeriodTypes.last24)
+ Text("period-yesterday").tag(ReportDates.PeriodTypes.yesterday)
+ Text("period-this-week").tag(ReportDates.PeriodTypes.thisWeek)
+ Text("period-last-7").tag(ReportDates.PeriodTypes.last7)
+ Text("period-this-month").tag(ReportDates.PeriodTypes.thisMonth)
+ Text("period-last-30").tag(ReportDates.PeriodTypes.last30)
+ Text("period-custom").tag(ReportDates.PeriodTypes.custom)
}
+ .pickerStyle(.menu)
}
- }.padding()
- Picker(selection: $unitReportsViewModel.reportType) {
- Text("report-positions").tag(ReportController.ReportType.positions)
- Text("report-events").tag(ReportController.ReportType.events)
- Text("report-stops").tag(ReportController.ReportType.stops)
- } label: {
- EmptyView()
- }.pickerStyle(SegmentedPickerStyle())
-
- HStack {
- Group {
- Button {
- unitReportsViewModel.saveXlsxReport()
- } label: {
- Text("report-save")
+
+ if unitReportsViewModel.periodType == .custom {
+ DatePicker(selection: $unitReportsViewModel.fromDate, in: ...Date()) {
+ Text("period-from")
}
-
- Button {
- unitReportsViewModel.shareXlsxReport()
- } label: {
- Text("report-share")
- }
- }.frame(maxWidth: .infinity)
- }.padding()
- .sheet(isPresented: $unitReportsViewModel.showShareDialog) {
- ShareView(activityItems: $unitReportsViewModel.activityItems)
- }
- .fileExporter(isPresented: $unitReportsViewModel.showExportDialog,
- documents: [unitReportsViewModel.saveDocument],
- contentType: .xlsx) { result in
- switch result {
- case .success (let url):
- print ("Saved to \(url)")
- case .failure (let error):
- print (error.localizedDescription)
+
+ DatePicker(selection: $unitReportsViewModel.toDate, in: ...Date()) {
+ Text("period-to")
}
}
- }.padding()
+ }
+
+ // MARK: - Report actions
+ Section {
+ Button {
+ unitReportsViewModel.saveXlsxReport()
+ } label: {
+ Text("report-save")
+ }.frame(maxWidth: .infinity)
+
+ Button {
+ unitReportsViewModel.shareXlsxReport()
+ } label: {
+ Text("report-share")
+ }.frame(maxWidth: .infinity)
+ }
+ }
+ .frame(maxHeight: 150)
+ .sheet(isPresented: $unitReportsViewModel.showShareDialog) {
+ ShareView(activityItems: $unitReportsViewModel.activityItems)
+ }
+ .fileExporter(isPresented: $unitReportsViewModel.showExportDialog,
+ documents: [unitReportsViewModel.saveDocument],
+ contentType: .xlsx) { result in
+ switch result {
+ case .success (let url):
+ print ("Saved to \(url)")
+ case .failure (let error):
+ print (error.localizedDescription)
+ }
+ }
}.onAppear {
unitReportsViewModel.fetchReport()
}
diff --git a/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift b/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift
index e5b3e4b..450bfa7 100644
--- a/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift
+++ b/iosApp/iosApp/Details/Reports/UnitReportsViewModel.swift
@@ -29,12 +29,38 @@ class UnitReportsViewModel: ObservableObject {
}
@Published var deviceId: Int32? = nil
+
+ // MARK: - Report settings
@Published var reportType: ReportController.ReportType = .positions {
didSet {
fetchReport()
}
}
- @Published var reportPeriod: ReportDates.ReportPeriod = .today {
+ @Published var periodType: ReportDates.PeriodTypes = .today {
+ didSet {
+ switch periodType {
+ case .today:
+ reportPeriod = ReportDates.ReportPeriodToday()
+ case .last24:
+ reportPeriod = ReportDates.ReportPeriodLast24()
+ case .yesterday:
+ reportPeriod = ReportDates.ReportPeriodYesterday()
+ case .thisWeek:
+ reportPeriod = ReportDates.ReportPeriodThisWeek()
+ case .last7:
+ reportPeriod = ReportDates.ReportPeriodLast7()
+ case .thisMonth:
+ reportPeriod = ReportDates.ReportPeriodThisMonth()
+ case .last30:
+ reportPeriod = ReportDates.ReportPeriodLast30()
+ case .custom:
+ reportPeriod = ReportDates.ReportPeriodCustom(from: nil, to: nil)
+ default:
+ reportPeriod = ReportDates.ReportPeriodToday()
+ }
+ }
+ }
+ @Published var reportPeriod: ReportDates.ReportPeriod = ReportDates.ReportPeriodToday() {
didSet {
fetchReport()
}
@@ -67,7 +93,25 @@ class UnitReportsViewModel: ObservableObject {
@Published var markers = [Marker]()
@Published var selectedMarker: Marker? = nil
- // Geofences
+ @Published var fromDate: Date = Date() {
+ didSet {
+ if let custom = reportPeriod as? ReportDates.ReportPeriodCustom {
+ reportPeriod = custom.withFrom(
+ from: DateUtils.companion.iosDateToKotlin(date: fromDate))
+ }
+ }
+ }
+
+ @Published var toDate: Date = Calendar.current.startOfDay(for: Date()) {
+ didSet {
+ if let custom = reportPeriod as? ReportDates.ReportPeriodCustom {
+ reportPeriod = custom.withTo(
+ to: DateUtils.companion.iosDateToKotlin(date: toDate))
+ }
+ }
+ }
+
+ // MARK: - Geofences
@Published var geofences: [Int: Geofence] = [:] {
didSet {
flatGeofences = Array(geofences.values)
@@ -75,7 +119,7 @@ class UnitReportsViewModel: ObservableObject {
}
@Published var flatGeofences: [Geofence] = []
- // Save and share
+ // MARK: - Save and share
var xlsxAction: XlsxAction = .save
var saveDocument = XlsxFile()
@Published var showExportDialog: Bool = false
diff --git a/iosApp/iosApp/en.lproj/Localizable.strings b/iosApp/iosApp/en.lproj/Localizable.strings
index ca8e5a1..0959a1f 100644
--- a/iosApp/iosApp/en.lproj/Localizable.strings
+++ b/iosApp/iosApp/en.lproj/Localizable.strings
@@ -68,6 +68,9 @@
"period-last-7" = "Last 7d";
"period-this-month" = "Month";
"period-last-30" = "Last 30d";
+"period-custom" = "Custom";
+"period-from" = "Start";
+"period-to" = "End";
"events-table-event" = "Event";
"events-table-datetime" = "Datetime";
diff --git a/iosApp/iosApp/es-419.lproj/Localizable.strings b/iosApp/iosApp/es-419.lproj/Localizable.strings
index 9abd96a..52267cc 100644
--- a/iosApp/iosApp/es-419.lproj/Localizable.strings
+++ b/iosApp/iosApp/es-419.lproj/Localizable.strings
@@ -68,6 +68,9 @@
"period-last-7" = "Últimos 7d";
"period-this-month" = "Mes";
"period-last-30" = "Últimos 30d";
+"period-custom" = "Personalizado";
+"period-from" = "Inicio";
+"period-to" = "Fin";
"events-table-event" = "Evento";
"events-table-datetime" = "Fecha y hora";
diff --git a/shared/src/androidMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt b/shared/src/androidMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt
new file mode 100644
index 0000000..35f1ac6
--- /dev/null
+++ b/shared/src/androidMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt
@@ -0,0 +1,23 @@
+package mx.trackermap.TrackerMap.utils
+
+import android.os.Build
+import kotlinx.datetime.LocalDateTime
+import kotlinx.datetime.toJavaLocalDateTime
+import java.text.SimpleDateFormat
+import java.time.format.DateTimeFormatter
+import java.util.*
+
+actual class DateUtils {
+ actual companion object {
+ actual fun formatDate(date: LocalDateTime): String {
+ val javaDate = date.toJavaLocalDateTime()
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")
+ return javaDate.format(formatter)
+ } else {
+ val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault())
+ return formatter.format(date)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/ReportController.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/ReportController.kt
index 29c4229..bc0a48f 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/ReportController.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/controllers/ReportController.kt
@@ -60,18 +60,17 @@ class ReportController(
return
}
- val (currentDate, previousDate) = ReportDates.getDates(reportPeriod)
+ val (from, to) = reportPeriod.getStringDates()
if (!xlsx) {
reportFlow.value = Report.LoadingReport
}
- // GlobalScope.launch {
- when (reportType) {
- ReportType.POSITIONS -> fetchPositions(deviceId, previousDate, currentDate, xlsx)
- ReportType.EVENTS -> fetchEvents(deviceId, previousDate, currentDate, eventTypes, xlsx)
- ReportType.STOPS -> fetchStops(deviceId, previousDate, currentDate, xlsx)
- }
- // }
+
+ when (reportType) {
+ ReportType.POSITIONS -> fetchPositions(deviceId, from, to, xlsx)
+ ReportType.EVENTS -> fetchEvents(deviceId, from, to, eventTypes, xlsx)
+ ReportType.STOPS -> fetchStops(deviceId, from, to, xlsx)
+ }
}
private suspend fun fetchPositions(
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt
new file mode 100644
index 0000000..a85ae69
--- /dev/null
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt
@@ -0,0 +1,9 @@
+package mx.trackermap.TrackerMap.utils
+
+import kotlinx.datetime.LocalDateTime
+
+expect class DateUtils {
+ companion object {
+ fun formatDate(date: LocalDateTime): String
+ }
+} \ No newline at end of file
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/Formatter.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/Formatter.kt
index 77d0d14..bc7e622 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/Formatter.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/Formatter.kt
@@ -23,9 +23,8 @@ class Formatter {
companion object {
fun formatDate(str: String): String {
val timezone = TimeZone.currentSystemDefault()
- val date = str.toInstant().toLocalDateTime(timezone).toString()
- return date
- .replace('T', ' ').trim('Z')
+ val datetime = str.toInstant().toLocalDateTime(timezone)
+ return DateUtils.formatDate(datetime)
}
fun formatSpeed(speed: Double, unit: SpeedUnit): String {
diff --git a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/ReportDates.kt b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/ReportDates.kt
index 5672536..5298df3 100644
--- a/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/ReportDates.kt
+++ b/shared/src/commonMain/kotlin/mx/trackermap/TrackerMap/utils/ReportDates.kt
@@ -22,75 +22,120 @@ import kotlinx.datetime.*
@DelicateCoroutinesApi
class ReportDates {
- enum class ReportPeriod {
- TODAY, LAST_24, YESTERDAY, THIS_WEEK, LAST_7, THIS_MONTH, LAST_30
+ enum class PeriodTypes {
+ TODAY,
+ LAST_24,
+ YESTERDAY,
+ THIS_WEEK,
+ LAST_7,
+ THIS_MONTH,
+ LAST_30,
+ CUSTOM
}
- companion object {
- private fun formatDateTime(dateTime: LocalDateTime, timezone: TimeZone) =
+ sealed class ReportPeriod {
+ val timezone = TimeZone.currentSystemDefault()
+ val clock = Clock.System
+ val instant = clock.now()
+ val dateTime = instant.toLocalDateTime(timezone)
+ val date = dateTime.date
+
+ abstract fun getObjectDates(): Pair<LocalDateTime, LocalDateTime>
+
+ fun getStringDates(): Pair<String, String> {
+ val (from, to) = getObjectDates()
+ return formatDateTime(from) to formatDateTime(to)
+ }
+
+ fun formatDateTime(dateTime: LocalDateTime) =
dateTime.toInstant(timezone).toString()
- fun getDates(period: ReportPeriod): Pair<String, String> {
- val timezone = TimeZone.currentSystemDefault()
- val clock = Clock.System
- val instant = clock.now()
- val dateTime = instant.toLocalDateTime(timezone)
- val date = dateTime.date
+ class Today : ReportPeriod() {
+ override fun getObjectDates(): Pair<LocalDateTime, LocalDateTime> {
+ val from = date.atTime(0, 0)
+ val to = dateTime
+ return from to to
+ }
+ }
+
+ class Last24 : ReportPeriod() {
+ override fun getObjectDates(): Pair<LocalDateTime, LocalDateTime> {
+ val from = instant
+ .minus(1, DateTimeUnit.DAY, timezone)
+ .toLocalDateTime(timezone)
+ val to = dateTime
+ return from to to
+ }
+ }
+
+ class Yesterday : ReportPeriod() {
+ override fun getObjectDates(): Pair<LocalDateTime, LocalDateTime> {
+ val yesterday = instant
+ .minus(1, DateTimeUnit.DAY, timezone)
+ .toLocalDateTime(timezone).date
+ val from = yesterday.atTime(0, 0)
+ val to = yesterday.atTime(23, 59)
+ return from to to
+ }
+ }
+
+ class ThisWeek : ReportPeriod() {
+ override fun getObjectDates(): Pair<LocalDateTime, LocalDateTime> {
+ val from = instant
+ .minus(date.dayOfWeek.isoDayNumber - 1, DateTimeUnit.DAY, timezone)
+ .toLocalDateTime(timezone).date
+ .atTime(0, 0)
+ val to = dateTime
+ return from to to
+ }
+ }
+
+ class Last7 : ReportPeriod() {
+ override fun getObjectDates(): Pair<LocalDateTime, LocalDateTime> {
+ val from = instant
+ .minus(1, DateTimeUnit.WEEK, timezone)
+ .toLocalDateTime(timezone).date
+ .atTime(0, 0)
+ val to = dateTime
+ return from to to
+ }
+ }
+
+ class ThisMonth : ReportPeriod() {
+ override fun getObjectDates(): Pair<LocalDateTime, LocalDateTime> {
+ val from = instant
+ .minus(date.dayOfMonth - 1, DateTimeUnit.DAY, timezone)
+ .toLocalDateTime(timezone).date
+ .atTime(0, 0)
+ val to = dateTime
+ return from to to
+ }
+ }
+
+ class Last30 : ReportPeriod() {
+ override fun getObjectDates(): Pair<LocalDateTime, LocalDateTime> {
+ val from = instant
+ .minus(1, DateTimeUnit.MONTH, timezone)
+ .toLocalDateTime(timezone).date
+ .atTime(0, 0)
+ val to = dateTime
+ return from to to
+ }
+ }
- var currentDate: LocalDateTime? = null
- var previousDate: LocalDateTime? = null
- when (period) {
- ReportPeriod.TODAY -> {
- previousDate = date.atTime(0, 0)
- currentDate = dateTime
- }
- ReportPeriod.LAST_24 -> {
- previousDate = instant
- .minus(1, DateTimeUnit.DAY, timezone)
- .toLocalDateTime(timezone)
- currentDate = dateTime
- }
- ReportPeriod.YESTERDAY -> {
- val yesterday = instant
- .minus(1, DateTimeUnit.DAY, timezone)
- .toLocalDateTime(timezone).date
- previousDate = yesterday.atTime(0, 0)
- currentDate = yesterday.atTime(23, 59)
- }
- ReportPeriod.THIS_WEEK -> {
- previousDate = instant
- .minus(date.dayOfWeek.isoDayNumber - 1, DateTimeUnit.DAY, timezone)
- .toLocalDateTime(timezone).date
- .atTime(0, 0)
- currentDate = dateTime
- }
- ReportPeriod.LAST_7 -> {
- previousDate = instant
- .minus(1, DateTimeUnit.WEEK, timezone)
- .toLocalDateTime(timezone).date
- .atTime(0, 0)
- currentDate = dateTime
- }
- ReportPeriod.THIS_MONTH -> {
- previousDate = instant
- .minus(date.dayOfMonth - 1, DateTimeUnit.DAY, timezone)
- .toLocalDateTime(timezone).date
- .atTime(0, 0)
- currentDate = dateTime
- }
- ReportPeriod.LAST_30 -> {
- previousDate = instant
- .minus(1, DateTimeUnit.MONTH, timezone)
- .toLocalDateTime(timezone).date
- .atTime(0, 0)
- currentDate = dateTime
- }
+ class Custom (
+ private val from: LocalDateTime? = null,
+ private val to: LocalDateTime? = null
+ ) : ReportPeriod() {
+ override fun getObjectDates(): Pair<LocalDateTime, LocalDateTime> {
+ return Pair(
+ from ?: date.atTime(0, 0),
+ to ?: dateTime
+ )
}
- return Pair(
- formatDateTime(currentDate, timezone),
- formatDateTime(previousDate, timezone)
- )
+ fun withFrom (from: LocalDateTime): Custom = Custom (from, to)
+ fun withTo (to: LocalDateTime): Custom = Custom (from, to)
}
}
} \ No newline at end of file
diff --git a/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt
new file mode 100644
index 0000000..7286ea5
--- /dev/null
+++ b/shared/src/iosMain/kotlin/mx/trackermap/TrackerMap/utils/DateUtils.kt
@@ -0,0 +1,22 @@
+package mx.trackermap.TrackerMap.utils
+
+import kotlinx.datetime.*
+import platform.Foundation.NSDate
+import platform.Foundation.NSDateFormatter
+
+actual class DateUtils {
+ actual companion object {
+ fun iosDateToKotlin(date: NSDate): LocalDateTime {
+ val timezone = TimeZone.currentSystemDefault()
+ return date.toKotlinInstant().toLocalDateTime(timezone)
+ }
+
+ actual fun formatDate(date: LocalDateTime): String {
+ val timezone = TimeZone.currentSystemDefault()
+ val iosDate = date.toInstant(timezone).toNSDate()
+ val formatter = NSDateFormatter()
+ formatter.setDateFormat("yyyy-MM-dd HH:mm")
+ return formatter.stringFromDate(iosDate)
+ }
+ }
+} \ No newline at end of file