diff --git a/CHANGELOG.md b/CHANGELOG.md index c8e5db6620d..b300b04c606 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ Mapbox welcomes participation and contributions from everyone. - Exposed a new API `MapboxManeuverView.updateLaneGuidanceIconStyle` that would allow changing the style of `MapboxLaneGuidanceAdapter` at runtime. [#5334](https://github.com/mapbox/mapbox-navigation-android/pull/5334) - Fixed a crash when `MapboxNavigationViewportDataSourceDebugger.enabled` is repeatedly set to true. [#5347](https://github.com/mapbox/mapbox-navigation-android/pull/5347) - Implemented vanishing route line feature from 1.x for exposing an option to adjust/limit the frequency of the vanishing route line updates. The MapboxRouteLineOptions.vanishingRouteLineUpdateIntervalNano can reduce the frequency of vanishing route line updates when the value of the option increases. [#5344](https://github.com/mapbox/mapbox-navigation-android/pull/5344) +- Fixed `RoadShield` by reverting the breaking changes and use the new shield callback. [#5302](https://github.com/mapbox/mapbox-navigation-android/pull/5302) ## Mapbox Navigation SDK 2.0.5 - January 7, 2022 This is a patch release on top of `v2.0.x` which does not include changes introduced in `v2.1.x` and later. diff --git a/examples/src/main/java/com/mapbox/navigation/examples/core/MapboxManeuverActivity.kt b/examples/src/main/java/com/mapbox/navigation/examples/core/MapboxManeuverActivity.kt index 7a932505212..51f4ae091ba 100644 --- a/examples/src/main/java/com/mapbox/navigation/examples/core/MapboxManeuverActivity.kt +++ b/examples/src/main/java/com/mapbox/navigation/examples/core/MapboxManeuverActivity.kt @@ -43,7 +43,6 @@ import com.mapbox.navigation.core.trip.session.LocationObserver import com.mapbox.navigation.core.trip.session.RouteProgressObserver import com.mapbox.navigation.examples.core.databinding.LayoutActivityManeuverBinding import com.mapbox.navigation.ui.maneuver.api.MapboxManeuverApi -import com.mapbox.navigation.ui.maneuver.api.RoadShieldsCallback import com.mapbox.navigation.ui.maneuver.model.Maneuver import com.mapbox.navigation.ui.maneuver.model.ManeuverError import com.mapbox.navigation.ui.maneuver.view.MapboxManeuverView @@ -59,6 +58,7 @@ import com.mapbox.navigation.ui.maps.route.line.api.MapboxRouteLineView import com.mapbox.navigation.ui.maps.route.line.model.MapboxRouteLineOptions import com.mapbox.navigation.ui.maps.route.line.model.RouteLine import com.mapbox.navigation.ui.maps.route.line.model.RouteLineResources +import com.mapbox.navigation.ui.shield.model.RouteShieldCallback import com.mapbox.navigation.ui.utils.internal.ifNonNull import com.mapbox.navigation.utils.internal.LoggerProvider import kotlinx.coroutines.CoroutineScope @@ -129,16 +129,8 @@ class MapboxManeuverActivity : AppCompatActivity(), OnMapLongClickListener { private val replayProgressObserver = ReplayProgressObserver(mapboxReplayer) - private val roadShieldCallback = RoadShieldsCallback { _, shieldResult, shieldErrors -> + private val roadShieldCallback = RouteShieldCallback { shieldResult -> binding.maneuverView.renderManeuverWith(shieldResult) - shieldErrors.forEach { (id, errors) -> - errors.forEach { error -> - LoggerProvider.logger.e( - Tag("MbxManeuverActivity"), - Message("id: $id -- error: ${error.url} - ${error.message}") - ) - } - } } private val locationObserver = object : LocationObserver { diff --git a/libnavui-maneuver/api/current.txt b/libnavui-maneuver/api/current.txt index 88e7f00d4b5..12847d20c3c 100644 --- a/libnavui-maneuver/api/current.txt +++ b/libnavui-maneuver/api/current.txt @@ -15,9 +15,8 @@ package com.mapbox.navigation.ui.maneuver.api { method public com.mapbox.bindgen.Expected> getManeuvers(com.mapbox.api.directions.v5.models.DirectionsRoute route); method public com.mapbox.bindgen.Expected> getManeuvers(com.mapbox.navigation.base.trip.model.RouteProgress routeProgress); method @Deprecated public void getRoadShields(java.util.List maneuvers, com.mapbox.navigation.ui.maneuver.api.RoadShieldCallback callback); - method @Deprecated public void getRoadShields(String? userId, String? styleId, String? accessToken, java.util.List maneuvers, com.mapbox.navigation.ui.maneuver.api.RoadShieldCallback callback); - method public void getRoadShields(java.util.List maneuvers, com.mapbox.navigation.ui.maneuver.api.RoadShieldsCallback shieldCallback); - method public void getRoadShields(String? userId, String? styleId, String? accessToken, java.util.List maneuvers, com.mapbox.navigation.ui.maneuver.api.RoadShieldsCallback shieldCallback); + method public void getRoadShields(java.util.List maneuvers, com.mapbox.navigation.ui.shield.model.RouteShieldCallback shieldCallback); + method public void getRoadShields(String? userId, String? styleId, String? accessToken, java.util.List maneuvers, com.mapbox.navigation.ui.shield.model.RouteShieldCallback shieldCallback); } public final class MapboxTurnIconsApi { @@ -33,10 +32,6 @@ package com.mapbox.navigation.ui.maneuver.api { method @Deprecated public void onRoadShields(java.util.List maneuvers, java.util.Map shields, java.util.Map errors); } - public fun interface RoadShieldsCallback { - method public void onRoadShields(java.util.List maneuvers, java.util.Map> shields, java.util.Map> errors); - } - } package com.mapbox.navigation.ui.maneuver.model { @@ -408,18 +403,14 @@ package com.mapbox.navigation.ui.maneuver.model { field public static final com.mapbox.navigation.ui.maneuver.model.PrimaryManeuverFactory INSTANCE; } - public final class RoadShield { - ctor public RoadShield(String shieldUrl, byte[] shieldIcon, com.mapbox.api.directions.v5.models.MapboxShield? mapboxShield = null); - ctor public RoadShield(String shieldUrl, byte[] shieldIcon); - method public String component1(); - method public byte[] component2(); - method public com.mapbox.api.directions.v5.models.MapboxShield? component3(); - method public com.mapbox.navigation.ui.maneuver.model.RoadShield copy(String shieldUrl, byte[] shieldIcon, com.mapbox.api.directions.v5.models.MapboxShield? mapboxShield); - method public com.mapbox.api.directions.v5.models.MapboxShield? getMapboxShield(); - method public byte[] getShieldIcon(); - method public String getShieldUrl(); - method public void setShieldIcon(byte[] p); - property public final com.mapbox.api.directions.v5.models.MapboxShield? mapboxShield; + @Deprecated public final class RoadShield { + ctor @Deprecated public RoadShield(String shieldUrl, byte[] shieldIcon); + method @Deprecated public String component1(); + method @Deprecated public byte[] component2(); + method @Deprecated public com.mapbox.navigation.ui.maneuver.model.RoadShield copy(String shieldUrl, byte[] shieldIcon); + method @Deprecated public byte[] getShieldIcon(); + method @Deprecated public String getShieldUrl(); + method @Deprecated public void setShieldIcon(byte[] p); property public final byte[] shieldIcon; property public final String shieldUrl; } @@ -442,16 +433,20 @@ package com.mapbox.navigation.ui.maneuver.model { method public com.mapbox.navigation.ui.maneuver.model.RoadShieldComponentNode.Builder text(String text); } - public final class RoadShieldError { - method public String component1(); - method public String component2(); - method public com.mapbox.navigation.ui.maneuver.model.RoadShieldError copy(String url, String message); - method public String getMessage(); - method public String getUrl(); + @Deprecated public final class RoadShieldError { + method @Deprecated public String component1(); + method @Deprecated public String component2(); + method @Deprecated public com.mapbox.navigation.ui.maneuver.model.RoadShieldError copy(String url, String message); + method @Deprecated public String getMessage(); + method @Deprecated public String getUrl(); property public final String message; property public final String url; } + public final class RoadShieldExKt { + method public static com.mapbox.navigation.ui.shield.model.RouteShield toRouteShield(com.mapbox.navigation.ui.maneuver.model.RoadShield); + } + public final class SecondaryManeuver { method public java.util.List getComponentList(); method public Double? getDegrees(); @@ -851,16 +846,16 @@ package com.mapbox.navigation.ui.maneuver.view { method public void renderAddLanes(com.mapbox.navigation.ui.maneuver.model.Lane lane); method public void renderDistanceRemaining(com.mapbox.navigation.ui.maneuver.model.StepDistance stepDistance); method @Deprecated public void renderManeuverShields(java.util.Map shieldMap); - method public void renderManeuverWith(java.util.Map> shields); + method public void renderManeuverWith(java.util.List> shields); method public void renderManeuvers(com.mapbox.bindgen.Expected> maneuvers); - method public void renderPrimary(com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver primary, java.util.List? roadShields); + method public void renderPrimary(com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver primary, java.util.Set? routeShields); method @Deprecated public void renderPrimaryManeuver(com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver primary, com.mapbox.navigation.ui.maneuver.model.RoadShield? roadShield = null); method @Deprecated public void renderPrimaryManeuver(com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver primary); method public void renderRemoveLanes(); - method public void renderSecondary(com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver? secondary, java.util.List? roadShields); + method public void renderSecondary(com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver? secondary, java.util.Set? routeShields); method @Deprecated public void renderSecondaryManeuver(com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver? secondary, com.mapbox.navigation.ui.maneuver.model.RoadShield? roadShield = null); method @Deprecated public void renderSecondaryManeuver(com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver? secondary); - method public void renderSub(com.mapbox.navigation.ui.maneuver.model.SubManeuver? sub, java.util.List? roadShields); + method public void renderSub(com.mapbox.navigation.ui.maneuver.model.SubManeuver? sub, java.util.Set? routeShields); method @Deprecated public void renderSubManeuver(com.mapbox.navigation.ui.maneuver.model.SubManeuver? sub, com.mapbox.navigation.ui.maneuver.model.RoadShield? roadShield = null); method @Deprecated public void renderSubManeuver(com.mapbox.navigation.ui.maneuver.model.SubManeuver? sub); method public void setUpcomingManeuverRenderingEnabled(boolean value); @@ -888,7 +883,7 @@ package com.mapbox.navigation.ui.maneuver.view { ctor public MapboxPrimaryManeuver(android.content.Context context, android.util.AttributeSet? attrs, int defStyleAttr); method @Deprecated public void render(com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver maneuver, com.mapbox.navigation.ui.maneuver.model.RoadShield? roadShield = null); method @Deprecated public void render(com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver maneuver); - method public void renderManeuver(com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver maneuver, java.util.List? roadShields); + method public void renderManeuver(com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver maneuver, java.util.Set? routeShields); } public final class MapboxSecondaryManeuver extends androidx.appcompat.widget.AppCompatTextView { @@ -897,7 +892,7 @@ package com.mapbox.navigation.ui.maneuver.view { ctor public MapboxSecondaryManeuver(android.content.Context context, android.util.AttributeSet? attrs, int defStyleAttr); method @Deprecated public void render(com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver? maneuver, com.mapbox.navigation.ui.maneuver.model.RoadShield? roadShield = null); method @Deprecated public void render(com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver? maneuver); - method public void renderManeuver(com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver? maneuver, java.util.List? roadShields); + method public void renderManeuver(com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver? maneuver, java.util.Set? routeShields); } public final class MapboxStepDistance extends androidx.appcompat.widget.AppCompatTextView { @@ -914,7 +909,7 @@ package com.mapbox.navigation.ui.maneuver.view { ctor public MapboxSubManeuver(android.content.Context context, android.util.AttributeSet? attrs, int defStyleAttr); method @Deprecated public void render(com.mapbox.navigation.ui.maneuver.model.SubManeuver? maneuver, com.mapbox.navigation.ui.maneuver.model.RoadShield? roadShield = null); method @Deprecated public void render(com.mapbox.navigation.ui.maneuver.model.SubManeuver? maneuver); - method public void renderManeuver(com.mapbox.navigation.ui.maneuver.model.SubManeuver? maneuver, java.util.List? roadShields); + method public void renderManeuver(com.mapbox.navigation.ui.maneuver.model.SubManeuver? maneuver, java.util.Set? routeShields); } public final class MapboxTurnIconManeuver extends androidx.appcompat.widget.AppCompatImageView { @@ -934,7 +929,7 @@ package com.mapbox.navigation.ui.maneuver.view { method public void onBindViewHolder(com.mapbox.navigation.ui.maneuver.view.MapboxUpcomingManeuverAdapter.MapboxUpcomingManeuverViewHolder holder, int position); method public com.mapbox.navigation.ui.maneuver.view.MapboxUpcomingManeuverAdapter.MapboxUpcomingManeuverViewHolder onCreateViewHolder(android.view.ViewGroup parent, int viewType); method @Deprecated public void updateRoadShields(java.util.Map shieldMap); - method public void updateShields(java.util.Map> shields); + method public void updateShields(java.util.Set shields); method public void updateUpcomingManeuverStepDistanceTextAppearance(@StyleRes int style); method public void updateUpcomingPrimaryManeuverTextAppearance(@StyleRes int style); method public void updateUpcomingSecondaryManeuverTextAppearance(@StyleRes int style); diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/api/MapboxManeuverApi.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/api/MapboxManeuverApi.kt index d0cd01061ca..f19098303bf 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/api/MapboxManeuverApi.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/api/MapboxManeuverApi.kt @@ -29,6 +29,8 @@ import com.mapbox.navigation.ui.shield.internal.api.getRouteShieldsFromModels import com.mapbox.navigation.ui.shield.internal.model.RouteShieldToDownload import com.mapbox.navigation.ui.shield.internal.model.ShieldSpriteToDownload import com.mapbox.navigation.ui.shield.model.RouteShield +import com.mapbox.navigation.ui.shield.model.RouteShieldCallback +import com.mapbox.navigation.ui.shield.model.RouteShieldError import com.mapbox.navigation.ui.shield.model.RouteShieldResult import com.mapbox.navigation.utils.internal.InternalJobControlFactory import kotlinx.coroutines.launch @@ -161,7 +163,9 @@ class MapboxManeuverApi internal constructor( * @see MapboxManeuverView.renderManeuverShields */ @Deprecated( - message = "The API is incapable of associating multiple shields with a single maneuver id", + message = "The API is incapable of delivering Mapbox designed route shields. In cases " + + "where an instruction contains multiple shields, the API may only render 1 shield " + + "instead of multiple for that maneuver.", replaceWith = ReplaceWith( "getRoadShields(maneuvers, shieldCallback)", "com.mapbox.navigation.ui.maneuver.api" @@ -181,6 +185,37 @@ class MapboxManeuverApi internal constructor( ) } + /** + * Given a list of [Maneuver] the function requests legacy road shields (if available) using + * [BannerComponents.imageBaseUrl] associated in [RoadShieldComponentNode]. + * + * If you do not wish to download all of the shields at once, + * make sure to pass in only a list of maneuvers that you'd like download the road shields. + * + * The function is safe to be called repeatably, all the results are cached in-memory + * and requests are managed to avoid duplicating network bandwidth usage. + * + * The function returns list of either [RouteShieldError] or [RouteShieldResult] in + * [RouteShieldCallback.onRoadShields] and can be used when displaying [PrimaryManeuver], + * [SecondaryManeuver] and [SubManeuver]. + * + * @param maneuvers list of maneuvers + * @param shieldCallback invoked with appropriate result + * @see MapboxManeuverView.renderManeuverShields + */ + fun getRoadShields( + maneuvers: List, + shieldCallback: RouteShieldCallback + ) { + getRoadShields( + null, + null, + null, + maneuvers, + shieldCallback + ) + } + /** * Given a list of [Maneuver] the function requests mapbox designed road shields (if available) * using [BannerComponents.mapboxShield] associated in [RoadShieldComponentNode]. If for any @@ -193,33 +228,23 @@ class MapboxManeuverApi internal constructor( * The function is safe to be called repeatably, all the results are cached in-memory * and requests are managed to avoid duplicating network bandwidth usage. * - * The function returns maps of [String] to [RoadShield] or [String] to [RoadShieldError] in - * [RoadShieldCallback.onRoadShields] and can be used when displaying [PrimaryManeuver], + * The function returns list of either [RouteShieldError] or [RouteShieldResult] in + * [RouteShieldCallback.onRoadShields] and can be used when displaying [PrimaryManeuver], * [SecondaryManeuver] and [SubManeuver]. * * @param maneuvers list of maneuvers - * @param callback invoked with appropriate result + * @param shieldCallback invoked with appropriate result * @see MapboxManeuverView.renderManeuverShields */ - @Deprecated( - message = "The API is incapable of associating multiple shields with a single maneuver id", - replaceWith = ReplaceWith( - "getRoadShields(userId, styleId, accessToken, maneuvers, shieldCallback)", - "com.mapbox.navigation.ui.maneuver.api" - ), - level = DeprecationLevel.WARNING - ) fun getRoadShields( userId: String?, styleId: String?, accessToken: String?, maneuvers: List, - callback: RoadShieldCallback + shieldCallback: RouteShieldCallback ) { mainJobController.scope.launch { - val shieldMap = hashMapOf>() - val errorMap = hashMapOf>() - + val shields = mutableListOf>() maneuvers.forEach { maneuver -> routeShieldApi.getRouteShieldsFromModels( maneuver.primary.componentList.findShieldsToDownload( @@ -228,24 +253,7 @@ class MapboxManeuverApi internal constructor( styleId = styleId ) ).let { results -> - val shields = mutableListOf() - val errors = mutableListOf() - results.forEach { result -> - result.fold( - { error -> - errors.add( - RoadShieldError(url = error.url, message = error.errorMessage) - ) - }, - { routeShieldResult -> - shields.add(getShield(routeShieldResult)) - } - ) - } - shieldMap[maneuver.primary.id] = shields - if (errors.isNotEmpty()) { - errorMap[maneuver.primary.id] = errors - } + shields.addAll(results) } maneuver.secondary?.let { secondary -> routeShieldApi.getRouteShieldsFromModels( @@ -255,27 +263,7 @@ class MapboxManeuverApi internal constructor( styleId = styleId ) ).let { results -> - val shields = mutableListOf() - val errors = mutableListOf() - results.forEach { result -> - result.fold( - { error -> - errors.add( - RoadShieldError( - url = error.url, - message = error.errorMessage - ) - ) - }, - { routeShieldResult -> - shields.add(getShield(routeShieldResult)) - } - ) - } - shieldMap[secondary.id] = shields - if (errors.isNotEmpty()) { - errorMap[secondary.id] = errors - } + shields.addAll(results) } } maneuver.sub?.let { sub -> @@ -286,95 +274,27 @@ class MapboxManeuverApi internal constructor( styleId = styleId ) ).let { results -> - val shields = mutableListOf() - val errors = mutableListOf() - results.forEach { result -> - result.fold( - { error -> - errors.add( - RoadShieldError( - url = error.url, - message = error.errorMessage - ) - ) - }, - { routeShieldResult -> - shields.add(getShield(routeShieldResult)) - } - ) - } - shieldMap[sub.id] = shields - if (errors.isNotEmpty()) { - errorMap[sub.id] = errors - } + shields.addAll(results) } } } - callback.onRoadShields( - maneuvers = maneuvers, - shields = shieldMap.mapValues { it.value.firstOrNull() }, - errors = errorMap.mapValues { it.value.first() } - ) + shieldCallback.onRoadShields(shields = shields) } } /** - * Given a list of [Maneuver] the function requests legacy road shields (if available) using - * [BannerComponents.imageBaseUrl] associated in [RoadShieldComponentNode]. - * - * If you do not wish to download all of the shields at once, - * make sure to pass in only a list of maneuvers that you'd like download the road shields. - * - * The function is safe to be called repeatably, all the results are cached in-memory - * and requests are managed to avoid duplicating network bandwidth usage. - * - * The function returns maps of [String] to [RoadShield] or [String] to [RoadShieldError] in - * [RoadShieldsCallback.onRoadShields] and can be used when displaying [PrimaryManeuver], - * [SecondaryManeuver] and [SubManeuver]. - * - * @param maneuvers list of maneuvers - * @param shieldCallback invoked with appropriate result - * @see MapboxManeuverView.renderManeuverShields + * Invoke the function to cancel any job invoked through other APIs */ - fun getRoadShields( - maneuvers: List, - shieldCallback: RoadShieldsCallback - ) { - getRoadShields( - null, - null, - null, - maneuvers, - shieldCallback - ) + fun cancel() { + routeShieldApi.cancel() } - /** - * Given a list of [Maneuver] the function requests mapbox designed road shields (if available) - * using [BannerComponents.mapboxShield] associated in [RoadShieldComponentNode]. If for any - * reason the API fails to download the mapbox designed shields, it fallbacks to use legacy - * [BannerComponents.imageBaseUrl] if available. - * - * If you do not wish to download all of the shields at once, - * make sure to pass in only a list of maneuvers that you'd like download the road shields. - * - * The function is safe to be called repeatably, all the results are cached in-memory - * and requests are managed to avoid duplicating network bandwidth usage. - * - * The function returns maps of [String] to [RoadShield] or [String] to [RoadShieldError] in - * [RoadShieldsCallback.onRoadShields] and can be used when displaying [PrimaryManeuver], - * [SecondaryManeuver] and [SubManeuver]. - * - * @param maneuvers list of maneuvers - * @param shieldCallback invoked with appropriate result - * @see MapboxManeuverView.renderManeuverShields - */ - fun getRoadShields( + internal fun getRoadShields( userId: String?, styleId: String?, accessToken: String?, maneuvers: List, - shieldCallback: RoadShieldsCallback + callback: RoadShieldCallback ) { mainJobController.scope.launch { val shieldMap = hashMapOf>() @@ -470,21 +390,14 @@ class MapboxManeuverApi internal constructor( } } } - shieldCallback.onRoadShields( + callback.onRoadShields( maneuvers = maneuvers, - shields = shieldMap, - errors = errorMap + shields = shieldMap.mapValues { it.value.firstOrNull() }, + errors = errorMap.mapValues { it.value.first() } ) } } - /** - * Invoke the function to cancel any job invoked through other APIs - */ - fun cancel() { - routeShieldApi.cancel() - } - private fun List.findShieldsToDownload( accessToken: String? = null, userId: String? = null, @@ -528,16 +441,13 @@ class MapboxManeuverApi internal constructor( is RouteShield.MapboxLegacyShield -> { RoadShield( shieldUrl = routeShieldResult.shield.url, - shieldIcon = routeShieldResult.shield.byteArray, - mapboxShield = null + shieldIcon = routeShieldResult.shield.byteArray ) } else -> { RoadShield( shieldUrl = routeShieldResult.shield.url, - shieldIcon = routeShieldResult.shield.byteArray, - mapboxShield = - (routeShieldResult.shield as RouteShield.MapboxDesignedShield).mapboxShield + shieldIcon = routeShieldResult.shield.byteArray ) } } diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/api/RoadShieldsCallback.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/api/RoadShieldsCallback.kt deleted file mode 100644 index b1c15d82dfd..00000000000 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/api/RoadShieldsCallback.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.mapbox.navigation.ui.maneuver.api - -import com.mapbox.navigation.ui.maneuver.model.Maneuver -import com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver -import com.mapbox.navigation.ui.maneuver.model.RoadShield -import com.mapbox.navigation.ui.maneuver.model.RoadShieldError -import com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver -import com.mapbox.navigation.ui.maneuver.model.SubManeuver - -/** - * An interface that is triggered when road shields are available. - */ -fun interface RoadShieldsCallback { - - /** - * The callback is invoked when road shields are ready. - * - * The method provides access to the original list of maneuvers used to request shields, - * a map of [String] to list of [RoadShield] containing the shields and - * a map of [String] to list of [RoadShieldError] containing errors when downloading shields for - * [PrimaryManeuver], [SecondaryManeuver], and [SubManeuver]. - * - * @param maneuvers list of [Maneuver]s for which the shields were requested. - * @param shields map of a key to list of [RoadShield] where key is an ID of a maneuver's banner, - * one of [PrimaryManeuver.id], [SecondaryManeuver.id] or [SubManeuver.id]. - * You can use those IDs to associate the maneuver's banner with the icon. - * @param errors map of a key to list of [RoadShieldError] where key is an ID of a maneuver's - * banner, one of [PrimaryManeuver.id], [SecondaryManeuver.id] or [SubManeuver.id]. - * You can use those IDs to associate the maneuver's banner with the error. - */ - fun onRoadShields( - maneuvers: List, - shields: Map>, - errors: Map> - ) -} diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/ManeuverInstructionGenerator.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/ManeuverInstructionGenerator.kt index 2bcdedcf2ff..d34a4bb6d0a 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/ManeuverInstructionGenerator.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/ManeuverInstructionGenerator.kt @@ -4,6 +4,9 @@ import android.content.Context import android.content.res.Resources import android.text.SpannableStringBuilder import com.mapbox.navigation.ui.maneuver.view.MapboxExitText +import com.mapbox.navigation.ui.shield.internal.model.getRefLen +import com.mapbox.navigation.ui.shield.model.RouteShield +import com.mapbox.navigation.ui.utils.internal.ifNonNull internal object ManeuverInstructionGenerator { @@ -12,7 +15,7 @@ internal object ManeuverInstructionGenerator { desiredHeight: Int, exitView: MapboxExitText, maneuver: PrimaryManeuver, - roadShields: List? = null + roadShields: Set? = null ): SpannableStringBuilder { val instructionBuilder = SpannableStringBuilder() maneuver.componentList.forEach { component -> @@ -31,16 +34,11 @@ internal object ManeuverInstructionGenerator { ) } is RoadShieldComponentNode -> { - val shield = roadShields?.find { - it.mapboxShield == node.mapboxShield - } ?: roadShields?.find { - it.shieldUrl == node.shieldUrl - } addShieldToBuilder( node.text, desiredHeight, context.resources, - shield, + getShieldToRender(node, roadShields), instructionBuilder ) } @@ -57,7 +55,7 @@ internal object ManeuverInstructionGenerator { desiredHeight: Int, exitView: MapboxExitText, maneuver: SecondaryManeuver?, - roadShields: List? = null + roadShields: Set? = null ): SpannableStringBuilder { val instructionBuilder = SpannableStringBuilder() maneuver?.componentList?.forEach { component -> @@ -76,16 +74,11 @@ internal object ManeuverInstructionGenerator { ) } is RoadShieldComponentNode -> { - val shield = roadShields?.find { - it.mapboxShield == node.mapboxShield - } ?: roadShields?.find { - it.shieldUrl == node.shieldUrl - } addShieldToBuilder( node.text, desiredHeight, context.resources, - shield, + getShieldToRender(node, roadShields), instructionBuilder ) } @@ -102,7 +95,7 @@ internal object ManeuverInstructionGenerator { desiredHeight: Int, exitView: MapboxExitText, maneuver: SubManeuver?, - roadShields: List? = null + roadShields: Set? = null ): SpannableStringBuilder { val instructionBuilder = SpannableStringBuilder() maneuver?.componentList?.forEach { component -> @@ -121,16 +114,11 @@ internal object ManeuverInstructionGenerator { ) } is RoadShieldComponentNode -> { - val shield = roadShields?.find { - it.mapboxShield == node.mapboxShield - } ?: roadShields?.find { - it.shieldUrl == node.shieldUrl - } addShieldToBuilder( node.text, desiredHeight, context.resources, - shield, + getShieldToRender(node, roadShields), instructionBuilder ) } @@ -168,14 +156,14 @@ internal object ManeuverInstructionGenerator { shieldText: String, desiredHeight: Int, resources: Resources, - roadShield: RoadShield?, + routeShield: RouteShield?, builder: SpannableStringBuilder ) { val roadShieldBuilder = RoadShieldGenerator.styleAndGetRoadShield( shieldText, desiredHeight, resources, - roadShield + routeShield ) builder.append(roadShieldBuilder) builder.append(" ") @@ -185,4 +173,25 @@ internal object ManeuverInstructionGenerator { builder.append(text) builder.append(" ") } + + private fun getShieldToRender( + node: RoadShieldComponentNode, + roadShields: Set? + ): RouteShield? { + return roadShields?.find { + when (it) { + is RouteShield.MapboxDesignedShield -> { + ifNonNull(node.mapboxShield) { mbxShield -> + val shieldName = mbxShield.name() + val displayRefLength = mbxShield.getRefLen() + val shieldRequested = shieldName.plus("-$displayRefLength") + it.url.contains(shieldRequested) + } ?: false + } + is RouteShield.MapboxLegacyShield -> { + it.initialUrl == node.shieldUrl + } + } + } + } } diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShield.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShield.kt index 036cec749ab..0a9e3e24cd5 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShield.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShield.kt @@ -1,17 +1,16 @@ package com.mapbox.navigation.ui.maneuver.model -import com.mapbox.api.directions.v5.models.MapboxShield - /** * Data structure that holds information about road shield. * @property shieldUrl String * @property shieldIcon ByteArray - * @property mapboxShield MapboxShield */ -data class RoadShield @JvmOverloads constructor( +@Deprecated( + message = "The data class is incapable of delivering Mapbox designed route shields." +) +data class RoadShield constructor( val shieldUrl: String, - var shieldIcon: ByteArray, - val mapboxShield: MapboxShield? = null + var shieldIcon: ByteArray ) { /** @@ -25,7 +24,6 @@ data class RoadShield @JvmOverloads constructor( if (shieldUrl != other.shieldUrl) return false if (!shieldIcon.contentEquals(other.shieldIcon)) return false - if (mapboxShield != other.mapboxShield) return false return true } @@ -36,7 +34,6 @@ data class RoadShield @JvmOverloads constructor( override fun hashCode(): Int { var result = shieldUrl.hashCode() result = 31 * result + shieldIcon.contentHashCode() - result = 31 * result + (mapboxShield?.hashCode() ?: 0) return result } @@ -47,7 +44,6 @@ data class RoadShield @JvmOverloads constructor( return "RoadShield(" + "shieldUrl='$shieldUrl', " + "shieldIcon=${shieldIcon.contentToString()}, " + - "mapboxShield=$mapboxShield" + ")" } } diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldError.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldError.kt index 213dd4fdf08..694a75a763e 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldError.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldError.kt @@ -5,6 +5,9 @@ package com.mapbox.navigation.ui.maneuver.model * @property url String * @property message String */ +@Deprecated( + message = "The data class is incapable of delivering Mapbox designed route shields." +) data class RoadShieldError internal constructor( val url: String, val message: String diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldEx.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldEx.kt new file mode 100644 index 00000000000..aeb1eda138d --- /dev/null +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldEx.kt @@ -0,0 +1,19 @@ +package com.mapbox.navigation.ui.maneuver.model + +import com.mapbox.navigation.base.ExperimentalMapboxNavigationAPI +import com.mapbox.navigation.ui.shield.model.RouteShield +import com.mapbox.navigation.ui.shield.model.RouteShieldFactory + +private const val SVG_EXTENSION = ".svg" + +/** + * Extension function to convert [RoadShield] to [RouteShield] + */ +@OptIn(ExperimentalMapboxNavigationAPI::class) +fun RoadShield.toRouteShield(): RouteShield { + return RouteShieldFactory.buildRouteShield( + downloadUrl = this.shieldUrl, + byteArray = this.shieldIcon, + initialUrl = this.shieldUrl.dropLast(SVG_EXTENSION.length) + ) +} diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldGenerator.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldGenerator.kt index df74a635b0a..876bb329f0e 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldGenerator.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldGenerator.kt @@ -4,6 +4,7 @@ import android.content.res.Resources import android.text.SpannableStringBuilder import android.text.Spanned import android.text.style.ImageSpan +import com.mapbox.navigation.ui.shield.model.RouteShield import com.mapbox.navigation.ui.utils.internal.SvgUtil import com.mapbox.navigation.ui.utils.internal.extensions.drawableWithHeight import java.io.ByteArrayInputStream @@ -14,10 +15,10 @@ internal object RoadShieldGenerator { shieldText: String, desiredHeight: Int, resources: Resources, - roadShield: RoadShield? = null + routeShield: RouteShield? = null ): SpannableStringBuilder { val roadShieldBuilder = SpannableStringBuilder(shieldText) - val shieldIcon = roadShield?.shieldIcon + val shieldIcon = routeShield?.byteArray if (shieldIcon != null && shieldIcon.isNotEmpty()) { val stream = ByteArrayInputStream(shieldIcon) val svgBitmap = SvgUtil.renderAsBitmapWithHeight(stream, desiredHeight) diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldResult.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldResult.kt deleted file mode 100644 index 5de88e2f5e8..00000000000 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldResult.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.mapbox.navigation.ui.maneuver.model - -internal data class RoadShieldResult( - val shields: Map, - val errors: Map -) diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxManeuverView.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxManeuverView.kt index 7ce576dd968..41738622a17 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxManeuverView.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxManeuverView.kt @@ -12,6 +12,8 @@ import androidx.core.widget.TextViewCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager.HORIZONTAL import androidx.recyclerview.widget.RecyclerView.VERTICAL +import com.mapbox.base.common.logger.model.Message +import com.mapbox.base.common.logger.model.Tag import com.mapbox.bindgen.Expected import com.mapbox.navigation.ui.maneuver.R import com.mapbox.navigation.ui.maneuver.api.MapboxManeuverApi @@ -27,6 +29,11 @@ import com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver import com.mapbox.navigation.ui.maneuver.model.StepDistance import com.mapbox.navigation.ui.maneuver.model.SubManeuver import com.mapbox.navigation.ui.maneuver.model.TurnIconResources +import com.mapbox.navigation.ui.maneuver.model.toRouteShield +import com.mapbox.navigation.ui.shield.model.RouteShield +import com.mapbox.navigation.ui.shield.model.RouteShieldError +import com.mapbox.navigation.ui.shield.model.RouteShieldResult +import com.mapbox.navigation.utils.internal.LoggerProvider import com.mapbox.navigation.utils.internal.ifNonNull /** @@ -75,7 +82,7 @@ class MapboxManeuverView : ConstraintLayout { private val mainLayoutBinding = MapboxMainManeuverLayoutBinding.bind(binding.root) private val subLayoutBinding = MapboxSubManeuverLayoutBinding.bind(binding.root) - private val maneuverToRoadShields = mutableMapOf>() + private val routeShields = mutableSetOf() private val currentlyRenderedManeuvers: MutableList = mutableListOf() /** @@ -163,31 +170,46 @@ class MapboxManeuverView : ConstraintLayout { * @param shieldMap the map of maneuver IDs to available shields */ @Deprecated( - message = "The method can only render one shield if an instruction has multiple shields", + message = "The method is incapable of rendering Mapbox designed route shields. In cases " + + "where an instruction contains multiple shields, the method may only render 1 shield " + + "instead of multiple shields for that maneuver.", replaceWith = ReplaceWith("renderManeuverWith(shields)") ) fun renderManeuverShields(shieldMap: Map) { - val shields = hashMapOf>() shieldMap.forEach { entry -> - shields[entry.key] = ifNonNull(entry.value) { value -> - listOf(value) - } ?: listOf() + ifNonNull(entry.value) { value -> + routeShields.add(value.toRouteShield()) + } } - renderManeuverWith(shields) + renderManeuvers() } /** * Invoke the method to update rendered maneuvers with road shields. * - * The provided shields are mapped to IDs of [PrimaryManeuver], [SecondaryManeuver], and [SubManeuver] - * and if a maneuver has already been rendered via [renderManeuvers], the respective shields' text will be changed to the shield icon. + * The provided shields are list of either [RouteShieldError] or [RouteShieldResult]. + * If a maneuver has already been rendered via [renderManeuvers], the respective shields' + * text will be changed to the shield icon. * - * Invoking this method also caches all of the available shields. Whenever [renderManeuvers] is invoked, the cached shields are reused for rendering. + * Invoking this method also caches all of the available shields. Whenever [renderManeuvers] is + * invoked, the cached shields are reused for rendering. * * @param shields the map of maneuver IDs to available list of shields */ - fun renderManeuverWith(shields: Map>) { - maneuverToRoadShields.putAll(shields) + fun renderManeuverWith(shields: List>) { + shields.forEach { shield -> + shield.fold( + { error -> + LoggerProvider.logger.e( + Tag("MbxManeuverView"), + Message("id: $id -- error: ${error.url} - ${error.errorMessage}") + ) + }, + { result -> + routeShields.add(result.shield) + } + ) + } renderManeuvers() } @@ -343,13 +365,15 @@ class MapboxManeuverView : ConstraintLayout { * @param roadShield shield if available otherwise null */ @Deprecated( - message = "The method can only render one shield if an instruction has multiple shields", - replaceWith = ReplaceWith("renderPrimary(primary, roadShields)") + message = "The method is incapable of rendering Mapbox designed route shields. In cases " + + "where an instruction contains multiple shields, the method may only render 1 shield " + + "instead of multiple shields for that maneuver.", + replaceWith = ReplaceWith("renderPrimary(primary, routeShields)") ) @JvmOverloads fun renderPrimaryManeuver(primary: PrimaryManeuver, roadShield: RoadShield? = null) { val shields = ifNonNull(roadShield) { - listOf(it) + setOf(it.toRouteShield()) } renderPrimary(primary, shields) } @@ -357,10 +381,10 @@ class MapboxManeuverView : ConstraintLayout { /** * Invoke the method to render primary instructions on top of [MapboxManeuverView] * @param primary PrimaryManeuver - * @param roadShields list of shields if available otherwise null + * @param routeShields set of shields if available otherwise null */ - fun renderPrimary(primary: PrimaryManeuver, roadShields: List?) { - mainLayoutBinding.primaryManeuverText.renderManeuver(primary, roadShields) + fun renderPrimary(primary: PrimaryManeuver, routeShields: Set?) { + mainLayoutBinding.primaryManeuverText.renderManeuver(primary, routeShields) mainLayoutBinding.maneuverIcon.renderPrimaryTurnIcon(primary) } @@ -370,13 +394,15 @@ class MapboxManeuverView : ConstraintLayout { * @param roadShield shield if available otherwise null */ @Deprecated( - message = "The method can only render one shield if an instruction has multiple shields", - replaceWith = ReplaceWith("renderSecondary(secondary, roadShields)") + message = "The method is incapable of rendering Mapbox designed route shields. In cases " + + "where an instruction contains multiple shields, the method may only render 1 shield " + + "instead of multiple shields for that maneuver.", + replaceWith = ReplaceWith("renderSecondary(secondary, routeShields)") ) @JvmOverloads fun renderSecondaryManeuver(secondary: SecondaryManeuver?, roadShield: RoadShield? = null) { val shields = ifNonNull(roadShield) { - listOf(it) + setOf(it.toRouteShield()) } renderSecondary(secondary, shields) } @@ -384,10 +410,10 @@ class MapboxManeuverView : ConstraintLayout { /** * Invoke the method to render secondary instructions on top of [MapboxManeuverView] * @param secondary SecondaryManeuver? - * @param roadShields list of shields if available otherwise null + * @param routeShields set of shields if available otherwise null */ - fun renderSecondary(secondary: SecondaryManeuver?, roadShields: List?) { - mainLayoutBinding.secondaryManeuverText.renderManeuver(secondary, roadShields) + fun renderSecondary(secondary: SecondaryManeuver?, routeShields: Set?) { + mainLayoutBinding.secondaryManeuverText.renderManeuver(secondary, routeShields) } /** @@ -396,13 +422,15 @@ class MapboxManeuverView : ConstraintLayout { * @param roadShield shield if available otherwise null */ @Deprecated( - message = "The method can only render one shield if an instruction has multiple shields", - replaceWith = ReplaceWith("renderSub(sub, roadShields)") + message = "The method is incapable of rendering Mapbox designed route shields. In cases " + + "where an instruction contains multiple shields, the method may only render 1 shield " + + "instead of multiple shields for that maneuver.", + replaceWith = ReplaceWith("renderSub(sub, routeShields)") ) @JvmOverloads fun renderSubManeuver(sub: SubManeuver?, roadShield: RoadShield? = null) { val shields = ifNonNull(roadShield) { - listOf(it) + setOf(it.toRouteShield()) } renderSub(sub, shields) } @@ -410,10 +438,10 @@ class MapboxManeuverView : ConstraintLayout { /** * Invoke the method to render sub instructions on top of [MapboxManeuverView] * @param sub SubManeuver? - * @param roadShields list of shields if available otherwise null + * @param routeShields set of shields if available otherwise null */ - fun renderSub(sub: SubManeuver?, roadShields: List?) { - subLayoutBinding.subManeuverText.renderManeuver(sub, roadShields) + fun renderSub(sub: SubManeuver?, routeShields: Set?) { + subLayoutBinding.subManeuverText.renderManeuver(sub, routeShields) subLayoutBinding.subManeuverIcon.renderSubTurnIcon(sub) } @@ -442,8 +470,8 @@ class MapboxManeuverView : ConstraintLayout { private fun renderManeuvers() { if (currentlyRenderedManeuvers.isNotEmpty()) { - drawManeuver(currentlyRenderedManeuvers[0], maneuverToRoadShields) - upcomingManeuverAdapter.updateShields(maneuverToRoadShields) + drawManeuver(currentlyRenderedManeuvers[0], routeShields) + upcomingManeuverAdapter.updateShields(routeShields) drawUpcomingManeuvers(currentlyRenderedManeuvers.drop(1)) } } @@ -517,22 +545,19 @@ class MapboxManeuverView : ConstraintLayout { upcomingManeuverAdapter.addUpcomingManeuvers(maneuvers) } - private fun drawManeuver(maneuver: Maneuver, shields: Map?>) { + private fun drawManeuver(maneuver: Maneuver, shields: Set?) { val primary = maneuver.primary val secondary = maneuver.secondary val sub = maneuver.sub val lane = maneuver.laneGuidance val stepDistance = maneuver.stepDistance - val primaryId = primary.id - val secondaryId = secondary?.id - val subId = sub?.id if (secondary?.componentList != null) { updateSecondaryManeuverVisibility(VISIBLE) - renderSecondary(secondary, shields[secondaryId]) + renderSecondary(secondary, shields) } else { updateSecondaryManeuverVisibility(GONE) } - renderPrimary(primary, shields[primaryId]) + renderPrimary(primary, shields) renderDistanceRemaining(stepDistance) if (sub?.componentList != null || lane != null) { updateSubManeuverViewVisibility(VISIBLE) @@ -540,9 +565,9 @@ class MapboxManeuverView : ConstraintLayout { updateSubManeuverViewVisibility(GONE) } if (sub?.componentList != null) { - renderSub(sub, shields[subId]) + renderSub(sub, shields) } else { - renderSub(null, listOf()) + renderSub(null, setOf()) } when (lane != null) { true -> { diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxPrimaryManeuver.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxPrimaryManeuver.kt index 08a5107a72f..10040b77448 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxPrimaryManeuver.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxPrimaryManeuver.kt @@ -8,6 +8,8 @@ import com.mapbox.navigation.ui.maneuver.R import com.mapbox.navigation.ui.maneuver.model.ManeuverInstructionGenerator import com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver import com.mapbox.navigation.ui.maneuver.model.RoadShield +import com.mapbox.navigation.ui.maneuver.model.toRouteShield +import com.mapbox.navigation.ui.shield.model.RouteShield import com.mapbox.navigation.utils.internal.ifNonNull /** @@ -61,13 +63,13 @@ class MapboxPrimaryManeuver : AppCompatTextView { * @param maneuver PrimaryManeuver */ @Deprecated( - message = "The method can only render one shield if an instruction has multiple shields", + message = "The method may or may not render multiple shields for a given instruction", replaceWith = ReplaceWith("renderManeuver(maneuver, roadShields)") ) @JvmOverloads fun render(maneuver: PrimaryManeuver, roadShield: RoadShield? = null) { val roadShields = ifNonNull(roadShield) { - listOf(it) + setOf(it.toRouteShield()) } renderManeuver(maneuver, roadShields) } @@ -76,7 +78,7 @@ class MapboxPrimaryManeuver : AppCompatTextView { * Invoke the method to render primary maneuver instructions * @param maneuver PrimaryManeuver */ - fun renderManeuver(maneuver: PrimaryManeuver, roadShields: List?) { + fun renderManeuver(maneuver: PrimaryManeuver, routeShields: Set?) { val exitView = MapboxExitText(context) exitView.setExitStyle(exitBackground, leftDrawable, rightDrawable) val instruction = ManeuverInstructionGenerator.generatePrimary( @@ -84,7 +86,7 @@ class MapboxPrimaryManeuver : AppCompatTextView { lineHeight, exitView, maneuver, - roadShields + routeShields ) if (instruction.isNotEmpty()) { text = instruction diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxSecondaryManeuver.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxSecondaryManeuver.kt index f87b50820d6..ef0a1f7cf77 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxSecondaryManeuver.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxSecondaryManeuver.kt @@ -8,6 +8,8 @@ import com.mapbox.navigation.ui.maneuver.R import com.mapbox.navigation.ui.maneuver.model.ManeuverInstructionGenerator import com.mapbox.navigation.ui.maneuver.model.RoadShield import com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver +import com.mapbox.navigation.ui.maneuver.model.toRouteShield +import com.mapbox.navigation.ui.shield.model.RouteShield import com.mapbox.navigation.utils.internal.ifNonNull /** @@ -61,13 +63,13 @@ class MapboxSecondaryManeuver : AppCompatTextView { * @param maneuver SecondaryManeuver */ @Deprecated( - message = "The method can only render one shield if an instruction has multiple shields", + message = "The method may or may not render multiple shields for a given instruction", replaceWith = ReplaceWith("renderManeuver(maneuver, roadShields)") ) @JvmOverloads fun render(maneuver: SecondaryManeuver?, roadShield: RoadShield? = null) { val roadShields = ifNonNull(roadShield) { - listOf(it) + setOf(it.toRouteShield()) } renderManeuver(maneuver, roadShields) } @@ -76,7 +78,7 @@ class MapboxSecondaryManeuver : AppCompatTextView { * Invoke the method to render secondary maneuver instructions * @param maneuver SecondaryManeuver */ - fun renderManeuver(maneuver: SecondaryManeuver?, roadShields: List?) { + fun renderManeuver(maneuver: SecondaryManeuver?, routeShields: Set?) { val exitView = MapboxExitText(context) exitView.setExitStyle(exitBackground, leftDrawable, rightDrawable) val instruction = ManeuverInstructionGenerator.generateSecondary( @@ -84,7 +86,7 @@ class MapboxSecondaryManeuver : AppCompatTextView { lineHeight, exitView, maneuver, - roadShields + routeShields ) if (instruction.isNotEmpty()) { text = instruction diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxSubManeuver.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxSubManeuver.kt index 837b07b4e7d..75393871270 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxSubManeuver.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxSubManeuver.kt @@ -8,6 +8,8 @@ import com.mapbox.navigation.ui.maneuver.R import com.mapbox.navigation.ui.maneuver.model.ManeuverInstructionGenerator import com.mapbox.navigation.ui.maneuver.model.RoadShield import com.mapbox.navigation.ui.maneuver.model.SubManeuver +import com.mapbox.navigation.ui.maneuver.model.toRouteShield +import com.mapbox.navigation.ui.shield.model.RouteShield import com.mapbox.navigation.utils.internal.ifNonNull /** @@ -61,13 +63,13 @@ class MapboxSubManeuver : AppCompatTextView { * @param maneuver SubManeuver */ @Deprecated( - message = "The method can only render one shield if an instruction has multiple shields", + message = "The method may or may not render multiple shields for a given instruction", replaceWith = ReplaceWith("renderManeuver(maneuver, roadShields)") ) @JvmOverloads fun render(maneuver: SubManeuver?, roadShield: RoadShield? = null) { val roadShields = ifNonNull(roadShield) { - listOf(it) + setOf(it.toRouteShield()) } renderManeuver(maneuver, roadShields) } @@ -76,7 +78,7 @@ class MapboxSubManeuver : AppCompatTextView { * Invoke the method to render sub maneuver instructions * @param maneuver SubManeuver */ - fun renderManeuver(maneuver: SubManeuver?, roadShields: List?) { + fun renderManeuver(maneuver: SubManeuver?, routeShields: Set?) { val exitView = MapboxExitText(context) exitView.setExitStyle(exitBackground, leftDrawable, rightDrawable) val instruction = ManeuverInstructionGenerator.generateSub( @@ -84,7 +86,7 @@ class MapboxSubManeuver : AppCompatTextView { lineHeight, exitView, maneuver, - roadShields + routeShields ) text = instruction } diff --git a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxUpcomingManeuverAdapter.kt b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxUpcomingManeuverAdapter.kt index b07d3467db2..76245c3f169 100644 --- a/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxUpcomingManeuverAdapter.kt +++ b/libnavui-maneuver/src/main/java/com/mapbox/navigation/ui/maneuver/view/MapboxUpcomingManeuverAdapter.kt @@ -18,7 +18,9 @@ import com.mapbox.navigation.ui.maneuver.model.PrimaryManeuver import com.mapbox.navigation.ui.maneuver.model.RoadShield import com.mapbox.navigation.ui.maneuver.model.SecondaryManeuver import com.mapbox.navigation.ui.maneuver.model.StepDistance +import com.mapbox.navigation.ui.maneuver.model.toRouteShield import com.mapbox.navigation.ui.maneuver.view.MapboxUpcomingManeuverAdapter.MapboxUpcomingManeuverViewHolder +import com.mapbox.navigation.ui.shield.model.RouteShield import com.mapbox.navigation.utils.internal.ifNonNull /** @@ -37,7 +39,7 @@ class MapboxUpcomingManeuverAdapter( @StyleRes private var secondaryManeuverAppearance: Int? = null private val inflater = LayoutInflater.from(context) private val upcomingManeuverList = mutableListOf() - private val maneuverShieldMap = mutableMapOf>() + private val routeShields = mutableSetOf() /** * Binds the given View to the position. @@ -78,29 +80,24 @@ class MapboxUpcomingManeuverAdapter( * @param shieldMap Map */ @Deprecated( - message = "The method can only render one shield if an instruction has multiple shields", + message = "The method may or may not render multiple shields for a given instruction", replaceWith = ReplaceWith("updateShields(shields)") ) fun updateRoadShields(shieldMap: Map) { - val shields = hashMapOf>() - shieldMap.forEach { - val value = it.value - shields[it.key] = if (value != null) { - listOf(value) - } else { - listOf() + shieldMap.forEach { entry -> + ifNonNull(entry.value) { value -> + routeShields.add(value.toRouteShield()) } } - updateShields(shields) } /** - * Given a [Map] of id to list of [RoadShield], the function maintains a map of id to list of - * [RoadShield] that is used to render the shield on the view - * @param shields Map> + * Given a set of [RoadShield], the function maintains a set of [RoadShield] that is used to + * render the shield on the view + * @param shields Set */ - fun updateShields(shields: Map>) { - maneuverShieldMap.putAll(shields) + fun updateShields(shields: Set) { + routeShields.addAll(shields) } /** @@ -159,10 +156,8 @@ class MapboxUpcomingManeuverAdapter( val primary = maneuver.primary val secondary = maneuver.secondary val stepDistance = maneuver.stepDistance - val primaryId = primary.id - val secondaryId = secondary?.id - drawPrimaryManeuver(primary, maneuverShieldMap[primaryId]) - drawSecondaryManeuver(secondary, maneuverShieldMap[secondaryId]) + drawPrimaryManeuver(primary, routeShields) + drawSecondaryManeuver(secondary, routeShields) drawTotalStepDistance(stepDistance) updateStepDistanceTextAppearance() updateUpcomingPrimaryManeuverTextAppearance() @@ -187,8 +182,8 @@ class MapboxUpcomingManeuverAdapter( } } - private fun drawPrimaryManeuver(primary: PrimaryManeuver, roadShields: List?) { - viewBinding.primaryManeuverText.renderManeuver(primary, roadShields) + private fun drawPrimaryManeuver(primary: PrimaryManeuver, routeShields: Set?) { + viewBinding.primaryManeuverText.renderManeuver(primary, routeShields) viewBinding.maneuverIcon.renderPrimaryTurnIcon(primary) } @@ -198,11 +193,11 @@ class MapboxUpcomingManeuverAdapter( private fun drawSecondaryManeuver( secondary: SecondaryManeuver?, - roadShields: List? + routeShields: Set? ) { if (secondary != null) { viewBinding.secondaryManeuverText.visibility = VISIBLE - viewBinding.secondaryManeuverText.renderManeuver(secondary, roadShields) + viewBinding.secondaryManeuverText.renderManeuver(secondary, routeShields) updateConstraintsToHaveSecondary() viewBinding.primaryManeuverText.maxLines = 1 } else { diff --git a/libnavui-maneuver/src/test/java/com/mapbox/navigation/ui/maneuver/api/MapboxManeuverApiTest.kt b/libnavui-maneuver/src/test/java/com/mapbox/navigation/ui/maneuver/api/MapboxManeuverApiTest.kt index 47dd54d2241..65c84ce4b78 100644 --- a/libnavui-maneuver/src/test/java/com/mapbox/navigation/ui/maneuver/api/MapboxManeuverApiTest.kt +++ b/libnavui-maneuver/src/test/java/com/mapbox/navigation/ui/maneuver/api/MapboxManeuverApiTest.kt @@ -2,6 +2,7 @@ package com.mapbox.navigation.ui.maneuver.api import com.mapbox.api.directions.v5.models.DirectionsRoute import com.mapbox.api.directions.v5.models.MapboxShield +import com.mapbox.bindgen.Expected import com.mapbox.bindgen.ExpectedFactory import com.mapbox.navigation.base.formatter.DistanceFormatter import com.mapbox.navigation.base.trip.model.RouteProgress @@ -18,6 +19,9 @@ import com.mapbox.navigation.ui.maneuver.model.RoadShieldError import com.mapbox.navigation.ui.shield.api.MapboxRouteShieldApi import com.mapbox.navigation.ui.shield.internal.api.getRouteShieldsFromModels import com.mapbox.navigation.ui.shield.model.RouteShield +import com.mapbox.navigation.ui.shield.model.RouteShieldCallback +import com.mapbox.navigation.ui.shield.model.RouteShieldError +import com.mapbox.navigation.ui.shield.model.RouteShieldResult import io.mockk.coEvery import io.mockk.every import io.mockk.mockk @@ -276,21 +280,17 @@ class MapboxManeuverApiTest { ) ) val maneuverList = listOf(testManeuvers[0]) - val callback: RoadShieldsCallback = mockk(relaxed = true) - val maneuverSlot = slot>() - val shieldSlot = slot>>() - val errorSlot = slot>>() + val callback: RouteShieldCallback = mockk(relaxed = true) + val shieldSlot = slot>>() mapboxManeuverApi.getRoadShields(maneuverList, callback) verify(exactly = 1) { callback.onRoadShields( - capture(maneuverSlot), - capture(shieldSlot), - capture(errorSlot) + capture(shieldSlot) ) } - assertEquals(2, errorSlot.captured["primary_0"]!!.size) + assertEquals(2, shieldSlot.captured.size) unmockkStatic(MapboxRouteShieldApi::getRouteShieldsFromModels) } @@ -379,25 +379,21 @@ class MapboxManeuverApiTest { ) ) val maneuverList = listOf(testManeuvers[1]) - val callback: RoadShieldsCallback = mockk(relaxed = true) - val maneuverSlot = slot>() - val shieldSlot = slot>>() - val errorSlot = slot>>() + val callback: RouteShieldCallback = mockk(relaxed = true) + val shieldSlot = slot>>() mapboxManeuverApi.getRoadShields(userId, styleId, accessToken, maneuverList, callback) verify(exactly = 1) { callback.onRoadShields( - capture(maneuverSlot), - capture(shieldSlot), - capture(errorSlot) + capture(shieldSlot) ) } - assertEquals(2, shieldSlot.captured["primary_1"]!!.size) - assertEquals(mockByteArray, shieldSlot.captured.values.first()[0].shieldIcon) + assertEquals(2, shieldSlot.captured.size) + assertEquals(mockByteArray, shieldSlot.captured[0].value?.shield?.byteArray) assertEquals( "https://shield.mapbox.com/url2", - shieldSlot.captured.values.first()[1].shieldUrl + shieldSlot.captured[1].value?.shield?.url ) unmockkStatic(MapboxRouteShieldApi::getRouteShieldsFromModels) } diff --git a/libnavui-maneuver/src/test/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldGeneratorTest.kt b/libnavui-maneuver/src/test/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldGeneratorTest.kt index 8901e4445d7..de3a44e5fa2 100644 --- a/libnavui-maneuver/src/test/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldGeneratorTest.kt +++ b/libnavui-maneuver/src/test/java/com/mapbox/navigation/ui/maneuver/model/RoadShieldGeneratorTest.kt @@ -3,6 +3,8 @@ package com.mapbox.navigation.ui.maneuver.model import android.content.Context import android.text.style.ImageSpan import androidx.test.core.app.ApplicationProvider +import com.mapbox.navigation.base.ExperimentalMapboxNavigationAPI +import com.mapbox.navigation.ui.shield.model.RouteShieldFactory import org.junit.Assert.assertNotNull import org.junit.Assert.assertTrue import org.junit.Before @@ -10,6 +12,7 @@ import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner +@ExperimentalMapboxNavigationAPI @RunWith(RobolectricTestRunner::class) class RoadShieldGeneratorTest { @@ -93,29 +96,17 @@ class RoadShieldGeneratorTest { val mockShieldText = "880" val mockDesiredHeight = 50 val mockResources = ctx.resources - val mockShieldIcon = byteArrayOf() + val emptyShieldIcon = byteArrayOf() val spannable = RoadShieldGenerator.styleAndGetRoadShield( mockShieldText, mockDesiredHeight, - mockResources - ) - val imageSpan = spannable.getSpans(0, spannable.length, ImageSpan::class.java) - - assertTrue(spannable.isNotEmpty()) - assertTrue(imageSpan.isEmpty()) - } - - @Test - fun `when road shield invalid then spannable has empty image span`() { - val mockShieldText = "880" - val mockDesiredHeight = 50 - val mockResources = ctx.resources - - val spannable = RoadShieldGenerator.styleAndGetRoadShield( - mockShieldText, - mockDesiredHeight, - mockResources + mockResources, + RouteShieldFactory.buildRouteShield( + "https://mapbox_legacy_url.svg", + emptyShieldIcon, + "https://mapbox_legacy_url" + ) ) val imageSpan = spannable.getSpans(0, spannable.length, ImageSpan::class.java) @@ -133,7 +124,11 @@ class RoadShieldGeneratorTest { mockShieldText, mockDesiredHeight, mockResources, - RoadShield("https://this_is_my_url", mockShieldIcon) + RouteShieldFactory.buildRouteShield( + "https://mapbox_legacy_url.svg", + mockShieldIcon, + "https://mapbox_legacy_url" + ) ) val imageSpan = spannable.getSpans(0, spannable.length, ImageSpan::class.java) diff --git a/libnavui-shield/api/current.txt b/libnavui-shield/api/current.txt index 30d21d66286..cfaee2b0d5d 100644 --- a/libnavui-shield/api/current.txt +++ b/libnavui-shield/api/current.txt @@ -34,6 +34,8 @@ package com.mapbox.navigation.ui.shield.model { } public static final class RouteShield.MapboxLegacyShield extends com.mapbox.navigation.ui.shield.model.RouteShield { + method public String getInitialUrl(); + property public final String initialUrl; } public fun interface RouteShieldCallback { @@ -47,6 +49,12 @@ package com.mapbox.navigation.ui.shield.model { property public final String url; } + @com.mapbox.navigation.base.ExperimentalMapboxNavigationAPI public final class RouteShieldFactory { + method public static com.mapbox.navigation.ui.shield.model.RouteShield.MapboxLegacyShield buildRouteShield(String downloadUrl, byte[] byteArray, String initialUrl); + method public static com.mapbox.navigation.ui.shield.model.RouteShield.MapboxDesignedShield buildRouteShield(String downloadUrl, byte[] byteArray, com.mapbox.api.directions.v5.models.MapboxShield mapboxShield, com.mapbox.api.directions.v5.models.ShieldSprite shieldSprite); + field public static final com.mapbox.navigation.ui.shield.model.RouteShieldFactory INSTANCE; + } + public final class RouteShieldOrigin { method public String getOriginalErrorMessage(); method public String getOriginalUrl(); diff --git a/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/ShieldsCache.kt b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/ShieldsCache.kt index 6d47d272bc5..ba141d5f050 100644 --- a/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/ShieldsCache.kt +++ b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/ShieldsCache.kt @@ -188,7 +188,8 @@ internal class ShieldResultCache( return shieldByteArrayCache.getOrRequest(shieldUrl).mapValue { byteArray -> RouteShield.MapboxLegacyShield( toDownload.url, - byteArray + byteArray, + toDownload.initialUrl ) } } diff --git a/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/internal/model/MapboxShieldEx.kt b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/internal/model/MapboxShieldEx.kt new file mode 100644 index 00000000000..db3726f8a6a --- /dev/null +++ b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/internal/model/MapboxShieldEx.kt @@ -0,0 +1,20 @@ +package com.mapbox.navigation.ui.shield.internal.model + +import com.mapbox.api.directions.v5.models.MapboxShield + +private const val MINIMUM_DISPLAY_REF_LENGTH = 2 +private const val MAXIMUM_DISPLAY_REF_LENGTH = 6 + +fun MapboxShield.getRefLen(): Int { + return when { + this.displayRef().length <= 1 -> { + MINIMUM_DISPLAY_REF_LENGTH + } + displayRef().length > 6 -> { + MAXIMUM_DISPLAY_REF_LENGTH + } + else -> { + displayRef().length + } + } +} diff --git a/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/internal/model/RouteShieldToDownload.kt b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/internal/model/RouteShieldToDownload.kt index 227556df970..454190e6c34 100644 --- a/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/internal/model/RouteShieldToDownload.kt +++ b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/internal/model/RouteShieldToDownload.kt @@ -4,8 +4,6 @@ import com.mapbox.api.directions.v5.models.MapboxShield import com.mapbox.api.directions.v5.models.ShieldSprite import com.mapbox.api.directions.v5.models.ShieldSprites -private const val MINIMUM_DISPLAY_REF_LENGTH = 2 -private const val MAXIMUM_DISPLAY_REF_LENGTH = 6 private const val SPRITE = "/sprite" private const val SPRITE_BASE_URL = "https://api.mapbox.com/styles/v1/" private const val SPRITE_JSON = "sprite.json" @@ -27,7 +25,7 @@ sealed class RouteShieldToDownload { .plus("/${this.shieldSpriteToDownload.styleId}") .plus(SPRITE) .plus("/${this.mapboxShield.name()}") - .plus("-${getRefLen(this.mapboxShield.displayRef())}") + .plus("-${this.mapboxShield.getRefLen()}") .plus(REQUEST_ACCESS_TOKEN) .plus(accessToken) } @@ -36,7 +34,7 @@ sealed class RouteShieldToDownload { * @param initialUrl url returned by the Navigation API which misses file extension */ data class MapboxLegacy( - private val initialUrl: String + val initialUrl: String ) : RouteShieldToDownload() { override val url: String = initialUrl.plus(SVG_EXTENSION) @@ -55,26 +53,12 @@ internal fun RouteShieldToDownload.MapboxDesign.generateSpriteSheetUrl(): String internal fun RouteShieldToDownload.MapboxDesign.getSpriteFrom( shieldSprites: ShieldSprites ): ShieldSprite? { - val refLen = getRefLen(this.mapboxShield.displayRef()) + val refLen = this.mapboxShield.getRefLen() return shieldSprites.sprites().find { shieldSprite -> shieldSprite.spriteName() == this.mapboxShield.name().plus("-$refLen") } } -private fun getRefLen(displayRef: String): Int { - return when { - displayRef.length <= 1 -> { - MINIMUM_DISPLAY_REF_LENGTH - } - displayRef.length > 6 -> { - MAXIMUM_DISPLAY_REF_LENGTH - } - else -> { - displayRef.length - } - } -} - data class ShieldSpriteToDownload( val userId: String, val styleId: String, diff --git a/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/model/RouteShield.kt b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/model/RouteShield.kt index ca8bf790d12..3c10e04aedb 100644 --- a/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/model/RouteShield.kt +++ b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/model/RouteShield.kt @@ -21,10 +21,13 @@ sealed class RouteShield( * Type representation of [RouteShield] which is to be used when requesting mapbox legacy shields. * @property url used to download the shield * @property byteArray shield image returned in the form of svg wrapped in a [ByteArray] + * @property initialUrl the original legacy url obtained from directions response pointing to + * a shield. This initial url can be used to match the url from the maneuver object. */ class MapboxLegacyShield internal constructor( url: String, byteArray: ByteArray, + val initialUrl: String, ) : RouteShield(url = url, byteArray = byteArray) { /** @@ -38,6 +41,7 @@ sealed class RouteShield( if (url != other.url) return false if (!byteArray.contentEquals(other.byteArray)) return false + if (initialUrl != other.initialUrl) return false return true } @@ -48,6 +52,7 @@ sealed class RouteShield( override fun hashCode(): Int { var result = url.hashCode() result = 31 * result + byteArray.contentHashCode() + result = 31 * result + initialUrl.hashCode() return result } @@ -57,7 +62,8 @@ sealed class RouteShield( override fun toString(): String { return "MapboxLegacyShield(" + "url='$url', " + - "byteArray=${byteArray.contentToString()}" + + "byteArray=${byteArray.contentToString()}, " + + "initialUrl=$initialUrl" + ")" } } diff --git a/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/model/RouteShieldFactory.kt b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/model/RouteShieldFactory.kt new file mode 100644 index 00000000000..5bc2bacf83e --- /dev/null +++ b/libnavui-shield/src/main/java/com/mapbox/navigation/ui/shield/model/RouteShieldFactory.kt @@ -0,0 +1,35 @@ +package com.mapbox.navigation.ui.shield.model + +import com.mapbox.api.directions.v5.models.MapboxShield +import com.mapbox.api.directions.v5.models.ShieldSprite +import com.mapbox.navigation.base.ExperimentalMapboxNavigationAPI + +/** + * A factory exposed to build a [RouteShield] object. + */ +@ExperimentalMapboxNavigationAPI +object RouteShieldFactory { + + /** + * Build [RouteShield.MapboxLegacyShield] given appropriate arguments. + */ + @JvmStatic + fun buildRouteShield( + downloadUrl: String, + byteArray: ByteArray, + initialUrl: String + ): RouteShield.MapboxLegacyShield = + RouteShield.MapboxLegacyShield(downloadUrl, byteArray, initialUrl) + + /** + * Build [RouteShield.MapboxDesignedShield] given appropriate arguments. + */ + @JvmStatic + fun buildRouteShield( + downloadUrl: String, + byteArray: ByteArray, + mapboxShield: MapboxShield, + shieldSprite: ShieldSprite + ): RouteShield.MapboxDesignedShield = + RouteShield.MapboxDesignedShield(downloadUrl, byteArray, mapboxShield, shieldSprite) +} diff --git a/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/RoadShieldContentManagerImplTest.kt b/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/RoadShieldContentManagerImplTest.kt index 340c0eb7f95..25aa61d4c55 100644 --- a/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/RoadShieldContentManagerImplTest.kt +++ b/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/RoadShieldContentManagerImplTest.kt @@ -33,19 +33,22 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val legacyShieldUrl = "url_legacy" + val legacyUrl = "url_legacy" + val downloadUrl = legacyUrl.plus(".svg") val toDownloadLegacy = mockk { - every { url } returns legacyShieldUrl + every { url } returns downloadUrl + every { initialUrl } returns legacyUrl } val expectedLegacyShield = RouteShield.MapboxLegacyShield( - legacyShieldUrl, - byteArrayOf() + downloadUrl, + byteArrayOf(), + legacyUrl ) val expectedLegacyResult = RouteShieldResult( expectedLegacyShield, RouteShieldOrigin( isFallback = false, - originalUrl = legacyShieldUrl, + originalUrl = downloadUrl, originalErrorMessage = "" ) ) @@ -97,19 +100,22 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val legacyShieldUrl = "url_legacy" + val legacyUrl = "url_legacy" + val downloadUrl = legacyUrl.plus(".svg") val toDownloadLegacy = mockk { - every { url } returns legacyShieldUrl + every { url } returns downloadUrl + every { initialUrl } returns legacyUrl } val expectedLegacyShield = RouteShield.MapboxLegacyShield( - legacyShieldUrl, - byteArrayOf() + url = downloadUrl, + byteArray = byteArrayOf(), + initialUrl = legacyUrl ) val expectedLegacyResult = RouteShieldResult( expectedLegacyShield, RouteShieldOrigin( isFallback = false, - originalUrl = legacyShieldUrl, + originalUrl = downloadUrl, originalErrorMessage = "" ) ) @@ -158,19 +164,22 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val legacyShieldUrl = "url_legacy" + val legacyUrl = "url_legacy" + val downloadUrl = legacyUrl.plus(".svg") val toDownloadLegacy = mockk { - every { url } returns legacyShieldUrl + every { url } returns downloadUrl + every { initialUrl } returns legacyUrl } val expectedLegacyShield = RouteShield.MapboxLegacyShield( - legacyShieldUrl, - byteArrayOf() + downloadUrl, + byteArrayOf(), + legacyUrl ) val expectedLegacyResult = RouteShieldResult( expectedLegacyShield, RouteShieldOrigin( isFallback = false, - originalUrl = legacyShieldUrl, + originalUrl = downloadUrl, originalErrorMessage = "" ) ) @@ -211,19 +220,22 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val legacyShieldUrl = "url_legacy" + val legacyUrl = "url_legacy" + val downloadUrl = legacyUrl.plus(".svg") val toDownloadLegacy = mockk { - every { url } returns legacyShieldUrl + every { url } returns downloadUrl + every { initialUrl } returns legacyUrl } val expectedLegacyShield = RouteShield.MapboxLegacyShield( - legacyShieldUrl, - byteArrayOf() + downloadUrl, + byteArrayOf(), + legacyUrl ) val expectedLegacyResult = RouteShieldResult( expectedLegacyShield, RouteShieldOrigin( isFallback = false, - originalUrl = legacyShieldUrl, + originalUrl = downloadUrl, originalErrorMessage = "" ) ) @@ -256,12 +268,20 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val shieldUrl = "url" - val legacyShieldUrl = "legacy_url" + val initialUrl = "url_legacy" + val downloadUrl = initialUrl.plus(".svg") val expectedLegacyShield = RouteShield.MapboxLegacyShield( - shieldUrl, - byteArrayOf() + downloadUrl, + byteArrayOf(), + initialUrl ) + val legacyToDownload = RouteShieldToDownload.MapboxLegacy(initialUrl) + val shieldUrl = "url_design" + val toDownload = mockk { + every { url } returns shieldUrl + every { legacyFallback } returns legacyToDownload + } + val expectedResult = RouteShieldResult( expectedLegacyShield, RouteShieldOrigin( @@ -270,12 +290,6 @@ class RoadShieldContentManagerImplTest { originalErrorMessage = "error" ) ) - val legacyToDownload = RouteShieldToDownload.MapboxLegacy(legacyShieldUrl) - val toDownload = mockk { - every { url } returns shieldUrl - every { legacyFallback } returns legacyToDownload - } - coEvery { cache.getOrRequest(toDownload) } returns ExpectedFactory.createError("error") @@ -294,26 +308,25 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val shieldUrl = "url" - val legacyShieldUrl = "legacy_url" + val legacyUrl = "url_legacy" + val legacyToDownload = RouteShieldToDownload.MapboxLegacy(legacyUrl) + val shieldUrl = "url_design" + val toDownload = mockk { + every { url } returns shieldUrl + every { legacyFallback } returns legacyToDownload + } val expectedResult = RouteShieldError( shieldUrl, """ |original request failed with: - |url: url + |url: url_design |error: error | |fallback request failed with: - |url: legacy_url.svg + |url: url_legacy.svg |error: error_legacy """.trimMargin() ) - val legacyToDownload = RouteShieldToDownload.MapboxLegacy(legacyShieldUrl) - val toDownload = mockk { - every { url } returns shieldUrl - every { legacyFallback } returns legacyToDownload - } - coEvery { cache.getOrRequest(toDownload) } returns ExpectedFactory.createError("error") @@ -331,7 +344,7 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val shieldUrl = "url" + val shieldUrl = "url_design" val toDownload = mockk { every { url } returns shieldUrl every { legacyFallback } returns null @@ -355,7 +368,7 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val shieldUrl = "url" + val shieldUrl = "url_design" val mapboxShield = mockk() val sprite = mockk() val toDownload = mockk { @@ -390,11 +403,13 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val shieldUrl = "url" - val toDownload = RouteShieldToDownload.MapboxLegacy(shieldUrl) + val initialUrl = "url_legacy" + val downloadUrl = initialUrl.plus(".svg") + val toDownload = RouteShieldToDownload.MapboxLegacy(initialUrl) val expectedShield = RouteShield.MapboxLegacyShield( - shieldUrl, - byteArrayOf() + downloadUrl, + byteArrayOf(), + initialUrl ) val expectedResult = RouteShieldResult( expectedShield, @@ -419,8 +434,8 @@ class RoadShieldContentManagerImplTest { val cache = mockk() val contentManager = createContentManager(cache) - val shieldUrl = "url" - val toDownload = RouteShieldToDownload.MapboxLegacy(shieldUrl) + val initialUrl = "url_legacy" + val toDownload = RouteShieldToDownload.MapboxLegacy(initialUrl) val expectedResult = RouteShieldError( toDownload.url, "error" diff --git a/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/ShieldResultCacheTest.kt b/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/ShieldResultCacheTest.kt index a33e9032432..a1b3205e8f3 100644 --- a/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/ShieldResultCacheTest.kt +++ b/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/ShieldResultCacheTest.kt @@ -185,7 +185,7 @@ class ShieldResultCacheTest { val shieldByteArray = byteArrayOf() val shieldUrl = "shield-url" val toDownload = mockk { - every { url } returns shieldUrl + every { initialUrl } returns shieldUrl every { url } returns shieldUrl.plus(".svg") } coEvery { @@ -194,7 +194,8 @@ class ShieldResultCacheTest { val expected = RouteShield.MapboxLegacyShield( url = toDownload.url, - byteArray = shieldByteArray + byteArray = shieldByteArray, + initialUrl = shieldUrl ) val result = cache.getOrRequest(toDownload) diff --git a/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/api/MapboxRouteShieldApiTest.kt b/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/api/MapboxRouteShieldApiTest.kt index b8a7f738df0..ab3a2b41979 100644 --- a/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/api/MapboxRouteShieldApiTest.kt +++ b/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/api/MapboxRouteShieldApiTest.kt @@ -70,16 +70,19 @@ class MapboxRouteShieldApiTest { coroutineRule.runBlockingTest { val bannerComponents: BannerComponents = mockk(relaxed = true) val bannerComponentsList = listOf(bannerComponents) + val initialUrl1 = "https://shield.mapbox.com/url1" + val toDownloadUrl1 = "https://shield.mapbox.com/url1.svg" val mockResultList = listOf>( ExpectedFactory.createValue( RouteShieldResult( shield = RouteShield.MapboxLegacyShield( - url = "https://shield.mapbox.com/url1", - byteArray = byteArrayOf(1) + url = toDownloadUrl1, + byteArray = byteArrayOf(1), + initialUrl = initialUrl1 ), origin = RouteShieldOrigin( isFallback = false, - originalUrl = "https://shield.mapbox.com/url1", + originalUrl = toDownloadUrl1, originalErrorMessage = "" ) ) @@ -95,6 +98,7 @@ class MapboxRouteShieldApiTest { callback.onRoadShields(capture(shieldSlot)) } assertEquals(mockResultList.size, shieldSlot.captured.size) + assertEquals(mockResultList[0].value, shieldSlot.captured[0].value) } @Test @@ -102,23 +106,26 @@ class MapboxRouteShieldApiTest { coroutineRule.runBlockingTest { val bannerComponents: BannerComponents = mockk(relaxed = true) val bannerComponentsList = listOf(bannerComponents) + val initialUrl1 = "https://shield.mapbox.com/url1" + val toDownloadUrl1 = "https://shield.mapbox.com/url1.svg" val mockResultList = listOf>( ExpectedFactory.createValue( RouteShieldResult( shield = RouteShield.MapboxLegacyShield( - url = "https://shield.mapbox.com/url1", - byteArray = byteArrayOf(1) + url = toDownloadUrl1, + byteArray = byteArrayOf(1), + initialUrl = initialUrl1 ), origin = RouteShieldOrigin( isFallback = false, - originalUrl = "https://shield.mapbox.com/url1", + originalUrl = toDownloadUrl1, originalErrorMessage = "" ) ) ), ExpectedFactory.createError( RouteShieldError( - url = "https://shield.mapbox.com/url2", + url = "https://shield.mapbox.com/url2.svg", errorMessage = "whatever" ) ) @@ -135,6 +142,8 @@ class MapboxRouteShieldApiTest { assertEquals(mockResultList.size, shieldSlot.captured.size) assertNotNull(shieldSlot.captured[0].value) assertNotNull(shieldSlot.captured[1].error) + assertEquals(mockResultList[0].value, shieldSlot.captured[0].value) + assertEquals(mockResultList[1].error, shieldSlot.captured[1].error) } @Test @@ -142,16 +151,18 @@ class MapboxRouteShieldApiTest { coroutineRule.runBlockingTest { val bannerComponents: BannerComponents = mockk(relaxed = true) val bannerComponentsList = listOf(bannerComponents) + val initialUrl1 = "https://shield.mapbox.com/url1" + val initialUrl2 = "https://shield.mapbox.com/url2" val mockResultList = listOf>( ExpectedFactory.createError( RouteShieldError( - url = "https://shield.mapbox.com/url1", + url = initialUrl1.plus(".svg"), errorMessage = "whatever" ) ), ExpectedFactory.createError( RouteShieldError( - url = "https://shield.mapbox.com/url2", + url = initialUrl2.plus(".svg"), errorMessage = "whatever" ) ) @@ -166,6 +177,8 @@ class MapboxRouteShieldApiTest { callback.onRoadShields(capture(shieldSlot)) } assertEquals(mockResultList.size, shieldSlot.captured.size) + assertEquals(mockResultList[0].error, shieldSlot.captured[0].error) + assertEquals(mockResultList[1].error, shieldSlot.captured[1].error) } @Test @@ -209,6 +222,7 @@ class MapboxRouteShieldApiTest { callback.onRoadShields(capture(shieldSlot)) } assertEquals(mockResultList.size, shieldSlot.captured.size) + assertEquals(mockResultList[0].value, shieldSlot.captured[0].value) } @Test @@ -260,6 +274,8 @@ class MapboxRouteShieldApiTest { assertEquals(mockResultList.size, shieldSlot.captured.size) assertNotNull(shieldSlot.captured[0].value) assertNotNull(shieldSlot.captured[1].error) + assertEquals(mockResultList[0].value, shieldSlot.captured[0].value) + assertEquals(mockResultList[1].error, shieldSlot.captured[1].error) } @Test @@ -300,6 +316,8 @@ class MapboxRouteShieldApiTest { callback.onRoadShields(capture(shieldSlot)) } assertEquals(mockResultList.size, shieldSlot.captured.size) + assertEquals(mockResultList[0].error, shieldSlot.captured[0].error) + assertEquals(mockResultList[1].error, shieldSlot.captured[1].error) } @Test @@ -310,6 +328,8 @@ class MapboxRouteShieldApiTest { val accessToken = "pk.123" val bannerComponents: BannerComponents = mockk(relaxed = true) val bannerComponentsList = listOf(bannerComponents) + val initialUrl1 = "https://shield.mapbox.com/legacy/url1" + val toDownloadUrl1 = "https://shield.mapbox.com/legacy/url1.svg" val mockResultList = listOf>( ExpectedFactory.createValue( RouteShieldResult( @@ -329,13 +349,14 @@ class MapboxRouteShieldApiTest { ExpectedFactory.createValue( RouteShieldResult( shield = RouteShield.MapboxLegacyShield( - url = "https://shield.mapbox.com/legacy/url1", - byteArray = byteArrayOf(1) + url = toDownloadUrl1, + byteArray = byteArrayOf(1), + initialUrl = initialUrl1 ), origin = RouteShieldOrigin( isFallback = true, - originalUrl = "https://shield.mapbox.com/url1", - originalErrorMessage = "placeholder was empty" + originalUrl = toDownloadUrl1, + originalErrorMessage = "" ) ) ) @@ -358,6 +379,8 @@ class MapboxRouteShieldApiTest { assertEquals(mockResultList.size, shieldSlot.captured.size) assertFalse(shieldSlot.captured[0].value!!.origin.isFallback) assertTrue(shieldSlot.captured[1].value!!.origin.isFallback) + assertEquals(mockResultList[0].value, shieldSlot.captured[0].value) + assertEquals(mockResultList[1].value, shieldSlot.captured[1].value) } @Test @@ -407,7 +430,7 @@ class MapboxRouteShieldApiTest { shield = RouteShield.MapboxDesignedShield( url = "https://shields.mapbox.com/mapbox/designed/using/road/2", byteArray = byteArrayOf(1), - mapboxShield = mockMapboxShield1, + mapboxShield = mockMapboxShield2, shieldSprite = mockk() ), origin = RouteShieldOrigin( @@ -435,32 +458,31 @@ class MapboxRouteShieldApiTest { } assertEquals(mockResultList.size, shieldSlot.captured.size) assertFalse(shieldSlot.captured[0].value!!.origin.isFallback) - assertEquals( - mockResultList[1].value!!.shield.url, - shieldSlot.captured[1].value!!.shield.url - ) + assertEquals(mockResultList[1].value, shieldSlot.captured[1].value) } @Test fun `when get legacy shields using road with all shields`() = coroutineRule.runBlockingTest { - val mockLegacyUrl1 = "https://shields.mapbox.com/mapbox/legacy/using/road/1" + val initialUrl1 = "https://shields.mapbox.com/mapbox/legacy/using/road/1" + val toDownloadUrl1 = "https://shields.mapbox.com/mapbox/legacy/using/road/1.svg" val road = mockk { every { name } returns "Central Av" every { shieldName } returns "I 880" - every { shieldUrl } returns mockLegacyUrl1 + every { shieldUrl } returns initialUrl1 every { mapboxShield } returns null } val mockResultList = listOf>( ExpectedFactory.createValue( RouteShieldResult( shield = RouteShield.MapboxLegacyShield( - url = "https://shields.mapbox.com/mapbox/legacy/using/road/1", - byteArray = byteArrayOf(1) + url = toDownloadUrl1, + byteArray = byteArrayOf(1), + initialUrl = initialUrl1 ), origin = RouteShieldOrigin( isFallback = false, - originalUrl = "https://shields.mapbox.com/mapbox/designed/using/road/1", + originalUrl = toDownloadUrl1, originalErrorMessage = "" ) ) @@ -480,10 +502,7 @@ class MapboxRouteShieldApiTest { } assertEquals(mockResultList.size, shieldSlot.captured.size) assertFalse(shieldSlot.captured[0].value!!.origin.isFallback) - assertEquals( - mockResultList[0].value!!.shield.url, - shieldSlot.captured[0].value!!.shield.url - ) + assertEquals(mockResultList[0].value, shieldSlot.captured[0].value) } @Test @@ -499,23 +518,25 @@ class MapboxRouteShieldApiTest { .textColor("black") .displayRef("880") .build() - val mockLegacyUrl1 = "https://shields.mapbox.com/mapbox/legacy/using/road/1" + val initialUrl1 = "https://shields.mapbox.com/mapbox/legacy/using/road/1" + val toDownloadUrl1 = "https://shields.mapbox.com/mapbox/legacy/using/road/1.svg" val road = mockk { every { name } returns "Central Av" every { shieldName } returns "I 880" - every { shieldUrl } returns mockLegacyUrl1 + every { shieldUrl } returns initialUrl1 every { mapboxShield } returns listOf(mockMapboxShield1) } val mockResultList = listOf>( ExpectedFactory.createValue( RouteShieldResult( shield = RouteShield.MapboxLegacyShield( - url = "https://shields.mapbox.com/mapbox/legacy/using/road/1", - byteArray = byteArrayOf(1) + url = toDownloadUrl1, + byteArray = byteArrayOf(1), + initialUrl = initialUrl1 ), origin = RouteShieldOrigin( isFallback = false, - originalUrl = "https://shields.mapbox.com/mapbox/designed/using/road/1", + originalUrl = toDownloadUrl1, originalErrorMessage = "" ) ) @@ -552,13 +573,7 @@ class MapboxRouteShieldApiTest { callback.onRoadShields(capture(shieldSlot)) } assertEquals(mockResultList.size, shieldSlot.captured.size) - assertEquals( - mockResultList[0].value!!.shield.url, - shieldSlot.captured[0].value!!.shield.url - ) - assertEquals( - mockResultList[1].value!!.shield.url, - shieldSlot.captured[1].value!!.shield.url - ) + assertEquals(mockResultList[0].value, shieldSlot.captured[0].value) + assertEquals(mockResultList[1].value, shieldSlot.captured[1].value) } } diff --git a/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/api/RouteShieldExTest.kt b/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/api/RouteShieldExTest.kt index a4a88094d5b..50100440d62 100644 --- a/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/api/RouteShieldExTest.kt +++ b/libnavui-shield/src/test/java/com/mapbox/navigation/ui/shield/api/RouteShieldExTest.kt @@ -32,7 +32,7 @@ class RouteShieldExTest { @Test fun `when get mapbox designed shield with default height`() { mockkObject(SvgUtil) - val mockBitmap = mockk() { + val mockBitmap = mockk { every { width } returns 72 every { height } returns 24 } @@ -40,7 +40,7 @@ class RouteShieldExTest { url = "https://shield.mapbox.designed", byteArrayOf(1), mockk(), - mockk() { + mockk { every { spriteAttributes().width() } returns 72 every { spriteAttributes().height() } returns 24 } @@ -65,7 +65,7 @@ class RouteShieldExTest { url = "https://shield.mapbox.designed", byteArrayOf(1), mockk(), - mockk() { + mockk { every { spriteAttributes().width() } returns 72 every { spriteAttributes().height() } returns 24 } @@ -87,8 +87,9 @@ class RouteShieldExTest { every { height } returns 36 } val mapboxDesignedShield = RouteShield.MapboxLegacyShield( - url = "https://shield.mapbox.legacy", - byteArrayOf(1) + url = "https://shield.mapbox.legacy.svg", + byteArrayOf(1), + initialUrl = "https://shield.mapbox.legacy", ) every { SvgUtil.renderAsBitmapWithHeight(any(), any(), any()) } returns mockBitmap @@ -107,8 +108,9 @@ class RouteShieldExTest { every { height } returns 333 } val mapboxDesignedShield = RouteShield.MapboxLegacyShield( - url = "https://shield.mapbox.legacy", - byteArrayOf(1) + url = "https://shield.mapbox.legacy.svg", + byteArrayOf(1), + initialUrl = "https://shield.mapbox.legacy", ) every { SvgUtil.renderAsBitmapWithHeight(any(), any(), any()) } returns mockBitmap