Skip to content

Commit

Permalink
Create a delegate for MapboxNavigation
Browse files Browse the repository at this point in the history
  • Loading branch information
kmadsen committed Sep 6, 2022
1 parent 7b80368 commit 6234305
Show file tree
Hide file tree
Showing 8 changed files with 616 additions and 112 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Mapbox welcomes participation and contributions from everyone.

## Unreleased
#### Features
- Added `requireMapboxNavigation` to offer a simple way to use `MapboxNavigationApp`. [#6233](https://github.com/mapbox/mapbox-navigation-android/pull/6233)
#### Bug fixes and improvements

## Mapbox Navigation SDK 2.8.0-beta.2 - 01 September, 2022
Expand Down
4 changes: 4 additions & 0 deletions libnavigation-core/api/current.txt
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,10 @@ package com.mapbox.navigation.core.lifecycle {
method public com.mapbox.navigation.base.options.NavigationOptions createNavigationOptions();
}

public final class RequireMapboxNavigation {
method @com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI public static kotlin.properties.ReadOnlyProperty<androidx.lifecycle.LifecycleOwner,com.mapbox.navigation.core.MapboxNavigation> requireMapboxNavigation(com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver? onCreatedObserver = null, com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver? onStartedObserver = null, com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver? onResumedObserver = null, kotlin.jvm.functions.Function0<kotlin.Unit>? onInitialize = null);
}

}

package com.mapbox.navigation.core.navigator {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
@file:JvmName("RequireMapboxNavigation")

package com.mapbox.navigation.core.lifecycle

import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
import com.mapbox.navigation.core.MapboxNavigation
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

/**
* Extension function to make it simple to create the [RequireMapboxNavigationDelegate]. Below are
* a couple examples of how you may use the delegate.
*
* Default can be used when [MapboxNavigationApp] is setup elsewhere.
* ```
* val mapboxNavigation by requireMapboxNavigation()
* ```
*
* Initialize the [MapboxNavigationApp] when you are ready to use it
* ```
* val mapboxNavigation by requireMapboxNavigation {
* MapboxNavigationApp.setup(..)
* }
* ```
*
* Register subscriptions and setup MapboxNavigationApp
* ```
* private val mapboxNavigation by requireMapboxNavigation(
* onResumedObserver = object : MapboxNavigationObserver {
* override fun onAttached(mapboxNavigation: MapboxNavigation) {
* mapboxNavigation.registerLocationObserver(locationObserver)
* mapboxNavigation.registerRoutesObserver(routesObserver)
* }
* override fun onDetached(mapboxNavigation: MapboxNavigation) {
* mapboxNavigation.unregisterLocationObserver(locationObserver)
* mapboxNavigation.unregisterRoutesObserver(routesObserver)
* }
* }
* ) {
* MapboxNavigationApp.setup(
* NavigationOptions.Builder(this)
* .accessToken(accessToken)
* .build()
* )
* }
* ```
*
* @see [RequireMapboxNavigationDelegate] for more details.
*/
@ExperimentalPreviewMapboxNavigationAPI
fun requireMapboxNavigation(
onCreatedObserver: MapboxNavigationObserver? = null,
onStartedObserver: MapboxNavigationObserver? = null,
onResumedObserver: MapboxNavigationObserver? = null,
onInitialize: (() -> Unit)? = null,
): ReadOnlyProperty<LifecycleOwner, MapboxNavigation> = RequireMapboxNavigationDelegate(
onCreatedObserver = onCreatedObserver,
onStartedObserver = onStartedObserver,
onResumedObserver = onResumedObserver,
onInitialize = onInitialize
)

/**
* Attaches a [LifecycleOwner] to [MapboxNavigationApp] and provides access to [MapboxNavigation].
*
* You can choose to call [MapboxNavigationApp.setup] in the [onInitialize]. You can also setup in
* the onCreate calls, or any call that happens before this delegate is accessed. The delegate will
* crash if accessed when the app is not setup or an attached lifecycle has not been created.
*
* You can use the observers parameter to setup any subscriptions. This is important because the
* [MapboxNavigation] instance can be re-created with [MapboxNavigationApp.disable], or if all
* [MapboxNavigationApp.attach] lifecycles are destroyed.
*
* @param onCreatedObserver registered to the [Lifecycle.State.CREATED] lifecycle
* @param onStartedObserver registered to the [Lifecycle.State.STARTED] lifecycle
* @param onResumedObserver registered to the [Lifecycle.State.RESUMED] lifecycle
* @param onInitialize called when the property is read for the first time
*/
internal class RequireMapboxNavigationDelegate(
private val onCreatedObserver: MapboxNavigationObserver? = null,
private val onStartedObserver: MapboxNavigationObserver? = null,
private val onResumedObserver: MapboxNavigationObserver? = null,
private val onInitialize: (() -> Unit)? = null
) : ReadOnlyProperty<LifecycleOwner, MapboxNavigation> {

private lateinit var lifecycleOwner: LifecycleOwner

private val lifecycleObserver = object : DefaultLifecycleObserver {
override fun onCreate(owner: LifecycleOwner) {
onCreatedObserver?.let { MapboxNavigationApp.registerObserver(it) }
}

override fun onDestroy(owner: LifecycleOwner) {
onCreatedObserver?.let { MapboxNavigationApp.unregisterObserver(it) }
}

override fun onStart(owner: LifecycleOwner) {
onStartedObserver?.let { MapboxNavigationApp.registerObserver(it) }
}

override fun onStop(owner: LifecycleOwner) {
onStartedObserver?.let { MapboxNavigationApp.unregisterObserver(it) }
}

override fun onResume(owner: LifecycleOwner) {
onResumedObserver?.let { MapboxNavigationApp.registerObserver(it) }
}

override fun onPause(owner: LifecycleOwner) {
onResumedObserver?.let { MapboxNavigationApp.unregisterObserver(it) }
}
}

/**
* Returns an instance of [MapboxNavigation], the first retrieval will attach the [thisRef]
* to [MapboxNavigationApp.attach]. If [MapboxNavigationApp.isSetup] is false after all
* observers and initializers, this property getter will crash.
*
* @param thisRef - the [LifecycleOwner] that needs access to [MapboxNavigation].
* @param property - ignored
*/
override fun getValue(thisRef: LifecycleOwner, property: KProperty<*>): MapboxNavigation {
if (!this::lifecycleOwner.isInitialized) {
onInitialize?.invoke()
this.lifecycleOwner = thisRef
MapboxNavigationApp.attach(lifecycleOwner)
lifecycleOwner.lifecycle.addObserver(lifecycleObserver)
}
val mapboxNavigation = MapboxNavigationApp.current()
checkNotNull(mapboxNavigation) {
"MapboxNavigation cannot be null. Ensure that MapboxNavigationApp is setup and an" +
" attached lifecycle is at least CREATED."
}
return mapboxNavigation
}
}
Loading

0 comments on commit 6234305

Please sign in to comment.