Skip to content

Commit 93bdc60

Browse files
committed
Remove location permission from starter options
1 parent add2cd4 commit 93bdc60

File tree

7 files changed

+123
-42
lines changed

7 files changed

+123
-42
lines changed

libnavigation-core/src/main/java/com/mapbox/navigation/core/trip/MapboxTripStarter.kt

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ import kotlinx.coroutines.SupervisorJob
1717
import kotlinx.coroutines.cancel
1818
import kotlinx.coroutines.flow.MutableStateFlow
1919
import kotlinx.coroutines.flow.collect
20+
import kotlinx.coroutines.flow.combine
2021
import kotlinx.coroutines.flow.update
2122
import kotlinx.coroutines.launch
23+
import kotlin.jvm.Throws
2224

2325
/**
2426
* This makes it simpler to start a MapboxNavigation trip session. It can be used to enable replay
@@ -33,6 +35,7 @@ import kotlinx.coroutines.launch
3335
class MapboxTripStarter internal constructor() : MapboxNavigationObserver {
3436

3537
private val optionsFlow = MutableStateFlow(MapboxTripStarterOptions.Builder().build())
38+
private val stateFlow = MutableStateFlow(MapboxTripStarterState())
3639
private var replayRouteTripSession: ReplayRouteSession? = null
3740
private var mapboxNavigation: MapboxNavigation? = null
3841

@@ -53,12 +56,12 @@ class MapboxTripStarter internal constructor() : MapboxNavigationObserver {
5356
// Initialize the options to be aware of the location permissions
5457
val context = mapboxNavigation.navigationOptions.applicationContext
5558
val granted = PermissionsManager.areLocationPermissionsGranted(context)
56-
optionsFlow.update { it.toBuilder().isLocationPermissionGranted(granted).build() }
59+
stateFlow.update { it.copy(isLocationPermissionGranted = granted) }
5760

5861
// Observe any changes to the options
5962
coroutineScope.launch {
60-
optionsFlow.collect { options ->
61-
onStarterOptionsChanged(mapboxNavigation, options)
63+
combine(optionsFlow, stateFlow, ::Pair).collect {
64+
onStarterOptionsChanged(mapboxNavigation, it.first, it.second)
6265
}
6366
}
6467
}
@@ -85,33 +88,37 @@ class MapboxTripStarter internal constructor() : MapboxNavigationObserver {
8588
this.optionsFlow.value = options
8689
}
8790

88-
private fun onStarterOptionsChanged(
89-
mapboxNavigation: MapboxNavigation,
90-
options: MapboxTripStarterOptions
91-
) {
92-
checkOptions(options)
93-
if (options.tripType == MAPBOX_TRIP_STARTER_REPLAY_ROUTE) {
94-
onReplayTripEnabled(mapboxNavigation)
95-
} else if (options.tripType == MAPBOX_TRIP_STARTER_FOLLOW_DEVICE &&
96-
options.isLocationPermissionGranted
97-
) {
98-
onTripSessionEnabled(mapboxNavigation)
99-
} else {
100-
onTripDisabled(mapboxNavigation)
101-
}
91+
/**
92+
* Get the location permission state. This may not reflect the actual permissions. Used for
93+
* verifying the tests and will likely be removed.
94+
*/
95+
@VisibleForTesting
96+
internal fun getLocationPermissionGranted(): Boolean =
97+
stateFlow.value.isLocationPermissionGranted
98+
99+
/**
100+
* Set the location permission state. [MAPBOX_TRIP_STARTER_FOLLOW_DEVICE] will not work unless
101+
* location permissions have been granted. If the location permissions are not granted and
102+
* this is set to true, an error will be thrown.
103+
*/
104+
@Throws(IllegalStateException::class)
105+
fun setLocationPermissionGranted(granted: Boolean) {
106+
val nextState = stateFlow.value.copy(isLocationPermissionGranted = granted)
107+
checkState(nextState)
108+
stateFlow.value = nextState
102109
}
103110

