Skip to content
Open
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
5 changes: 5 additions & 0 deletions examples/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@
android:screenOrientation="portrait">
</activity>

<activity
android:name="com.mapbox.navigation.examples.voicefeedback.MapboxVoiceFeedbackActivity"
android:label="@string/title_voice_feedback">
</activity>

<activity
android:name="com.mapbox.navigation.examples.MainActivity"
android:exported="true">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.mapbox.navigation.examples.core.databinding.LayoutActivityMainBinding
import com.mapbox.navigation.examples.util.LocationPermissionsHelper
import com.mapbox.navigation.examples.util.LocationPermissionsHelper.Companion.areLocationPermissionsGranted
import com.mapbox.navigation.examples.util.RouteDrawingActivity
import com.mapbox.navigation.examples.voicefeedback.MapboxVoiceFeedbackActivity

class MainActivity : AppCompatActivity(), PermissionsListener {

Expand Down Expand Up @@ -148,6 +149,11 @@ class MainActivity : AppCompatActivity(), PermissionsListener {
getString(R.string.title_adasis),
getString(R.string.description_adasis),
AdasisActivity::class.java
),
SampleItem(
getString(R.string.title_voice_feedback),
getString(R.string.description_voice_feedback),
MapboxVoiceFeedbackActivity::class.java
)
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package com.mapbox.navigation.examples.voicefeedback

import android.Manifest
import android.os.Bundle
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import com.mapbox.maps.MapboxMap
import com.mapbox.maps.plugin.locationcomponent.location
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
import com.mapbox.navigation.base.options.NavigationOptions
import com.mapbox.navigation.core.MapboxNavigation
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
import com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver
import com.mapbox.navigation.core.lifecycle.requireMapboxNavigation
import com.mapbox.navigation.examples.core.databinding.LayoutActivityVoiceFeedbackBinding
import com.mapbox.navigation.examples.util.Utils
import com.mapbox.navigation.ui.base.installer.installComponents
import com.mapbox.navigation.ui.maps.location.NavigationLocationProvider
import com.mapbox.navigation.ui.voicefeedback.voiceFeedbackButton

/**
* Example activity demonstrating the [libnavui-voicefeedback](https://docs.mapbox.com/android/navigation/ui/latest/libnavui-voicefeedback/overview/)
* SDK. This SDK provides voice feedback capabilities, allowing users to report issues,
* suggest improvements, or give feedback directly through voice while navigating.
*
* It utilizes [MapboxVoiceFeedbackButton] and [FeedbackAgentSession] (via the ComponentInstaller)
* to manage the lifecycle and UI states of the voice interaction flow.
*/
@OptIn(ExperimentalPreviewMapboxNavigationAPI::class)
class MapboxVoiceFeedbackActivity : AppCompatActivity() {

private val navigationLocationProvider = NavigationLocationProvider()
private lateinit var binding: LayoutActivityVoiceFeedbackBinding
private lateinit var mapboxMap: MapboxMap
private val mapboxNavigation: MapboxNavigation by requireMapboxNavigation(
onResumedObserver = object : MapboxNavigationObserver {
override fun onAttached(mapboxNavigation: MapboxNavigation) {
}

override fun onDetached(mapboxNavigation: MapboxNavigation) {
}
},
onInitialize = this::initNavigation
)

private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val areGranted = permissions.values.all { it }
if (areGranted) {
mapboxNavigation.startTripSession()
} else {
Toast.makeText(this, "Location and Microphone permissions are required", Toast.LENGTH_LONG).show()
finish()
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = LayoutActivityVoiceFeedbackBinding.inflate(layoutInflater)
setContentView(binding.root)

mapboxMap = binding.mapView.getMapboxMap()

requestPermissionLauncher.launch(
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.RECORD_AUDIO
)
)
}

private fun initNavigation() {
if (!MapboxNavigationApp.isSetup()) {
MapboxNavigationApp.setup(
NavigationOptions.Builder(this)
.accessToken(Utils.getMapboxAccessToken(this))
.build()
)
}

MapboxNavigationApp.installComponents(this) {
voiceFeedbackButton(binding.voiceFeedbackButton)
}

binding.mapView.location.apply {
enabled = true
setLocationProvider(navigationLocationProvider)
}

mapboxMap.setCamera(
com.mapbox.maps.CameraOptions.Builder()
.center(com.mapbox.geojson.Point.fromLngLat(-73.98513, 40.748817)) // New York City
.zoom(13.0)
.build()
)
}

}
21 changes: 21 additions & 0 deletions examples/src/main/res/layout/layout_activity_voice_feedback.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.mapbox.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:mapbox_cameraZoom="14" />

