Skip to content

[Android Auto] Allow for downstream upgrades #1683

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 23, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@
import com.mapbox.maps.MapboxExperimental;
import com.mapbox.maps.ResourceOptions;
import com.mapbox.maps.ScreenCoordinate;
import com.mapbox.maps.extension.androidauto.CarMapSurfaceOwner;
import com.mapbox.maps.extension.androidauto.MapboxCarMap;
import com.mapbox.maps.extension.androidauto.DefaultMapboxCarMapGestureHandler;
import com.mapbox.maps.extension.androidauto.MapboxCarMapEx;
@@ -24,8 +25,13 @@
@MapboxExperimental
class CarJavaInterfaceChecker {

void constructors(MapInitOptions mapInitOptions) {
MapboxCarMap mapboxCarMap = new MapboxCarMap();
void constructorMapboxCarMap(MapInitOptions mapInitOptions) {
new MapboxCarMap();
}

void constructorsCarMapSurfaceOwner(MapboxCarMapGestureHandler gestures) {
new CarMapSurfaceOwner();
new CarMapSurfaceOwner(gestures);
}

void getters(MapboxCarMap mapboxCarMap) {
1 change: 1 addition & 0 deletions extension-androidauto/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@ Mapbox welcomes participation and contributions from everyone.
* Change `MapboxCarMapObserver` to an java interface so default methods can be added without breaking java backwards compatibility. ([#1670](https://github.com/mapbox/mapbox-maps-android/pull/1648))
* Change `MapboxCarMap#getEdgeInsets()` to `mapboxCarMap.getVisibleEdgeInsets()` with the addition of `mapboxCarMap.getStableEdgeInsets()`. ([#1670](https://github.com/mapbox/mapbox-maps-android/pull/1648))
* Add `MapboxCarMapObserver#onStableAreaChanged` to support all the available functions from the SurfaceCallback. ([#1670](https://github.com/mapbox/mapbox-maps-android/pull/1648))
* Add support for intercepting the `SurfaceCallback#onClick` when using `MapboxCarMap.prepareSurfaceCallback`. ([#1683](https://github.com/mapbox/mapbox-maps-android/pull/1683)])

## Bug fixes 🐞

7 changes: 4 additions & 3 deletions extension-androidauto/api/extension-androidauto.api
Original file line number Diff line number Diff line change
@@ -7,18 +7,19 @@ public class com/mapbox/maps/extension/androidauto/DefaultMapboxCarMapGestureHan

public final class com/mapbox/maps/extension/androidauto/MapboxCarMap {
public fun <init> ()V
public final fun clearObservers ()V
public final fun clearObservers ()Lcom/mapbox/maps/extension/androidauto/MapboxCarMap;
public final fun getCarContext ()Landroidx/car/app/CarContext;
public final fun getCarMapSurface ()Lcom/mapbox/maps/extension/androidauto/MapboxCarMapSurface;
public final fun getMapInitOptions ()Lcom/mapbox/maps/MapInitOptions;
public final fun getStableArea ()Landroid/graphics/Rect;
public final fun getStableEdgeInsets ()Lcom/mapbox/maps/EdgeInsets;
public final fun getVisibleArea ()Landroid/graphics/Rect;
public final fun getVisibleEdgeInsets ()Lcom/mapbox/maps/EdgeInsets;
public final fun prepareSurfaceCallback (Landroidx/car/app/CarContext;Lcom/mapbox/maps/MapInitOptions;)Landroidx/car/app/SurfaceCallback;
public final fun registerObserver (Lcom/mapbox/maps/extension/androidauto/MapboxCarMapObserver;)Lcom/mapbox/maps/extension/androidauto/MapboxCarMap;
public final fun setGestureHandler (Lcom/mapbox/maps/extension/androidauto/MapboxCarMapGestureHandler;)V
public final fun setGestureHandler (Lcom/mapbox/maps/extension/androidauto/MapboxCarMapGestureHandler;)Lcom/mapbox/maps/extension/androidauto/MapboxCarMap;
public final fun setup (Landroidx/car/app/CarContext;Lcom/mapbox/maps/MapInitOptions;)Lcom/mapbox/maps/extension/androidauto/MapboxCarMap;
public final fun unregisterObserver (Lcom/mapbox/maps/extension/androidauto/MapboxCarMapObserver;)V
public final fun unregisterObserver (Lcom/mapbox/maps/extension/androidauto/MapboxCarMapObserver;)Lcom/mapbox/maps/extension/androidauto/MapboxCarMap;
}

public final class com/mapbox/maps/extension/androidauto/MapboxCarMapEx {
7 changes: 4 additions & 3 deletions extension-androidauto/api/metalava.txt
Original file line number Diff line number Diff line change
@@ -7,18 +7,19 @@ package com.mapbox.maps.extension.androidauto {

@com.mapbox.maps.MapboxExperimental public final class MapboxCarMap {
ctor public MapboxCarMap();
method public void clearObservers();
method public com.mapbox.maps.extension.androidauto.MapboxCarMap clearObservers();
method public androidx.car.app.CarContext getCarContext();
method public com.mapbox.maps.extension.androidauto.MapboxCarMapSurface? getCarMapSurface();
method public com.mapbox.maps.MapInitOptions getMapInitOptions();
method public android.graphics.Rect? getStableArea();
method public com.mapbox.maps.EdgeInsets? getStableEdgeInsets();
method public android.graphics.Rect? getVisibleArea();
method public com.mapbox.maps.EdgeInsets? getVisibleEdgeInsets();
method @com.mapbox.maps.MapboxExperimental public androidx.car.app.SurfaceCallback prepareSurfaceCallback(androidx.car.app.CarContext carContext, com.mapbox.maps.MapInitOptions mapInitOptions);
method public com.mapbox.maps.extension.androidauto.MapboxCarMap registerObserver(com.mapbox.maps.extension.androidauto.MapboxCarMapObserver mapboxCarMapObserver);
method public void setGestureHandler(com.mapbox.maps.extension.androidauto.MapboxCarMapGestureHandler? gestureHandler);
method public com.mapbox.maps.extension.androidauto.MapboxCarMap setGestureHandler(com.mapbox.maps.extension.androidauto.MapboxCarMapGestureHandler? gestureHandler);
method public com.mapbox.maps.extension.androidauto.MapboxCarMap setup(androidx.car.app.CarContext carContext, com.mapbox.maps.MapInitOptions mapInitOptions);
method public void unregisterObserver(com.mapbox.maps.extension.androidauto.MapboxCarMapObserver mapboxCarMapObserver);
method public com.mapbox.maps.extension.androidauto.MapboxCarMap unregisterObserver(com.mapbox.maps.extension.androidauto.MapboxCarMapObserver mapboxCarMapObserver);
property public final androidx.car.app.CarContext carContext;
property public final com.mapbox.maps.extension.androidauto.MapboxCarMapSurface? carMapSurface;
property public final com.mapbox.maps.MapInitOptions mapInitOptions;
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import androidx.car.app.SurfaceCallback
import androidx.car.app.SurfaceContainer
import com.mapbox.maps.EdgeInsets
import com.mapbox.maps.MapInitOptions
import com.mapbox.maps.MapSurface
import com.mapbox.maps.MapboxExperimental
import com.mapbox.maps.ScreenCoordinate
import com.mapbox.maps.logI
@@ -17,8 +18,8 @@ import java.util.concurrent.CopyOnWriteArraySet
* Maintains the surface state for [MapboxCarMap].
*/
@MapboxExperimental
internal class CarMapSurfaceOwner(
var gestureHandler: MapboxCarMapGestureHandler? = DefaultMapboxCarMapGestureHandler()
internal class CarMapSurfaceOwner @JvmOverloads constructor(
internal var gestureHandler: MapboxCarMapGestureHandler? = DefaultMapboxCarMapGestureHandler()
) : SurfaceCallback {

internal var mapboxCarMapSurface: MapboxCarMapSurface? = null
@@ -38,14 +39,14 @@ internal class CarMapSurfaceOwner(

private val carMapObservers = CopyOnWriteArraySet<MapboxCarMapObserver>()

fun setup(carContext: CarContext, mapInitOptions: MapInitOptions) = apply {
internal fun setup(carContext: CarContext, mapInitOptions: MapInitOptions) = apply {
this.carContext = carContext
this.mapInitOptions = mapInitOptions
}

fun isSetup(): Boolean = this::carContext.isInitialized && this::mapInitOptions.isInitialized
internal fun isSetup(): Boolean = this::carContext.isInitialized && this::mapInitOptions.isInitialized

fun registerObserver(mapboxCarMapObserver: MapboxCarMapObserver) {
internal fun registerObserver(mapboxCarMapObserver: MapboxCarMapObserver) {
carMapObservers.add(mapboxCarMapObserver)
logI(TAG, "registerObserver + 1 = ${carMapObservers.size}")

@@ -62,17 +63,23 @@ internal class CarMapSurfaceOwner(
}
}

fun unregisterObserver(mapboxCarMapObserver: MapboxCarMapObserver) {
internal fun unregisterObserver(mapboxCarMapObserver: MapboxCarMapObserver) {
carMapObservers.remove(mapboxCarMapObserver)
mapboxCarMapSurface?.let { mapboxCarMapObserver.onDetached(it) }
logI(TAG, "unregisterObserver - 1 = ${carMapObservers.size}")
}

fun clearObservers() {
internal fun clearObservers() {
this.mapboxCarMapSurface?.let { surface -> carMapObservers.forEach { it.onDetached(surface) } }
carMapObservers.clear()
}

/**
* Prepares the [MapSurface] and notifies any registered observers that the map has been attached
* with [MapboxCarMapObserver.onAttached].
*
* @see SurfaceCallback.onSurfaceAvailable
*/
override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) {
logI(TAG, "onSurfaceAvailable $surfaceContainer")
surfaceContainer.surface?.let { surface ->
@@ -101,6 +108,12 @@ internal class CarMapSurfaceOwner(
}
}

/**
* Destroys the resources used by [MapSurface] and notifies any registered observers that the map
* has been detached with [MapboxCarMapObserver.onDetached].
*
* @see SurfaceCallback.onSurfaceDestroyed
*/
override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) {
logI(TAG, "onSurfaceDestroyed")
val detachSurface = this.mapboxCarMapSurface
@@ -111,6 +124,12 @@ internal class CarMapSurfaceOwner(
detachSurface?.let { carMapObservers.forEach { it.onDetached(detachSurface) } }
}

/**
* Notifies any registered observers that the visible area has changed with
* [MapboxCarMapObserver.onVisibleAreaChanged].
*
* @see SurfaceCallback.onVisibleAreaChanged
*/
override fun onVisibleAreaChanged(visibleArea: Rect) {
logI(TAG, "onVisibleAreaChanged visibleArea:$visibleArea")
this.visibleArea = visibleArea
@@ -128,6 +147,12 @@ internal class CarMapSurfaceOwner(
}
}

/**
* Notifies any registered observers that the visible area has changed with
* [MapboxCarMapObserver.onStableAreaChanged].
*
* @see SurfaceCallback.onStableAreaChanged
*/
override fun onStableAreaChanged(stableArea: Rect) {
logI(TAG, "onStableAreaChanged stableArea:$stableArea")
this.stableEdgeInsets = stableArea.edgeInsets()
@@ -140,18 +165,33 @@ internal class CarMapSurfaceOwner(
}
}

/**
* Forwards the gesture to the [MapboxCarMapGestureHandler.onScroll].
*
* @see SurfaceCallback.onScroll
*/
override fun onScroll(distanceX: Float, distanceY: Float) {
logI(TAG, "onScroll $distanceX, $distanceY")
val carMapSurface = mapboxCarMapSurface ?: return
gestureHandler?.onScroll(carMapSurface, visibleCenter, distanceX, distanceY)
}

/**
* Forwards the gesture to the [MapboxCarMapGestureHandler.onFling].
*
* @see SurfaceCallback.onFling
*/
override fun onFling(velocityX: Float, velocityY: Float) {
logI(TAG, "onFling $velocityX, $velocityY")
val carMapSurface = mapboxCarMapSurface ?: return
gestureHandler?.onFling(carMapSurface, velocityX, velocityY)
}

/**
* Forwards the gesture to the [MapboxCarMapGestureHandler.onScale].
*
* @see SurfaceCallback.onScale
*/
override fun onScale(focusX: Float, focusY: Float, scaleFactor: Float) {
logI(TAG, "onScroll $focusX, $focusY, $scaleFactor")
val carMapSurface = mapboxCarMapSurface ?: return
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import android.graphics.Rect
import androidx.car.app.AppManager
import androidx.car.app.CarContext
import androidx.car.app.Session
import androidx.car.app.SurfaceCallback
import androidx.lifecycle.Lifecycle
import com.mapbox.maps.EdgeInsets
import com.mapbox.maps.MapInitOptions
@@ -71,11 +72,29 @@ class MapboxCarMap {
carContext: CarContext,
mapInitOptions: MapInitOptions,
) = apply {
val surfaceCallback = prepareSurfaceCallback(carContext, mapInitOptions)
carContext.getCarService(AppManager::class.java).setSurfaceCallback(surfaceCallback)
}

/**
* Instead of using [setup], this function allows you to create your own [SurfaceCallback] and
* forward the calls to the returned [SurfaceCallback]. This makes it possible for you to adopt
* new api versions or intercept the calls and continue to use the [MapboxCarMap] as designed.
*
* This may be a temporary solution, while androidx.car.app:app:1.3.0 is rolling out
* [SurfaceCallback.onClick]. If there is no use for this function in the future, it will be
* removed.
*/
@MapboxExperimental
fun prepareSurfaceCallback(
carContext: CarContext,
mapInitOptions: MapInitOptions
): SurfaceCallback {
check(mapInitOptions.context is CarContext) {
"You must setup the MapboxCarMap MapInitOptions with a CarContext"
"You must set up the MapboxCarMap MapInitOptions with a CarContext"
}
carMapSurfaceOwner.setup(carContext, mapInitOptions)
carContext.getCarService(AppManager::class.java).setSurfaceCallback(carMapSurfaceOwner)
return carMapSurfaceOwner
}

/**
@@ -123,14 +142,14 @@ class MapboxCarMap {
/**
* @param mapboxCarMapObserver the instance used in [registerObserver]
*/
fun unregisterObserver(mapboxCarMapObserver: MapboxCarMapObserver) {
fun unregisterObserver(mapboxCarMapObserver: MapboxCarMapObserver) = apply {
carMapSurfaceOwner.unregisterObserver(mapboxCarMapObserver)
}

/**
* Optional function to clear all observers registered through [registerObserver]
*/
fun clearObservers() {
fun clearObservers() = apply {
carMapSurfaceOwner.clearObservers()
}

@@ -140,7 +159,7 @@ class MapboxCarMap {
* interface, or override the [DefaultMapboxCarMapGestureHandler], or set to null to disable
* gesture handling.
*/
fun setGestureHandler(gestureHandler: MapboxCarMapGestureHandler?) {
fun setGestureHandler(gestureHandler: MapboxCarMapGestureHandler?) = apply {
carMapSurfaceOwner.gestureHandler = gestureHandler
}
}
Original file line number Diff line number Diff line change
@@ -76,6 +76,26 @@ class MapboxCarMapTest {
assertTrue(surfaceCallbackSlot.isCaptured)
}

@Test
fun `setup will call setSurfaceCallback`() {
MapboxCarMap().setup(carContext, mapInitOptions)

val appManager = carContext.getCarService(AppManager::class.java)
verify(exactly = 1) {
appManager.setSurfaceCallback(any())
}
}

@Test
fun `prepareSurfaceCallback does not call setSurfaceCallback`() {
MapboxCarMap().prepareSurfaceCallback(carContext, mapInitOptions)

val appManager = carContext.getCarService(AppManager::class.java)
verify(exactly = 0) {
appManager.setSurfaceCallback(any())
}
}

@Test
fun `carMapSurface is valid after onSurfaceAvailable`() {
val mapboxCarMap = MapboxCarMap().setup(carContext, mapInitOptions)