diff --git a/Package.swift b/Package.swift index 3e587626..5a7897de 100644 --- a/Package.swift +++ b/Package.swift @@ -16,7 +16,7 @@ if useLocalFramework { path: "./common/target/ios/libferrostar-rs.xcframework" ) } else { - let releaseTag = "0.11.0" + let releaseTag = "0.12.0" let releaseChecksum = "32a28d66933b6dc7f9b206c79e265767fc51339a9a81f81304c3f15cd7722d19" binaryTarget = .binaryTarget( name: "FerrostarCoreRS", diff --git a/android/build.gradle b/android/build.gradle index ab97c9f9..ee71caef 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -12,5 +12,5 @@ plugins { allprojects { group = "com.stadiamaps.ferrostar" - version = "0.11.0" + version = "0.12.0" } diff --git a/android/google-play-services/.gitignore b/android/google-play-services/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/android/google-play-services/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/android/google-play-services/build.gradle b/android/google-play-services/build.gradle new file mode 100644 index 00000000..323b3285 --- /dev/null +++ b/android/google-play-services/build.gradle @@ -0,0 +1,67 @@ +import com.vanniktech.maven.publish.AndroidMultiVariantLibrary +import com.vanniktech.maven.publish.SonatypeHost + +plugins { + alias libs.plugins.androidLibrary + alias libs.plugins.jetbrainsKotlinAndroid + alias libs.plugins.ktfmt + alias libs.plugins.mavenPublish +} + +android { + namespace 'com.stadiamaps.ferrostar.googleplayservices' + compileSdk 34 + + defaultConfig { + minSdk 25 + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles "consumer-rules.pro" + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + // For as long as we support API 25; once we can raise support to 26, all is fine + coreLibraryDesugaring libs.desugar.jdk.libs + + implementation libs.androidx.ktx + implementation libs.androidx.appcompat + implementation libs.material + + implementation project(':core') + implementation libs.play.services.location + + testImplementation libs.junit + androidTestImplementation libs.androidx.test.junit + androidTestImplementation libs.androidx.test.espresso +} + +mavenPublishing { + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) + signAllPublications() + + configure(new AndroidMultiVariantLibrary(true, true)) + + apply from: "${rootProject.projectDir}/common-pom.gradle" + + pom { + name = "Ferrostar Google Play Services" + description = "Ferrostar components that rely on Google Play Services" + commonPomConfig(it, true) + } +} diff --git a/android/google-play-services/consumer-rules.pro b/android/google-play-services/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/android/google-play-services/proguard-rules.pro b/android/google-play-services/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/android/google-play-services/proguard-rules.pro @@ -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 \ No newline at end of file diff --git a/android/google-play-services/src/androidTest/java/com/stadiamaps/ferrostar/googleplayservices/ExampleInstrumentedTest.kt b/android/google-play-services/src/androidTest/java/com/stadiamaps/ferrostar/googleplayservices/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..8fa2eec0 --- /dev/null +++ b/android/google-play-services/src/androidTest/java/com/stadiamaps/ferrostar/googleplayservices/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package com.stadiamaps.ferrostar.googleplayservices + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith + +/** + * 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.stadiamaps.ferrostar.googleplayservices.test", appContext.packageName) + } +} diff --git a/android/google-play-services/src/main/AndroidManifest.xml b/android/google-play-services/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a5918e68 --- /dev/null +++ b/android/google-play-services/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/android/google-play-services/src/main/java/com/stadiamaps/ferrostar/googleplayservices/FusedLocationProvider.kt b/android/google-play-services/src/main/java/com/stadiamaps/ferrostar/googleplayservices/FusedLocationProvider.kt new file mode 100644 index 00000000..566f9212 --- /dev/null +++ b/android/google-play-services/src/main/java/com/stadiamaps/ferrostar/googleplayservices/FusedLocationProvider.kt @@ -0,0 +1,64 @@ +package com.stadiamaps.ferrostar.googleplayservices + +import android.annotation.SuppressLint +import android.content.Context +import android.os.Looper +import android.util.Log +import com.google.android.gms.location.FusedLocationProviderClient +import com.google.android.gms.location.LocationListener +import com.google.android.gms.location.LocationRequest +import com.google.android.gms.location.LocationServices +import com.google.android.gms.location.Priority +import com.stadiamaps.ferrostar.core.LocationProvider +import com.stadiamaps.ferrostar.core.LocationUpdateListener +import com.stadiamaps.ferrostar.core.toUserLocation +import java.util.concurrent.Executor +import uniffi.ferrostar.Heading +import uniffi.ferrostar.UserLocation + +class FusedLocationProvider( + context: Context, + private val fusedLocationProviderClient: FusedLocationProviderClient = + LocationServices.getFusedLocationProviderClient(context) +) : LocationProvider { + + companion object { + private const val TAG = "FusedLocationProvider" + } + + override var lastLocation: UserLocation? = null + private set + + override var lastHeading: Heading? = null + private set + + private val listeners: MutableMap = mutableMapOf() + + @SuppressLint("MissingPermission") + override fun addListener(listener: LocationUpdateListener, executor: Executor) { + Log.d(TAG, "Adding listener") + if (listeners.contains(listener)) { + Log.d(TAG, "Listener already added") + return + } + + val locationListener = LocationListener { newLocation -> + listener.onLocationUpdated(newLocation.toUserLocation()) + } + listeners[listener] = locationListener + + val locationRequest = + LocationRequest.Builder(1000L).setPriority(Priority.PRIORITY_HIGH_ACCURACY).build() + + fusedLocationProviderClient.requestLocationUpdates( + locationRequest, locationListener, Looper.getMainLooper()) + } + + override fun removeListener(listener: LocationUpdateListener) { + val activeListener = listeners.remove(listener) + + if (activeListener != null) { + fusedLocationProviderClient.removeLocationUpdates(activeListener) + } + } +} diff --git a/android/google-play-services/src/test/java/com/stadiamaps/ferrostar/googleplayservices/ExampleUnitTest.kt b/android/google-play-services/src/test/java/com/stadiamaps/ferrostar/googleplayservices/ExampleUnitTest.kt new file mode 100644 index 00000000..1dfb1e72 --- /dev/null +++ b/android/google-play-services/src/test/java/com/stadiamaps/ferrostar/googleplayservices/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package com.stadiamaps.ferrostar.googleplayservices + +import org.junit.Assert.* +import org.junit.Test + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} diff --git a/android/gradle/libs.versions.toml b/android/gradle/libs.versions.toml index 6ca14df4..7acc1234 100644 --- a/android/gradle/libs.versions.toml +++ b/android/gradle/libs.versions.toml @@ -15,12 +15,14 @@ compose = "2024.09.01" okhttp = "4.12.0" moshi = "1.15.1" maplibre-compose = "0.1.0" +playServicesLocation = "21.3.0" junit = "4.13.2" junitVersion = "1.2.1" junitCompose = "1.7.1" espressoCore = "3.6.1" okhttp-mock = "2.0.0" mavenPublish = "0.29.0" +material = "1.12.0" [libraries] desugar_jdk_libs = { module = "com.android.tools:desugar_jdk_libs", version.ref = "desugar_jdk_libs" } @@ -53,11 +55,14 @@ moshi = { group = "com.squareup.moshi", name = "moshi", version.ref = "moshi" } okhttp-mock = { group = "com.github.gmazzo", name = "okhttp-mock", version.ref = "okhttp-mock" } # MapLibre maplibre-compose = { group = "io.github.rallista", name = "maplibre-compose", version.ref = "maplibre-compose" } +# Google Play Services (for Google module) +play-services-location = { module = "com.google.android.gms:play-services-location", version.ref = "playServicesLocation" } # Testing junit = { group = "junit", name = "junit", version.ref = "junit" } androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-test-espresso = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4", version.ref = "junitCompose" } +material = { group = "com.google.android.material", name = "material", version.ref = "material" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } diff --git a/android/settings.gradle b/android/settings.gradle index 64905c23..e34d6ca0 100644 --- a/android/settings.gradle +++ b/android/settings.gradle @@ -17,3 +17,4 @@ include ':core' include ':composeui' include ':demo-app' include ':maplibreui' +include ':google-play-services' diff --git a/common/Cargo.lock b/common/Cargo.lock index 1279d5bd..3933ac31 100644 --- a/common/Cargo.lock +++ b/common/Cargo.lock @@ -337,7 +337,7 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "ferrostar" -version = "0.11.0" +version = "0.12.0" dependencies = [ "assert-json-diff", "geo", diff --git a/common/ferrostar/Cargo.toml b/common/ferrostar/Cargo.toml index a277132b..797d2211 100644 --- a/common/ferrostar/Cargo.toml +++ b/common/ferrostar/Cargo.toml @@ -2,7 +2,7 @@ lints.workspace = true [package] name = "ferrostar" -version = "0.11.0" +version = "0.12.0" readme = "README.md" description = "The core of modern turn-by-turn navigation." keywords = ["navigation", "routing", "valhalla", "osrm"] diff --git a/guide/src/android-getting-started.md b/guide/src/android-getting-started.md index 993a6a87..4c826377 100644 --- a/guide/src/android-getting-started.md +++ b/guide/src/android-getting-started.md @@ -23,6 +23,9 @@ dependencies { implementation "com.stadiamaps.ferrostar:core:${ferrostarVersion}" implementation "com.stadiamaps.ferrostar:maplibreui:${ferrostarVersion}" + // Optional - if using Google Play Service's FusedLocation + implementation "com.stadiamaps.ferrostar:google-play-services:${ferrostarVersion}" + // okhttp3 implementation platform("com.squareup.okhttp3:okhttp-bom:4.11.0") implementation 'com.squareup.okhttp3:okhttp' @@ -129,7 +132,27 @@ In other cases, get a context using an appropriate method. locationProvider = AndroidSystemLocationProvider(context = this) ``` -#### TODO: Google Play Fused Location Client +#### Google Play Fused Location Client + +Alternatively, you can use the `FusedLocationProvider` +if your app uses Google Play Services. +This normally offers better device positioning than the default Android location provider +on supported devices. +To make use of it, +you will need to include the optional `implementation "com.stadiamaps.ferrostar:google-play-services:${ferrostarVersion}"` +in your Gradle dependencies block. + +Just as with the `AndroidSystemLocationProvider`, +you probably need to declare it as a `lateinit var` instance variable first, +and then initialize later once the `Context` is available. + +```kotlin +// Instance variable definition +private lateinit var locationProvider: FusedLocationProvider + +// Later when the activity loads and a context is avaialable +locationProvider = FusedLocationProvider(context = this) +``` #### `SimulatedLocationProvider` diff --git a/web/package-lock.json b/web/package-lock.json index c52c756e..e16d63ec 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,12 +1,12 @@ { "name": "@stadiamaps/ferrostar-webcomponents", - "version": "0.11.0", + "version": "0.12.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@stadiamaps/ferrostar-webcomponents", - "version": "0.11.0", + "version": "0.12.0", "license": "BSD-3-Clause", "dependencies": { "@stadiamaps/ferrostar": "file:../common/ferrostar/pkg", diff --git a/web/package.json b/web/package.json index 0d585dec..013d9579 100644 --- a/web/package.json +++ b/web/package.json @@ -6,7 +6,7 @@ "CatMe0w (https://github.com/CatMe0w)", "Luke Seelenbinder " ], - "version": "0.11.0", + "version": "0.12.0", "license": "BSD-3-Clause", "type": "module", "main": "./dist/ferrostar-webcomponents.js",