Skip to content
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

[Android Auto] Add onClick example #1682

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
6 changes: 5 additions & 1 deletion android-auto-app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ androidExtensions {

dependencies {
implementation(project(":extension-androidauto"))
implementation(Dependencies.googleCarAppLibrary)

// Upgrade the google car library to demonstrate adopting new apis.
// https://developer.android.com/jetpack/androidx/releases/car-app
implementation("androidx.car.app:app:1.3.0-beta01")
// implementation(Dependencies.googleCarAppLibrary)

// Please review the compatibility guide. This app is showcasing the latest features.
// https://github.com/mapbox/mapbox-maps-android/tree/main/extension-androidauto#compatibility-with-maps-sdk-v10
Expand Down
2 changes: 1 addition & 1 deletion android-auto-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
tools:ignore="MetadataTagInsideApplicationTag" />
<meta-data
android:name="androidx.car.app.minCarApiLevel"
android:value="1"
android:value="5"
tools:ignore="MetadataTagInsideApplicationTag" />

<service
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package com.mapbox.maps.testapp.auto

import android.content.Context
import android.content.SharedPreferences

/**
* Class gives you the ability to modify preferences that can be used by the car and app.
*/
class CarAppPreferences(context: Context) {

val sharedPreferences: SharedPreferences by lazy {
context.getSharedPreferences(SHARED_PREFERENCES_KEY, Context.MODE_PRIVATE)
}

fun enableOnClick(enable: Boolean) {
sharedPreferences.edit().putBoolean(BOOLEAN_KEY_ENABLE_ON_CLICK, enable).apply()
}

fun isOnClickEnabled() = sharedPreferences
.getBoolean(BOOLEAN_KEY_ENABLE_ON_CLICK, false)

companion object {
private const val SHARED_PREFERENCES_KEY = "mapbox_maps_android_auto_app"

const val BOOLEAN_KEY_ENABLE_ON_CLICK = "enable_onclick"
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,29 @@
package com.mapbox.maps.testapp.auto.app

import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SwitchCompat
import com.mapbox.maps.testapp.auto.CarAppPreferences
import com.mapbox.maps.testapp.auto.R

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val switchButton: SwitchCompat = findViewById(R.id.switchButton)

val carAppPreferences = CarAppPreferences(applicationContext)
switchButton.isChecked = carAppPreferences.isOnClickEnabled()
switchButton.setOnCheckedChangeListener { _, isChecked ->
if (carAppPreferences.isOnClickEnabled() != isChecked) {
Toast.makeText(
this,
"Custom setting changed, reconnect Android Auto to ensure a restart",
Toast.LENGTH_LONG
).show()
carAppPreferences.enableOnClick(isChecked)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.mapbox.maps.testapp.auto.car

import android.content.SharedPreferences
import androidx.car.app.AppManager
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.mapbox.maps.MapboxExperimental
import com.mapbox.maps.extension.androidauto.MapboxCarMap
import com.mapbox.maps.extension.androidauto.MapboxCarMapInitializer
import com.mapbox.maps.logI
import com.mapbox.maps.testapp.auto.CarAppPreferences
import com.mapbox.maps.testapp.auto.custom.SurfaceCallbackInterceptor

/**
* In order to enable onClick, you need to override the mapboxCarMap.prepareSurfaceCallback
*
* This class allows you to toggle the preference from the app, to show that you can change this
* setting at runtime. In real scenarios, it may be initialize the map with onClick enabled.
*/
@OptIn(MapboxExperimental::class)
class CarMapOnClickEnabler(
private val mapboxCarMap: MapboxCarMap,
private val initializer: MapboxCarMapInitializer
) : DefaultLifecycleObserver {

private var carAppPreferences: CarAppPreferences? = null

private val listener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
if (key == CarAppPreferences.BOOLEAN_KEY_ENABLE_ON_CLICK) {
val customCallbackEnabled = sharedPreferences.getBoolean(key, false)
onEnableOnClickPreferenceChanged(customCallbackEnabled)
}
}

override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
carAppPreferences = CarAppPreferences(mapboxCarMap.carContext).apply {
sharedPreferences.registerOnSharedPreferenceChangeListener(listener)
onEnableOnClickPreferenceChanged(isOnClickEnabled())
}
}

override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
carAppPreferences?.sharedPreferences?.unregisterOnSharedPreferenceChangeListener(listener)
carAppPreferences = null
}

private fun onEnableOnClickPreferenceChanged(customCallbackEnabled: Boolean) {
val carContext = mapboxCarMap.carContext
if (customCallbackEnabled) {
val surfaceCallback = mapboxCarMap.prepareSurfaceCallback(
carContext, initializer.onCreate(carContext)
)
carContext.getCarService(AppManager::class.java)
.setSurfaceCallback(object : SurfaceCallbackInterceptor(surfaceCallback) {
override fun onClick(x: Float, y: Float) {
super.onClick(x, y)
onMapSurfaceClick(x, y)
}
})
} else {
mapboxCarMap.setup(carContext, initializer.onCreate(carContext))
}
}
Comment on lines +50 to +65
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can toggle between these at runtime and everything works.

All observers that are attached, will be detached and then attached with a new map surface


private fun onMapSurfaceClick(x: Float, y: Float) {
logI("CarMapOnClickEnabler", "onMapSurfaceClick $x $y")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import androidx.car.app.ScreenManager
import androidx.car.app.Session
import com.mapbox.maps.MapInitOptions
import com.mapbox.maps.MapboxExperimental
import com.mapbox.maps.extension.androidauto.MapboxCarMapInitializer
import com.mapbox.maps.extension.androidauto.mapboxMapInstaller

/**
Expand All @@ -18,14 +19,20 @@ import com.mapbox.maps.extension.androidauto.mapboxMapInstaller
class MapSession : Session() {

private val carMapShowcase = CarMapShowcase()
private val initializer = MapboxCarMapInitializer { carContext -> MapInitOptions(carContext) }
private val mapboxCarMap = mapboxMapInstaller()
.onCreated(CarAnimationThreadController(), CarMapWidgets(), carMapShowcase)
.onCreated(CarAnimationThreadController(), CarMapWidgets(), carMapShowcase,)
.install { carContext ->
// Callback is triggered when the Session calls onCreate. This allows you to specify
// custom MapInitOptions.
MapInitOptions(carContext)
initializer.onCreate(carContext)
}

init {
// Override the SurfaceCallback to capture onClick events
lifecycle.addObserver(CarMapOnClickEnabler(mapboxCarMap, initializer))
}

override fun onCreateScreen(intent: Intent): Screen {
// The onCreate is guaranteed to be called before onCreateScreen. You can pass the
// mapboxCarMap to other screens. Each screen can register and unregister observers.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.mapbox.maps.testapp.auto.custom

import android.graphics.Rect
import androidx.car.app.SurfaceCallback
import androidx.car.app.SurfaceContainer

abstract class SurfaceCallbackInterceptor constructor(
private val surfaceCallback: SurfaceCallback
) : SurfaceCallback {

override fun onSurfaceAvailable(surfaceContainer: SurfaceContainer) =
surfaceCallback.onSurfaceAvailable(surfaceContainer)

override fun onSurfaceDestroyed(surfaceContainer: SurfaceContainer) =
surfaceCallback.onSurfaceDestroyed(surfaceContainer)

override fun onVisibleAreaChanged(visibleArea: Rect) =
surfaceCallback.onVisibleAreaChanged(visibleArea)

override fun onStableAreaChanged(stableArea: Rect) =
surfaceCallback.onStableAreaChanged(stableArea)

override fun onScale(focusX: Float, focusY: Float, scaleFactor: Float) =
surfaceCallback.onScale(focusX, focusY, scaleFactor)

override fun onScroll(distanceX: Float, distanceY: Float) =
surfaceCallback.onScroll(distanceX, distanceY)

override fun onFling(velocityX: Float, velocityY: Float) =
surfaceCallback.onFling(velocityX, velocityY)

override fun onClick(x: Float, y: Float) =
surfaceCallback.onClick(x, y)
}
14 changes: 13 additions & 1 deletion android-auto-app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,25 @@
tools:context=".app.MainActivity">

<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Android Auto testing activity"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="Android Auto testing activity"
tools:ignore="HardcodedText" />

<androidx.appcompat.widget.SwitchCompat
android:id="@+id/switchButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Enable onClick"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView"
tools:ignore="HardcodedText"/>

</androidx.constraintlayout.widget.ConstraintLayout>
4 changes: 2 additions & 2 deletions buildSrc/src/main/kotlin/Project.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ object AndroidVersions {
object AndroidAuto {
const val minSdkVersion = 23
const val targetSdkVersion = 30
const val compileSdkVersion = 31
const val compileSdkVersion = 33
}
object Compose {
const val minSdkVersion = 23
Expand Down Expand Up @@ -98,7 +98,7 @@ object Dependencies {

object Versions {
const val pluginAndroidGradle = "7.0.4"
const val pluginKotlin = "1.5.31"
const val pluginKotlin = "1.7.10"
const val pluginLicense = "0.8.80"
const val pluginDokka = "1.5.31"
const val pluginJacoco = "0.2"
Expand Down