104111
/**
105112
* Throws an error if location permissions are set to true but the location permissions are
106113
* not actually granted.
107114
*/
108-
private fun checkOptions(options: MapboxTripStarterOptions) {
115+
private fun checkState(state: MapboxTripStarterState) {
109116
val mapboxNavigation = this.mapboxNavigation
110117
checkNotNull(mapboxNavigation) {
111118
"MapboxTripStarter cannot be used while MapboxNavigation is detached."
112119
}
113120
val context = mapboxNavigation.navigationOptions.applicationContext
114-
val granted = options.isLocationPermissionGranted
121+
val granted = state.isLocationPermissionGranted
115122
if (granted && !PermissionsManager.areLocationPermissionsGranted(context)) {
116123
error(
117124
"updateLocationPermissions can only be set to true when location permissions" +
@@ -120,6 +127,22 @@ class MapboxTripStarter internal constructor() : MapboxNavigationObserver {
120127
}
121128
}
122129

130+
private fun onStarterOptionsChanged(
131+
mapboxNavigation: MapboxNavigation,
132+
options: MapboxTripStarterOptions,
133+
state: MapboxTripStarterState,
134+
) {
135+
if (options.tripType == MAPBOX_TRIP_STARTER_REPLAY_ROUTE) {
136+
onReplayTripEnabled(mapboxNavigation)
137+
} else if (options.tripType == MAPBOX_TRIP_STARTER_FOLLOW_DEVICE &&
138+
state.isLocationPermissionGranted
139+
) {
140+
onTripSessionEnabled(mapboxNavigation)
141+
} else {
142+
onTripDisabled(mapboxNavigation)
143+
}
144+
}
145+
123146
private fun onReplayTripEnabled(mapboxNavigation: MapboxNavigation) {
124147
if (mapboxNavigation.getTripSessionState() != TripSessionState.STARTED ||
125148
!mapboxNavigation.isReplayEnabled()

libnavigation-core/src/main/java/com/mapbox/navigation/core/trip/MapboxTripStarterOptions.kt

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,62 @@ import com.mapbox.navigation.core.replay.route.ReplayRouteSessionOptions
77
* Defines options for the automatic trip starter [MapboxTripStarter].
88
*
99
* @param tripType Specify the type of trip to start.
10-
* @param isLocationPermissionGranted True if location permissions are granted.
1110
* @param replayRouteSessionOptions options to use when isReplayRouteEnabled is true
1211
*/
1312
@ExperimentalPreviewMapboxNavigationAPI
1413
class MapboxTripStarterOptions private constructor(
1514
@MapboxTripStarterExtra.Type
1615
val tripType: String,
17-
val isLocationPermissionGranted: Boolean,
1816
val replayRouteSessionOptions: ReplayRouteSessionOptions,
1917
) {
2018
/**
2119
* @return builder matching the one used to create this instance
2220
*/
2321
fun toBuilder(): Builder = Builder()
2422
.tripType(tripType)
25-
.isLocationPermissionGranted(isLocationPermissionGranted)
2623
.replayRouteSessionOptions(replayRouteSessionOptions)
2724

25+
/**
26+
* Regenerate whenever a change is made
27+
*/
28+
override fun toString(): String {
29+
return "MapboxTripStarterOptions(" +
30+
"tripType='$tripType', " +
31+
"replayRouteSessionOptions=$replayRouteSessionOptions" +
32+
")"
33+
}
34+
35+
/**
36+
* Regenerate whenever a change is made
37+
*/
38+
override fun equals(other: Any?): Boolean {
39+
if (this === other) return true
40+
if (javaClass != other?.javaClass) return false
41+
42+
other as MapboxTripStarterOptions
43+
44+
if (tripType != other.tripType) return false
45+
if (replayRouteSessionOptions != other.replayRouteSessionOptions) return false
46+
47+
return true
48+
}
49+
50+
/**
51+
* Regenerate whenever a change is made
52+
*/
53+
override fun hashCode(): Int {
54+
var result = tripType.hashCode()
55+
result = 31 * result + replayRouteSessionOptions.hashCode()
56+
return result
57+
}
58+
2859
/**
2960
* Build your [MapboxTripStarterOptions].
3061
*/
3162
class Builder {
3263

3364
@MapboxTripStarterExtra.Type
3465
private var tripType: String = MapboxTripStarterExtra.MAPBOX_TRIP_STARTER_FOLLOW_DEVICE
35-
private var isLocationPermissionGranted = false
3666
private var replayRouteSessionOptions: ReplayRouteSessionOptions? = null
3767

3868
/**
@@ -42,13 +72,6 @@ class MapboxTripStarterOptions private constructor(
4272
this.tripType = tripType
4373
}
4474

45-
/**
46-
* True if location permissions are granted.
47-
*/
48-
fun isLocationPermissionGranted(isLocationPermissionGranted: Boolean) = apply {
49-
this.isLocationPermissionGranted = isLocationPermissionGranted
50-
}
51-
5275
/**
5376
* True if navigation routes will be simulated.
5477
*/
@@ -62,7 +85,6 @@ class MapboxTripStarterOptions private constructor(
6285
fun build(): MapboxTripStarterOptions {
6386
return MapboxTripStarterOptions(
6487
tripType = tripType,
65-
isLocationPermissionGranted = isLocationPermissionGranted,
6688
replayRouteSessionOptions = replayRouteSessionOptions
6789
?: ReplayRouteSessionOptions.Builder().build()
6890
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.mapbox.navigation.core.trip
2+
3+
/**
4+
* State used by [MapboxTripStarter]. This class is internal because location permissions is the
5+
* only property and it may be removed in favor of better location permission core classes.
6+
*
7+
* @param isLocationPermissionGranted True if location permissions are granted.
8+
*/
9+
internal data class MapboxTripStarterState(
10+
val isLocationPermissionGranted: Boolean = false
11+
)
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package com.mapbox.navigation.core.trip
2+
3+
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
4+
import com.mapbox.navigation.testing.BuilderTest
5+
import io.mockk.mockk
6+
7+
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
8+
class MapboxTripStarterOptionsTest :
9+
BuilderTest<MapboxTripStarterOptions, MapboxTripStarterOptions.Builder>() {
10+
override fun getImplementationClass() = MapboxTripStarterOptions::class
11+
12+
override fun getFilledUpBuilder() = MapboxTripStarterOptions.Builder()
13+
.tripType("Not a real type")
14+
.replayRouteSessionOptions(mockk())
15+
16+
override fun trigger() {
17+
// only used to trigger JUnit4 to run this class if all test cases come from the parent
18+
}
19+
}

libnavigation-core/src/test/java/com/mapbox/navigation/core/trip/MapboxTripStarterTest.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,11 +39,11 @@ class MapboxTripStarterTest {
3939
fun `onAttached will update location permission options`() {
4040
every { PermissionsManager.areLocationPermissionsGranted(any()) } returns true
4141

42-
val initialOptions = sut.getOptions()
42+
val initialState = sut.getLocationPermissionGranted()
4343
sut.onAttached(mockk(relaxed = true))
4444

45-
assertFalse(initialOptions.isLocationPermissionGranted)
46-
assertTrue(sut.getOptions().isLocationPermissionGranted)
45+
assertFalse(initialState)
46+
assertTrue(sut.getLocationPermissionGranted())
4747
}
4848

4949
@Test
@@ -73,7 +73,7 @@ class MapboxTripStarterTest {
7373
val mapboxNavigation = mockMapboxNavigation()
7474
sut.onAttached(mapboxNavigation)
7575
every { PermissionsManager.areLocationPermissionsGranted(any()) } returns true
76-
sut.update { it.isLocationPermissionGranted(true) }
76+
sut.setLocationPermissionGranted(true)
7777

7878
verify(exactly = 1) { mapboxNavigation.startTripSession() }
7979
}
@@ -84,7 +84,7 @@ class MapboxTripStarterTest {
8484

8585
val mapboxNavigation = mockMapboxNavigation()
8686
sut.onAttached(mapboxNavigation)
87-
sut.update { it.isLocationPermissionGranted(true) }
87+
sut.setLocationPermissionGranted(true)
8888

8989
// Clean up coroutines early so that the error is caught by the tests.
9090
assertThrows(Exception::class.java) {
@@ -112,7 +112,7 @@ class MapboxTripStarterTest {
112112
sut.onAttached(mapboxNavigation)
113113
sut.update { it.tripType(MapboxTripStarterExtra.MAPBOX_TRIP_STARTER_REPLAY_ROUTE) }
114114
every { PermissionsManager.areLocationPermissionsGranted(any()) } returns true
115-
sut.update { it.isLocationPermissionGranted(true) }
115+
sut.setLocationPermissionGranted(true)
116116

117117
verify(exactly = 1) { mapboxNavigation.startReplayTripSession() }
118118
}

libnavui-app/src/main/java/com/mapbox/navigation/ui/app/internal/controller/TripSessionStarterStateController.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,7 @@ class TripSessionStarterStateController(store: Store) : StateController() {
3535
) {
3636
when (action) {
3737
is TripSessionStarterAction.OnLocationPermission -> {
38-
tripStarter.update {
39-
it.isLocationPermissionGranted(action.granted)
40-
}
38+
tripStarter.setLocationPermissionGranted(action.granted)
4139
}
4240
TripSessionStarterAction.EnableReplayTripSession -> {
4341
tripStarter.update {

libnavui-app/src/test/java/com/mapbox/navigation/ui/app/internal/controller/TripSessionStarterStateControllerTest.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import com.mapbox.navigation.core.trip.MapboxTripStarterExtra.MAPBOX_TRIP_STARTE
66
import com.mapbox.navigation.core.trip.MapboxTripStarterExtra.MAPBOX_TRIP_STARTER_REPLAY_ROUTE
77
import com.mapbox.navigation.core.trip.MapboxTripStarterOptions
88
import com.mapbox.navigation.testing.MainCoroutineRule
9+
import com.mapbox.navigation.ui.app.internal.Store
910
import com.mapbox.navigation.ui.app.internal.tripsession.TripSessionStarterAction
1011
import io.mockk.every
1112
import io.mockk.mockk
1213
import io.mockk.mockkObject
1314
import io.mockk.unmockkAll
15+
import io.mockk.verify
1416
import kotlinx.coroutines.ExperimentalCoroutinesApi
1517
import org.junit.After
1618
import org.junit.Assert.assertEquals
@@ -27,6 +29,7 @@ class TripSessionStarterStateControllerTest {
2729

2830
private lateinit var mapboxTripStarter: MapboxTripStarter
2931
private lateinit var sut: TripSessionStarterStateController
32+
private val store: Store = mockk(relaxed = true)
3033
private val updateSlot = mutableListOf<MapboxTripStarterOptions>()
3134

3235
@Before
@@ -37,19 +40,24 @@ class TripSessionStarterStateControllerTest {
3740

3841
mockkObject(MapboxTripStarter.Companion)
3942
every { MapboxTripStarter.getRegisteredInstance() } returns mapboxTripStarter
40-
sut = TripSessionStarterStateController()
43+
sut = TripSessionStarterStateController(store)
4144
}
4245

4346
@After
4447
fun teardown() {
4548
unmockkAll()
4649
}
4750

51+
@Test
52+
fun `constructor will register to the store`() {
53+
verify(exactly = 1) { store.register(sut) }
54+
}
55+
4856
@Test
4957
fun `on OnLocationPermission action should update TripSessionStarterState`() {
5058
sut.process(mockk(), TripSessionStarterAction.OnLocationPermission(true))
5159

52-
assertTrue(updateSlot[0].isLocationPermissionGranted)
60+
verify(exactly = 1) { mapboxTripStarter.setLocationPermissionGranted(true) }
5361
}
5462

5563
@Test

0 commit comments

Comments
 (0)