<com.mapbox.navigation.ui.voicefeedback.view.MapboxVoiceFeedbackButton
android:id="@+id/voiceFeedbackButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
3 changes: 3 additions & 0 deletions examples/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
<string name="title_adasis" translatable="false">Adasis Example</string>
<string name="description_adasis" translatable="false">Demonstrates to to access Adasis functionality.</string>

<string name="title_voice_feedback" translatable="false">Voice Feedback Example</string>
<string name="description_voice_feedback" translatable="false">Demonstrates the use of voice feedback API.</string>

<string name="label_start_navigation" translatable="false">Start Navigation</string>
<string name="play_history" translatable="false">Play history</string>
<string name="select_history" translatable="false">Select history</string>
Expand Down
1 change: 1 addition & 0 deletions gradle/publish.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ sdkNameMap["libnavui-tripprogress"] = "mobile-navigation-ui-tripprogress"
sdkNameMap["libnavui-maneuver"] = "mobile-navigation-ui-maneuver"
sdkNameMap["libnavui-resources"] = "mobile-navigation-ui-resources"
sdkNameMap["libnavui-voice"] = "mobile-navigation-ui-voice"
sdkNameMap["libnavui-voicefeedback"] = "mobile-navigation-ui-voicefeedback"
sdkNameMap["libnavui-speedlimit"] = "mobile-navigation-ui-speedlimit"
sdkNameMap["libnavui-shield"] = "mobile-navigation-ui-shield"
sdkNameMap["libnavui-status"] = "mobile-navigation-ui-status"
Expand Down
1 change: 1 addition & 0 deletions libnavigation-android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ dependencies {
api project(":libnavui-speedlimit")
api project(":libnavui-tripprogress")
api project(":libnavui-voice")
api project(":libnavui-voicefeedback")
api project(":libnavui-status")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,12 @@ class FeedbackAgentSession private constructor(
engine.disconnect()
}

private companion object {
companion object {
private const val TAG = "FeedbackAgentSession"

@JvmStatic
fun getRegisteredInstance(): FeedbackAgentSession = MapboxNavigationApp
.getObservers(FeedbackAgentSession::class)
.firstOrNull() ?: Builder().build().also { MapboxNavigationApp.registerObserver(it) }
}
}
1 change: 1 addition & 0 deletions libnavui-dropin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ dependencies {
api project(":libnavui-speedlimit")
api project(":libnavui-tripprogress")
api project(":libnavui-voice")
api project(":libnavui-voicefeedback")
implementation project(":libnavui-app")

api dependenciesList.mapboxSdkServices
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.mapbox.navigation.dropin.voicefeedback

import com.mapbox.maps.MapView
import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
import com.mapbox.navigation.ui.base.installer.ComponentInstaller
import com.mapbox.navigation.ui.base.installer.Installation
import com.mapbox.navigation.ui.maps.util.ViewUtils.capture
import com.mapbox.navigation.ui.voicefeedback.internal.ScreenshotCapturer
import com.mapbox.navigation.ui.voicefeedback.internal.VoiceFeedbackComponent
import com.mapbox.navigation.ui.voicefeedback.view.MapboxVoiceFeedbackButton

/**
* Installs the voice feedback button component into the current Mapbox Navigation flow.
*
* @param mapView The [MapView] that will be used to capture screenshots during feedback. Leave null
* if you don't want to capture screenshots.
* @param button The visual representation of the voice feedback button.
* @return An [Installation] handle that can be used to remove the component.
*/
@ExperimentalPreviewMapboxNavigationAPI
fun ComponentInstaller.voiceFeedbackButton(
mapView: MapView? = null,
button: MapboxVoiceFeedbackButton,
): Installation {
val screenshotCapturer = mapView?.let {
ScreenshotCapturer { callback ->
it.capture { screenshot -> callback(screenshot) }
}
}
return component(VoiceFeedbackComponent(button, screenshotCapturer))
}
2 changes: 2 additions & 0 deletions libnavui-voicefeedback/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/build
src/main/assets/sdk_versions/*
77 changes: 77 additions & 0 deletions libnavui-voicefeedback/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-parcelize'
apply plugin: 'org.jetbrains.dokka'
apply plugin: 'com.jaredsburrows.license'
apply plugin: 'com.mapbox.android.sdk.versions'
apply from: "../gradle/ktlint.gradle"
apply from: file("../gradle/artifact-settings.gradle")
apply from: "../gradle/kdoc-settings.gradle"

version = project.ext.versionName
group = project.ext.mapboxArtifactGroupId

android {
compileSdkVersion androidVersions.compileSdkVersion

compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}

defaultConfig {
minSdkVersion androidVersions.minSdkVersion
targetSdkVersion androidVersions.targetSdkVersion
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles 'proguard-rules.pro', "../proguard/proguard-project.pro"
}

testOptions {
unitTests.returnDefaultValues = true
unitTests.includeAndroidResources = true
}

buildFeatures {
viewBinding true
}
}

dependencies {
api project(":libnavigation-voicefeedback")
api project(":libnavui-base")
implementation project(":libnavui-resources")

api dependenciesList.mapboxSdkServices

implementation dependenciesList.kotlinStdLib

implementation dependenciesList.coroutinesAndroid

// androidX
implementation dependenciesList.androidXConstraintLayout
implementation dependenciesList.androidXAppCompat
implementation dependenciesList.androidXCoreKtx

apply from: "../gradle/unit-testing-dependencies.gradle"
testImplementation(project(':libtesting-utils'))
testImplementation project(':libtesting-navigation-util')
}

dokkaHtmlPartial {
outputDirectory.set(kdocPath)
moduleName.set("UI Feedback Agent")
dokkaSourceSets {
configureEach {
reportUndocumented.set(true)
perPackageOption {
matchingRegex.set("com.mapbox.navigation.ui.voicefeedback.internal.*")
suppress.set(true)
}
}
}
}

apply from: "../gradle/track-public-apis.gradle"
apply from: "../gradle/jacoco.gradle"
apply from: "../gradle/publish.gradle"
3 changes: 3 additions & 0 deletions libnavui-voicefeedback/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_ARTIFACT_ID=ui-voicefeedback
POM_ARTIFACT_TITLE=Mapbox Navigation Feedback Agent
POM_DESCRIPTION=Artifact that provides ability to collect feedback using voice
21 changes: 21 additions & 0 deletions libnavui-voicefeedback/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
3 changes: 3 additions & 0 deletions libnavui-voicefeedback/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.mapbox.navigation.ui.voicefeedback">
</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.mapbox.navigation.ui.voicefeedback

import com.mapbox.navigation.base.ExperimentalPreviewMapboxNavigationAPI
import com.mapbox.navigation.ui.base.installer.ComponentInstaller
import com.mapbox.navigation.ui.base.installer.Installation
import com.mapbox.navigation.ui.voicefeedback.internal.VoiceFeedbackComponent
import com.mapbox.navigation.ui.voicefeedback.view.MapboxVoiceFeedbackButton

/**
* Installs the voice feedback button component into the current Mapbox Navigation flow.
*
* @param button The visual representation of the voice feedback button.
* @return An [Installation] handle that can be used to remove the component.
*/
@ExperimentalPreviewMapboxNavigationAPI
fun ComponentInstaller.voiceFeedbackButton(button: MapboxVoiceFeedbackButton): Installation {
return component(VoiceFeedbackComponent(button))
}

Loading