Skip to content

Commit

Permalink
[viewport] Align public API surface with iOS. (#1079)
Browse files Browse the repository at this point in the history
  • Loading branch information
pengdev committed Jan 26, 2022
1 parent 2dd268b commit 5e6bdc3
Show file tree
Hide file tree
Showing 24 changed files with 114 additions and 115 deletions.
25 changes: 12 additions & 13 deletions plugin-viewport/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ At any given time, the viewport is either:
- transitioning (camera is being managed by a ViewportTransition)

Two ViewportState are provided by default that user can create instance from the ViewportPlugin:
* `viewport.makeFollowingViewportState(options): FollowingViewportState`
* `viewport.makeFollowPuckViewportState(options): FollowPuckViewportState`
This state allows to follow user's location, and syncs the viewport position with the location puck provided by the LocationComponent.
* `viewport.makeOverviewViewportState(options): OverviewViewportState`
This state sets the viewport to cover user provided geometry with configurations.
Expand Down Expand Up @@ -50,18 +50,18 @@ allprojects {
}
// In the app build.gradle file
dependencies {
implementation 'com.mapbox.plugin:maps-viewport:10.3.0-beta.1'
implementation 'com.mapbox.plugin:maps-viewport:10.3.0-rc.1'
}
```

### Example

```kotlin
val viewportPlugin = mapView.viewport
val followingViewportState: FollowingViewportState = viewportPlugin.makeFollowingViewportState(
FollowingViewportStateOptions.Builder()
.bearing(FollowingViewportStateBearing.Constant(0.0))
.frameAnimationDurationMs(500)
val followPuckViewportState: FollowPuckViewportState = viewportPlugin.makeFollowPuckViewportState(
FollowPuckViewportStateOptions.Builder()
.bearing(FollowPuckViewportStateBearing.Constant(0.0))
.animationDurationMs(500)
.padding(EdgeInsets(200.0 * resources.displayMetrics.density, 0.0, 0.0, 0.0))
.build()
)
Expand All @@ -73,15 +73,14 @@ dependencies {
)
}
val immediateTransition = viewportPlugin.makeImmediateViewportTransition()
viewportPlugin.setTransition(immediateTransition, from = followingViewportState, to = overviewViewportState)
// transition from idle(the default state) to the created followingViewportState
viewportPlugin.transitionTo(followingViewportState) { isFinished ->
// the transtion has been completed with flag to check the finished status
// transition from idle(the default state) to the created followPuckViewportState with default transition
viewportPlugin.transitionTo(followPuckViewportState) { isFinished ->
// the transition has been completed with flag to check the finished status
}
...
// transition from followingViewportState to overviewViewportState with the customised immediate transiton
viewportPlugin.transitionTo(overviewViewportState) { isFinished ->
// the transtion has been completed with flag to check the finished status
// transition from followPuckViewportState to overviewViewportState with immediate transition
viewportPlugin.transitionTo(overviewViewportState, immediateTransition) { isFinished ->
// the transition has been completed with flag to check the finished status
}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ class ViewportPluginImpl(private val handler: Handler = Handler(Looper.getMainLo
completionBlockInvoked = true
if (isFinished) {
// transfer camera updating responsibility to targetState
currentCancelable = targetState.startUpdatingCamera()
targetState.startUpdatingCamera()
currentCancelable = Cancelable { targetState.stopUpdatingCamera() }
updateStatus(
ViewportStatus.State(targetState),
ViewportStatusChangeReason.TRANSITION_SUCCEEDED
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,17 +134,15 @@ internal class FollowPuckViewportStateImpl(
if (!locationComponent.enabled) {
Logger.w(
TAG,
"Location component is required to be enabled to use FollowingViewportState, otherwise there would be no FollowingViewportState updates or ViewportTransition updates towards the FollowingViewportState."
"Location component is required to be enabled to use FollowPuckViewportState, otherwise there would be no FollowPuckViewportState updates or ViewportTransition updates towards the FollowPuckViewportState."
)
}
}

/**
* Start updating the camera for the current [ViewportState].
*
* @return a handle that cancels the camera updates.
*/
override fun startUpdatingCamera(): Cancelable {
override fun startUpdatingCamera() {
checkLocationComponentEnablement()
addIndicatorListenerIfNeeded()
updateFrame(
Expand All @@ -154,11 +152,6 @@ internal class FollowPuckViewportStateImpl(
isFollowingStateRunning = true
}
}
return Cancelable {
isFollowingStateRunning = false
cancelAnimation()
removeIndicatorListenerIfNeeded()
}
}

/**
Expand Down Expand Up @@ -239,6 +232,6 @@ internal class FollowPuckViewportStateImpl(
}

private companion object {
const val TAG = "FollowingViewportStateImpl"
const val TAG = "FollowPuckViewportStateImpl"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,15 +80,9 @@ internal class OverviewViewportStateImpl(

/**
* Start updating the camera for the current [ViewportState].
*
* @return a handle that cancels the camera updates.
*/
override fun startUpdatingCamera(): Cancelable {
override fun startUpdatingCamera() {
isOverviewStateRunning = true
return Cancelable {
isOverviewStateRunning = false
cancelAnimation()
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ class ViewportPluginImplTest {
private val targetState = mockk<ViewportState>()
private val transition = mockk<ViewportTransition>()
private val transitionToCompletionListener = mockk<CompletionListener>()
private val stateUpdatingCancelable = mockk<Cancelable>()
private val transitionUpdateCancelable = mockk<Cancelable>()
private lateinit var viewportPlugin: ViewportPluginImpl

Expand All @@ -63,12 +62,12 @@ class ViewportPluginImplTest {
every { mapPluginProviderDelegate.camera } returns cameraPlugin
every { cameraPlugin.addCameraAnimationsLifecycleListener(any()) } just runs
every { cameraPlugin.removeCameraAnimationsLifecycleListener(any()) } just runs
every { targetState.startUpdatingCamera() } returns stateUpdatingCancelable
every { targetState.startUpdatingCamera() } just runs
every { targetState.stopUpdatingCamera() } just runs
every { transition.run(any(), any()) } returns transitionUpdateCancelable
every { transitionToCompletionListener.onComplete(any()) } just runs
every { handler.post(any()) } returns true
every { statusObserver.onViewportStatusChanged(any(), any(), any()) } just runs
every { stateUpdatingCancelable.cancel() } just runs
every { transitionUpdateCancelable.cancel() } just runs
viewportPlugin = ViewportPluginImpl(handler)
viewportPlugin.onDelegateProvider(delegateProvider)
Expand Down Expand Up @@ -331,7 +330,7 @@ class ViewportPluginImplTest {
}
}
verifyOrder {
stateUpdatingCancelable.cancel()
targetState.stopUpdatingCamera()
transition.run(newState, any())
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ import com.mapbox.maps.plugin.locationcomponent.OnIndicatorBearingChangedListene
import com.mapbox.maps.plugin.locationcomponent.OnIndicatorPositionChangedListener
import com.mapbox.maps.plugin.locationcomponent.location
import com.mapbox.maps.plugin.viewport.CAMERA_ANIMATIONS_UTILS
import com.mapbox.maps.plugin.viewport.DEFAULT_FOLLOW_VIEWPORT_STATE_PITCH
import com.mapbox.maps.plugin.viewport.DEFAULT_FOLLOW_VIEWPORT_STATE_ZOOM
import com.mapbox.maps.plugin.viewport.DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_PITCH
import com.mapbox.maps.plugin.viewport.DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_ZOOM
import com.mapbox.maps.plugin.viewport.LOCATION_COMPONENT_UTILS
import com.mapbox.maps.plugin.viewport.data.FollowPuckViewportStateBearing
import com.mapbox.maps.plugin.viewport.data.FollowPuckViewportStateOptions
Expand Down Expand Up @@ -109,8 +109,8 @@ class FollowPuckViewportStateImplTest {
cameraOptions {
bearing(testBearing)
center(testCenter)
pitch(DEFAULT_FOLLOW_VIEWPORT_STATE_PITCH)
zoom(DEFAULT_FOLLOW_VIEWPORT_STATE_ZOOM)
pitch(DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_PITCH)
zoom(DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_ZOOM)
padding(EdgeInsets(0.0, 0.0, 0.0, 0.0))
}
)
Expand Down Expand Up @@ -162,8 +162,8 @@ class FollowPuckViewportStateImplTest {
cameraOptions {
center(testCenter)
bearing(constantBearing)
zoom(DEFAULT_FOLLOW_VIEWPORT_STATE_ZOOM)
pitch(DEFAULT_FOLLOW_VIEWPORT_STATE_PITCH)
zoom(DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_ZOOM)
pitch(DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_PITCH)
padding(EdgeInsets(0.0, 0.0, 0.0, 0.0))
}
)
Expand Down Expand Up @@ -213,8 +213,8 @@ class FollowPuckViewportStateImplTest {
cameraOptions {
bearing(testBearing)
center(testCenter)
pitch(DEFAULT_FOLLOW_VIEWPORT_STATE_PITCH)
zoom(DEFAULT_FOLLOW_VIEWPORT_STATE_ZOOM)
pitch(DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_PITCH)
zoom(DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_ZOOM)
padding(EdgeInsets(0.0, 0.0, 0.0, 0.0))
}
)
Expand All @@ -228,8 +228,8 @@ class FollowPuckViewportStateImplTest {
cameraOptions {
bearing(testBearing)
center(testCenter)
pitch(DEFAULT_FOLLOW_VIEWPORT_STATE_PITCH)
zoom(DEFAULT_FOLLOW_VIEWPORT_STATE_ZOOM)
pitch(DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_PITCH)
zoom(DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_ZOOM)
padding(EdgeInsets(0.0, 0.0, 0.0, 0.0))
}
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class OverviewViewportStateImplTest {
every { animatorSet.cancel() } just runs

// test start updating camera
val cancelable = overviewState.startUpdatingCamera()
overviewState.startUpdatingCamera()
assertTrue(overviewState.isOverviewStateRunning)

// test updating overview state option to trigger an animation
Expand All @@ -181,7 +181,7 @@ class OverviewViewportStateImplTest {
}

// test stop updating camera
cancelable.cancel()
overviewState.stopUpdatingCamera()
assertFalse(overviewState.isOverviewStateRunning)
verify { animatorSet.cancel() }
verify { cameraPlugin.unregisterAnimators(animator) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import com.mapbox.maps.plugin.delegates.MapCameraManagerDelegate
import com.mapbox.maps.plugin.delegates.MapDelegateProvider
import com.mapbox.maps.plugin.delegates.MapPluginProviderDelegate
import com.mapbox.maps.plugin.viewport.CAMERA_ANIMATIONS_UTILS
import com.mapbox.maps.plugin.viewport.DEFAULT_FRAME_ANIMATION_DURATION_MS
import com.mapbox.maps.plugin.viewport.DEFAULT_STATE_TRANSITION_MAX_DURATION_MS
import com.mapbox.maps.plugin.viewport.DEFAULT_STATE_ANIMATION_DURATION_MS
import com.mapbox.maps.plugin.viewport.DEFAULT_TRANSITION_MAX_DURATION_MS
import com.mapbox.maps.plugin.viewport.TRANSITION_UTILS
import io.mockk.every
import io.mockk.mockk
Expand Down Expand Up @@ -70,7 +70,7 @@ class MapboxViewportTransitionFactoryTest {
} returns mockk()
transitionsFactory.transitionFromLowZoomToHighZoom(
cameraOptions,
DEFAULT_STATE_TRANSITION_MAX_DURATION_MS
DEFAULT_TRANSITION_MAX_DURATION_MS
)

assertEquals(-10.0, valueSlot.captured.targets.last(), EPS)
Expand All @@ -93,7 +93,7 @@ class MapboxViewportTransitionFactoryTest {
} returns mockk()
transitionsFactory.transitionFromHighZoomToLowZoom(
cameraOptions,
DEFAULT_STATE_TRANSITION_MAX_DURATION_MS
DEFAULT_TRANSITION_MAX_DURATION_MS
)

assertEquals(-10.0, valueSlot.captured.targets.last(), EPS)
Expand All @@ -113,7 +113,7 @@ class MapboxViewportTransitionFactoryTest {
every {
cameraPlugin.createBearingAnimator(capture(valueSlot), any(), any())
} returns mockk()
transitionsFactory.transitionLinear(cameraOptions, DEFAULT_FRAME_ANIMATION_DURATION_MS)
transitionsFactory.transitionLinear(cameraOptions, DEFAULT_STATE_ANIMATION_DURATION_MS)

assertEquals(-10.0, valueSlot.captured.targets.last(), EPS)
verify { normalizeBearing(10.0, 350.0) }
Expand All @@ -130,10 +130,10 @@ class MapboxViewportTransitionFactoryTest {
val animator =
transitionsFactory.transitionFromLowZoomToHighZoom(
TEST_CAMERA_OPTIONS,
DEFAULT_STATE_TRANSITION_MAX_DURATION_MS
DEFAULT_TRANSITION_MAX_DURATION_MS
)

verify { animatorSet.constrainDurationTo(DEFAULT_STATE_TRANSITION_MAX_DURATION_MS) }
verify { animatorSet.constrainDurationTo(DEFAULT_TRANSITION_MAX_DURATION_MS) }
assertEquals(constrainedSet, animator)
}

Expand All @@ -148,10 +148,10 @@ class MapboxViewportTransitionFactoryTest {
val animator =
transitionsFactory.transitionFromHighZoomToLowZoom(
TEST_CAMERA_OPTIONS,
DEFAULT_STATE_TRANSITION_MAX_DURATION_MS
DEFAULT_TRANSITION_MAX_DURATION_MS
)

verify { animatorSet.constrainDurationTo(DEFAULT_STATE_TRANSITION_MAX_DURATION_MS) }
verify { animatorSet.constrainDurationTo(DEFAULT_TRANSITION_MAX_DURATION_MS) }
assertEquals(constrainedSet, animator)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.mapbox.maps.plugin.viewport

import com.mapbox.maps.MapboxExperimental

/**
* A listener that's notified when the action is completed.
*/
@MapboxExperimental
fun interface CompletionListener {
/**
* Notifies the action is completed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,19 @@ package com.mapbox.maps.plugin.viewport
* The default maximum duration of the generated transitions set for the state transition options,
* including delays between animators and their respective durations.
*/
const val DEFAULT_STATE_TRANSITION_MAX_DURATION_MS = 3500L
const val DEFAULT_TRANSITION_MAX_DURATION_MS = 3500L

/**
* The default duration of the generated transitions set for the frame transition options.
*/
const val DEFAULT_FRAME_ANIMATION_DURATION_MS = 1000L
const val DEFAULT_STATE_ANIMATION_DURATION_MS = 1000L

/**
* The default pitch value for the follow viewport state.
* The default pitch value for the follow puck viewport state.
*/
const val DEFAULT_FOLLOW_VIEWPORT_STATE_PITCH = 45.0
const val DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_PITCH = 45.0

/**
* The default zoom value for the follow viewport state.
* The default zoom value for the follow puck viewport state.
*/
const val DEFAULT_FOLLOW_VIEWPORT_STATE_ZOOM = 16.35

/**
* Indicates that the [ViewportStatus] is changed due to programmatic reason.
*
* Used in the reason field of [ViewportStatusObserver]
*/
const val VIEWPORT_STATUS_OBSERVER_REASON_PROGRAMMATIC = "PROGRAMMATIC"

/**
* Indicates that the [ViewportStatus] is changed due to user interaction reason.
*
* Used in the reason field of [ViewportStatusObserver]
*/
const val VIEWPORT_STATUS_OBSERVER_REASON_USER_INTERACTION = "USER_INTERACTION"
const val DEFAULT_FOLLOW_PUCK_VIEWPORT_STATE_ZOOM = 16.35
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mapbox.maps.plugin.viewport

import com.mapbox.maps.MapboxExperimental
import com.mapbox.maps.plugin.viewport.state.ViewportState
import com.mapbox.maps.plugin.viewport.transition.ViewportTransition
import java.util.*
Expand All @@ -9,11 +10,10 @@ import java.util.*
*
* It could be either a [ViewportState] or [ViewportTransition].
*/
@MapboxExperimental
sealed class ViewportStatus {
/**
* Represents the current status is a [ViewportState].
*
* The state is null if current status is IDLE.
*/
class State(
/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package com.mapbox.maps.plugin.viewport

import com.mapbox.maps.MapboxExperimental
import com.mapbox.maps.plugin.viewport.data.ViewportStatusChangeReason

/**
* Observer that gets notified whenever [ViewportStatus] changes.
*/
@MapboxExperimental
fun interface ViewportStatusObserver {
/**
* Called whenever [ViewportStatus] changes.
*
* @param from The previous [ViewportStatus], null if the previous [ViewportStatus] is IDLE.
* @param to The current [ViewportStatus], null if the current [ViewportStatus] is IDLE.
* @param from The previous [ViewportStatus]
* @param to The current [ViewportStatus].
* @param reason The reason that the [ViewportStatus] has been changed.
*/
fun onViewportStatusChanged(from: ViewportStatus, to: ViewportStatus, reason: ViewportStatusChangeReason)
Expand Down
Loading

0 comments on commit 5e6bdc3

Please sign in to comment.