From 1c7593631ea1dc9c9b6b64754365fb78453d1fc7 Mon Sep 17 00:00:00 2001 From: rBergCode Date: Sat, 5 Oct 2024 12:42:45 +0200 Subject: [PATCH 1/2] feat(map): create the viewmodel for the list of hike routes Implemented the ListOfHikeRoutesViewModel to later retrieve the hike routes from the repository. --- .../model/map/ListOfHikeRoutesViewModel.kt | 51 +++++++++++++++++++ .../map/ListOfHikeRoutesViewModelTest.kt | 34 +++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 app/src/main/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModel.kt create mode 100644 app/src/test/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModelTest.kt diff --git a/app/src/main/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModel.kt b/app/src/main/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModel.kt new file mode 100644 index 000000000..cc2fa42ec --- /dev/null +++ b/app/src/main/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModel.kt @@ -0,0 +1,51 @@ +package ch.hikemate.app.model.map + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** + * ViewModel for the list of hike routes + * + * @param TODO: should take a repository as a parameter + */ +open class ListOfHikeRoutesViewModel() : ViewModel() { + // List of all routes in the database + private val hikeRoutes_ = + MutableStateFlow>(emptyList()) // TODO: should be a list of Route objects + val hikeRoutes: StateFlow> = hikeRoutes_.asStateFlow() + + // Selected route, i.e the route for the detail view + private val selectedHikeRoute_ = MutableStateFlow(null) // TODO: should be a Route object + open val selectedHikeRoute: StateFlow = selectedHikeRoute_.asStateFlow() + + // Creates a factory + companion object { + val Factory: ViewModelProvider.Factory = + object : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + return ListOfHikeRoutesViewModel() as T + } + } + } + + /** Gets all the routes from the database and updates the routes_ variable */ + fun getRoutes() { + // TODO: should call the repository to get all the routes + // repository.getRoutes(onSuccess = { routes_.value = it }, onFailure = {}) + + hikeRoutes_.value = listOf("Route 1", "Route 2", "Route 3") + } + + /** + * Selects a route to be displayed in the detail view + * + * @param hikeRoute The route to be displayed + */ + fun selectRoute(hikeRoute: String) { // TODO: should take a Route object + selectedHikeRoute_.value = hikeRoute + } +} diff --git a/app/src/test/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModelTest.kt b/app/src/test/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModelTest.kt new file mode 100644 index 000000000..464b7487a --- /dev/null +++ b/app/src/test/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModelTest.kt @@ -0,0 +1,34 @@ +package ch.hikemate.app.model.map + +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test + +/** Testing the ListOfRoutesViewModel class */ +class ListOfHikeRoutesViewModelTest { + private lateinit var listOfHikeRoutesViewModel: ListOfHikeRoutesViewModel + + @Before + fun setUp() { + listOfHikeRoutesViewModel = ListOfHikeRoutesViewModel() + } + + @Test + fun canBeCreatedAsFactory() { + val factory = ListOfHikeRoutesViewModel.Factory + val viewModel = factory.create(ListOfHikeRoutesViewModel::class.java) + assertNotNull(viewModel) + } + + @Test + fun canGetRoutes() { + listOfHikeRoutesViewModel.getRoutes() + assertNotEquals(listOfHikeRoutesViewModel.hikeRoutes.value.size, 0) + } + + @Test + fun canSelectRoute() { + listOfHikeRoutesViewModel.selectRoute("Route 1") + assertEquals(listOfHikeRoutesViewModel.selectedHikeRoute.value, "Route 1") + } +} From 28d6727a8fdabc2566c7bacfa6e100a06f816eb2 Mon Sep 17 00:00:00 2001 From: rBergCode Date: Sun, 6 Oct 2024 17:53:53 +0200 Subject: [PATCH 2/2] feat(map): add setArea and use of coroutines Added the setArea method to get the displayed portion of the map. Started to use co-routines for future API calls. --- app/build.gradle.kts | 3 +++ .../model/map/ListOfHikeRoutesViewModel.kt | 27 ++++++++++++++++--- .../map/ListOfHikeRoutesViewModelTest.kt | 11 ++++++++ gradle/libs.versions.toml | 1 + 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 324b4b84a..a4a8384e3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -154,6 +154,9 @@ dependencies { // ---------- FireBase ------------ implementation(platform(libs.firebase.bom)) + // ---------- OpenStreetMap ------------ + implementation(libs.osmdroid) + // Adds a remote binary dependency only for local tests. testImplementation(libs.junit) diff --git a/app/src/main/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModel.kt b/app/src/main/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModel.kt index cc2fa42ec..99849cd0d 100644 --- a/app/src/main/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModel.kt +++ b/app/src/main/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModel.kt @@ -2,9 +2,14 @@ package ch.hikemate.app.model.map import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.osmdroid.util.BoundingBox /** * ViewModel for the list of hike routes @@ -21,6 +26,8 @@ open class ListOfHikeRoutesViewModel() : ViewModel() { private val selectedHikeRoute_ = MutableStateFlow(null) // TODO: should be a Route object open val selectedHikeRoute: StateFlow = selectedHikeRoute_.asStateFlow() + private val area_ = MutableStateFlow(null) + // Creates a factory companion object { val Factory: ViewModelProvider.Factory = @@ -32,12 +39,26 @@ open class ListOfHikeRoutesViewModel() : ViewModel() { } } + private suspend fun getRoutesAsync() { + // TODO: Should call the API repository to get all the routes filtered by the area + withContext(Dispatchers.IO) { Thread.sleep(100) } + hikeRoutes_.value = listOf("Route 1", "Route 2", "Route 3") + } + /** Gets all the routes from the database and updates the routes_ variable */ fun getRoutes() { - // TODO: should call the repository to get all the routes - // repository.getRoutes(onSuccess = { routes_.value = it }, onFailure = {}) + viewModelScope.launch(Dispatchers.IO) { getRoutesAsync() } + } - hikeRoutes_.value = listOf("Route 1", "Route 2", "Route 3") + /** + * Sets the current displayed area on the map and updates the list of routes displayed in the + * list. + * + * @param area The area to be displayed + */ + fun setArea(area: BoundingBox) { + area_.value = area + getRoutes() } /** diff --git a/app/src/test/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModelTest.kt b/app/src/test/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModelTest.kt index 464b7487a..a7e1b2931 100644 --- a/app/src/test/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModelTest.kt +++ b/app/src/test/java/ch/hikemate/app/model/map/ListOfHikeRoutesViewModelTest.kt @@ -3,6 +3,7 @@ package ch.hikemate.app.model.map import org.junit.Assert.* import org.junit.Before import org.junit.Test +import org.osmdroid.util.BoundingBox /** Testing the ListOfRoutesViewModel class */ class ListOfHikeRoutesViewModelTest { @@ -23,6 +24,8 @@ class ListOfHikeRoutesViewModelTest { @Test fun canGetRoutes() { listOfHikeRoutesViewModel.getRoutes() + // Wait for the coroutine to finish + Thread.sleep(500) assertNotEquals(listOfHikeRoutesViewModel.hikeRoutes.value.size, 0) } @@ -31,4 +34,12 @@ class ListOfHikeRoutesViewModelTest { listOfHikeRoutesViewModel.selectRoute("Route 1") assertEquals(listOfHikeRoutesViewModel.selectedHikeRoute.value, "Route 1") } + + @Test + fun canSetArea() { + listOfHikeRoutesViewModel.setArea(BoundingBox(0.0, 0.0, 0.0, 0.0)) + // Wait for the coroutine to finish + Thread.sleep(500) + assertNotEquals(listOfHikeRoutesViewModel.hikeRoutes.value.size, 0) + } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 1104c7f3a..dbc4e5980 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -53,6 +53,7 @@ kaspresso = { group = "com.kaspersky.android-components", name = "kaspresso", ve kaspresso-compose = { group = "com.kaspersky.android-components", name = "kaspresso-compose-support", version.ref = "kaspresso" } robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } +osmdroid = { module = "org.osmdroid:osmdroid-android", version = "6.1.14" } # Firebase Libraries firebase-auth = { module = "com.google.firebase:firebase-auth", version.ref = "firebaseAuth" }