Skip to content

Commit

Permalink
Add implementation of TomTom Maps Composables
Browse files Browse the repository at this point in the history
Adding map, marker, overlays and route support for Jetpack Compose.

- Add TomTomMap Composable.
- Add Marker Composable.
- Add Circle Composable.
- Add Polygon Composable.
- Add Polyline Composable.
- Add Route Composable.
  • Loading branch information
voidp-nt-r committed Sep 30, 2024
1 parent afea416 commit 018f57c
Show file tree
Hide file tree
Showing 24 changed files with 1,568 additions and 0 deletions.
1 change: 1 addition & 0 deletions tomtom-compose/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
58 changes: 58 additions & 0 deletions tomtom-compose/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
plugins {
alias(libs.plugins.dokka)
alias(libs.plugins.android.library)
alias(libs.plugins.jetbrains.kotlin.android)
}

android {
namespace = "com.tomtom.maps.compose"
compileSdk = 34

defaultConfig {
minSdk = 26

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles("consumer-rules.pro")
}

packaging {
jniLibs.pickFirsts.add("lib/**/libc++_shared.so")
}

buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.1"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}


dependencies {
implementation(libs.map.display)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.runtime.android)
implementation(libs.androidx.ui.android)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
Empty file.
21 changes: 21 additions & 0 deletions tomtom-compose/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.tomtom.compose

import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4

import org.junit.Test
import org.junit.runner.RunWith

import org.junit.Assert.*

/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.tomtomcompose.test", appContext.packageName)
}
}
4 changes: 4 additions & 0 deletions tomtom-compose/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package com.tomtom.maps.compose

import android.content.Context
import android.view.View
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import com.tomtom.sdk.map.display.marker.BalloonViewAdapter
import com.tomtom.sdk.map.display.marker.Marker

/**
* Implementation of the [BalloonViewAdapter] interface.
* This adapter is responsible for creating a custom balloon view for a given marker in a map.
*
* @param context The context in which the balloon view will be created.
* @param findLayout A lambda function that takes a [Marker] and returns a [Composable] function,
* which defines the layout of the balloon view.
*
* @see BalloonViewAdapter
*/
internal class CustomBalloonViewAdapter(
private val context: Context,
private val findLayout: (Marker) -> (@Composable (Marker) -> Unit)?
) : BalloonViewAdapter {

/**
* Creates a balloon view for the specified marker.
*
* @param marker The [Marker] for which the balloon view is being created.
* @return A [View] that represents the balloon, containing the Composable content
* defined by the layout returned from findLayout.
*/
override fun onCreateBalloonView(marker: Marker): View =
ComposeView(context).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
findLayout(marker)?.invoke(marker)
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package com.tomtom.maps.compose

import androidx.compose.runtime.Composable
import androidx.compose.runtime.ComposeNode
import androidx.compose.runtime.currentComposer
import com.tomtom.sdk.location.GeoPoint
import com.tomtom.sdk.map.display.TomTomMap
import com.tomtom.sdk.map.display.camera.CameraController
import com.tomtom.sdk.map.display.camera.CameraOptions
import kotlin.time.Duration

/**
* Function to create a [TomTomCameraState] to manage the camera of a [TomTomMap] view.
*
* @param animationDuration [Duration] of the animations when camera position is modified.
*
* @return A [TomTomCameraState] that can be used to set and update the position of the camera of
* a [TomTomMap] view.
*
* @see CameraOptions
* @see Duration
*/
@Composable
fun rememberCameraState(
initialCoordinates: GeoPoint? = null,
initialZoom: Double? = null,
initialTilt: Double? = null,
initialRotation: Double? = null,
initialFieldOfView: Double? = null,
animationDuration: Duration = CameraController.DEFAULT_ANIMATION_DURATION
): TomTomCameraState =
TomTomCameraState(
initialCameraOptions = CameraOptions(
initialCoordinates,
initialZoom,
initialTilt,
initialRotation,
initialFieldOfView
),
initialAnimationDuration = animationDuration
)

/**
* Class to create state objects of the MapView camera.
* An instance of this class may only be used for a single [TomTomMap] at time.
*
* @param initialCameraOptions Initial [CameraOptions] for the camera.
* @param initialAnimationDuration [Duration] of the animations when camera position is modified.
*
* @see CameraOptions
* @see Duration
*/
class TomTomCameraState(
initialCameraOptions: CameraOptions = CameraOptions(),
initialAnimationDuration: Duration
) {

var cameraOptions: CameraOptions = initialCameraOptions
set(value) {
field = value
map?.animateCamera(
options = this.cameraOptions,
animationDuration = this.animationDuration
)
field = value
}

private var map: TomTomMap? = null

internal fun setMap(map: TomTomMap?) {
this.map = map
}

private var animationDuration: Duration = initialAnimationDuration

}

/**
* Compose node to be used to link the [TomTomCameraState] to the [TomTomMap].
*
* @property map A [TomTomMap] to which the camera state will be associated.
* @property cameraState The camera state to be linked to the [TomTomMap]
*/
internal class TomTomCameraNode(
val map: TomTomMap?,
var cameraState: TomTomCameraState
) : TomTomMapNode {

init {
cameraState.setMap(map)
}

override fun onAttached() {
}

override fun onCleared() {
cameraState.setMap(null)
}

override fun onRemove() {
cameraState.setMap(null)
}

}

/**
* Composable to manage the sate of the camera and prevent recomposition on the main map view,
* updating only the camera values on state change.
*
* @param cameraState The state of the camera to observe for updates.
*
* @see TomTomCameraState
* @see TomTomCameraNode
*/
@Composable
fun CameraUpdater(
cameraState: TomTomCameraState,
) {
val mapApplier = currentComposer.applier as? TomTomMapApplier
val map = mapApplier?.map ?: error("Error setting camera")
ComposeNode<TomTomCameraNode, TomTomMapApplier>(
factory = {
TomTomCameraNode(
map = map,
cameraState = cameraState
)
},
update = {
update(cameraState) {
this.cameraState = it
}
}
)
}
Loading

0 comments on commit 018f57c

Please sign in to comment.