Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Mapbox welcomes participation and contributions from everyone.
- Introduced `NavigationViewApi#onManeuverCollapsed` and `NavigationViewApi#onManeuverExpanded` callback which is triggered upon changes to `MapboxManeuverViewState`. [#6286](https://github.com/mapbox/mapbox-navigation-android/pull/6286)
- Implemented logic that would display `BannerComponents` of `type` `GuidanceView` and `subType` `BannerComponents#SAPA`, `BannerComponents#CITYREAL`, `BannerComponents#AFTERTOLL`, `BannerComponents#SIGNBOARD`, `BannerComponents#TOLLBRANCH`, `BannerComponents#EXPRESSWAY_ENTRANCE`, `BannerComponents#EXPRESSWAY_EXIT` using `JunctionViewApi`. [#6285](https://github.com/mapbox/mapbox-navigation-android/pull/6285)
- Calling `MapboxNavigationApp.setup` will create a new `MapboxNavigation` instance with new `NavigationOptions` even if the app has been setup. [#6285](https://github.com/mapbox/mapbox-navigation-android/pull/6285)
- Added `MapboxDumpRegistry` which allows you to define adb commands for the sdk. [#6234](https://github.com/mapbox/mapbox-navigation-android/pull/6234)
#### Bug fixes and improvements
- Fixed an issue with `NavigationView` that caused overview camera to have wrong pitch. [#6278](https://github.com/mapbox/mapbox-navigation-android/pull/6278)
- Fixed an issue with `NavigationView` that caused camera issues after reroute or switching to an alternative route. [#6283](https://github.com/mapbox/mapbox-navigation-android/pull/6283)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.mapbox.navigation.core.internal.dump

import java.io.FileDescriptor
import java.io.PrintWriter

/**
* Default interceptor which provides help for the adb service interface.
*/
class HelpDumpInterceptor : MapboxDumpInterceptor {
override fun command() = "help"

override fun description(): String = "Shows available commands and instructions"

override fun availableCommands(): List<Pair<String, String>> {
return MapboxDumpRegistry.getInterceptors().filter { it != this }.map {
Pair(
"${command()}:${it.command()}",
Comment thread
kmadsen marked this conversation as resolved.
"Get the commands available from ${it.command()}"
)
}
}

override fun intercept(
fileDescriptor: FileDescriptor,
writer: PrintWriter,
commands: List<String>
) {
if (commands.isEmpty()) {
printHelpFullDescription(writer)
} else if (commands.size == 1 && commands[0] == command()) {
printHelpCommandList(writer)
} else {
commands.forEach { command ->
val interceptorCommand = command.substringAfter("${command()}:")
val interceptors = MapboxDumpRegistry.getInterceptors(interceptorCommand)
if (interceptors.isEmpty()) {
writer.println("Could not find $command")
} else {
writer.println("Available commands for $command")
interceptors.forEach { interceptor ->
interceptor.availableCommands().forEach {
writer.println(" ${it.prettyString()}")
}
}
}
}
}
}

private fun List<Pair<String, String>>.prettyString() = joinToString(
separator = System.lineSeparator(),
transform = { it.prettyString() }
)

private fun Pair<String, String>.prettyString() = "$first, $second"

private fun printHelpFullDescription(writer: PrintWriter) {
writer.println(
"""
Hello and welcome to the Mapbox Navigation dump!
This allows you to control Mapbox Navigation
from adb. Below are the commands and shortcuts
that are available. If you'd like to create your
own commands, look at the `MapboxDumpRegistry`.

Command arguments can be passed as key:value and are separated by spaces.
For example, if you pass data to dumpsys
and you have added a `MapboxDumpInterceptor`, your
interceptor will receive the command and the data.

$ adb shell dumpsys activity service <service-package> turn_off_audio_guidance
>> turn_off_audio_guidance

$ adb shell dumpsys activity service <service-package> months:june months:july
>> months:june months:july

$ adb shell dumpsys activity service <service-package> "animal":{"age":4,"name":"cat","weight":{"units":"kilograms","value":4.5}}
>> args[0] = animal:age:4
>> args[1] = animal:name:cat
>> args[2] = animal:weight:units:kilograms
>> args[3] = animal:weight:value:4.5

Warning: json format may give unexpected results because arguments are split by spaces.
$ adb shell dumpsys activity service <service-package> "name":"big cat"
>> args[0] = name:big
>> args[1] = cat

Request help for the commands available. This list is given with the `help` command.
${availableCommands().prettyString()}
""".trimIndent()
)
}

private fun printHelpCommandList(writer: PrintWriter) {
writer.println("Request help for the commands available")
writer.println(availableCommands().prettyString())
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.mapbox.navigation.core.internal.dump

import java.io.FileDescriptor
import java.io.PrintWriter

/**
* This will process commands from the dumpsys. Use the [MapboxDumpRegistry] to control what will
* happen when the dump command is called.
*
* @see MapboxDumpRegistry registry for interacting with the interceptors
*/
internal class MapboxDumpHandler {

fun handle(fd: FileDescriptor, writer: PrintWriter, args: Array<String>?) {
val handled = handleArguments(fd, writer, args)
if (handled.isEmpty()) {
// Call the default command when no interceptors are recognized.
MapboxDumpRegistry.defaultInterceptor?.intercept(fd, writer, emptyList())
}
}

private fun handleArguments(
fd: FileDescriptor,
writer: PrintWriter,
args: Array<String>?
): List<MapboxDumpInterceptor> {
val matches: List<Pair<List<MapboxDumpInterceptor>, List<String>>> = args
?.groupBy { it.substringBefore(":", it) }
?.map { Pair(MapboxDumpRegistry.getInterceptors(it.key), it.value) }
?: return emptyList()

matches.forEach { match ->
val interceptors = match.first
val commands = match.second
if (interceptors.isEmpty()) {
writer.println("Unrecognized commands: ${commands.joinToString()}")
} else {
interceptors.forEach { it.intercept(fd, writer, commands) }
writer.println("Processed: ${commands.joinToString()}")
}
}
return matches.flatMap { it.first }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.mapbox.navigation.core.internal.dump

import android.app.Service
import com.google.gson.JsonSyntaxException
import java.io.FileDescriptor
import java.io.PrintWriter

/**
* This is an interface that allows you to intercept command line arguments from dumpsys.
*
* All arguments beginning with `your_command:*` will be sent to the [intercept] function.
*
* For example, if you have an interceptor called "nav_feature".
* service=com.mapbox.navigation.core.trip.service.NavigationNotificationService
* $ adb shell dumpsys activity service ${service} nav_feature:one nav_feature:two
* The intercept function will receive listOf("nav_feature:one", "nav_feature:two")
*/
interface MapboxDumpInterceptor {
Comment thread
kmadsen marked this conversation as resolved.
/**
* String representing the type of commands this interceptor can handle.
Comment thread
kmadsen marked this conversation as resolved.
*/
fun command(): String

/**
* Human readable description of what this interceptor is going to do when the command is
* passed.
*
* All available commands can be viewed with the `help` command
* $ adb shell dumpsys activity service {service-path} help
*/
fun description(): String

/**
* Human readable list of each command and a description of what the command will do. This will
* be displayed with the `help:$command`
*
* @return pair of strings where the first is the `command` and the second is a `description`.
*/
fun availableCommands(): List<Pair<String, String>>

/**
* When a command is found by [Service.dump], the result will be passed to this interceptor.
*
* @throws JsonSyntaxException the caller is expected to handle json exceptions.
*/
@Throws(JsonSyntaxException::class)
fun intercept(
fileDescriptor: FileDescriptor,
writer: PrintWriter,
commands: List<String>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.mapbox.navigation.core.internal.dump

/**
* This is a singleton that allows downstream solutions to define their own interceptors.
*/
object MapboxDumpRegistry {
private val delegate = MapboxDumpRegistryDelegate()

/**
* The default interceptor is usually the [HelpDumpInterceptor]. If you decide to override
* this, note that it will receive the empty command.
*/
var defaultInterceptor: MapboxDumpInterceptor?
get() = delegate.defaultInterceptor
set(value) {
delegate.defaultInterceptor = value
}

/**
* Add interceptors.
*/
fun addInterceptors(vararg interceptors: MapboxDumpInterceptor) =
delegate.addInterceptors(*interceptors)

/**
* Get all available interceptors.
*/
fun getInterceptors(): List<MapboxDumpInterceptor> =
delegate.getInterceptors()

/**
* Get all available interceptors that can handle the [command].
*/
fun getInterceptors(command: String): List<MapboxDumpInterceptor> =
delegate.getInterceptors(command)

/**
* Remove interceptors. Note that this can remove the [defaultInterceptor] if you choose to
* use [getInterceptors] to [removeInterceptors].
*/
fun removeInterceptors(vararg interceptors: MapboxDumpInterceptor) =
delegate.removeInterceptors(*interceptors)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.mapbox.navigation.core.internal.dump

/**
* This is used for unit testing the [MapboxDumpRegistry] singleton.
*/
internal class MapboxDumpRegistryDelegate {
private val interceptors = mutableSetOf<MapboxDumpInterceptor>()

var defaultInterceptor: MapboxDumpInterceptor? = null
set(value) {
field?.let { this.interceptors.remove(it) }
value?.let { addInterceptors(it) }
field = value
}

init {
defaultInterceptor = HelpDumpInterceptor()
}

fun addInterceptors(vararg interceptors: MapboxDumpInterceptor) =
this.interceptors.addAll(interceptors)

fun getInterceptors(): List<MapboxDumpInterceptor> = interceptors.toList()

fun getInterceptors(command: String): List<MapboxDumpInterceptor> =
interceptors.filter { it.command() == command }

fun removeInterceptors(vararg interceptors: MapboxDumpInterceptor) {
if (interceptors.contains(defaultInterceptor)) {
defaultInterceptor = null
}
this.interceptors.removeAll(interceptors.toSet())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import androidx.annotation.CallSuper
import androidx.core.app.ServiceCompat
import com.mapbox.navigation.core.internal.dump.MapboxDumpHandler
import com.mapbox.navigation.core.internal.dump.MapboxDumpRegistry
import com.mapbox.navigation.core.telemetry.MapboxNavigationTelemetry
import java.io.FileDescriptor
import java.io.PrintWriter

/**
* Service is updating information about current trip
Expand All @@ -18,6 +23,13 @@ internal class NavigationNotificationService : Service() {
startForeground(notificationResponse.notificationId, notificationResponse.notification)
}

/**
* This will handle commands from `adb shell dumpsys activity service`.
*
* Use the [MapboxDumpRegistry] to add or remove dump interceptors.
*/
private val mapboxDumpHandler = MapboxDumpHandler()

/**
* Return the communication channel to the service (always *null*)
*/
Expand Down Expand Up @@ -47,4 +59,12 @@ internal class NavigationNotificationService : Service() {
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
MapboxTripService.unregisterOneTimeNotificationDataObserver(notificationDataObserver)
}

/**
* Overrides the dump command so that state can be changed with adb.
*/
@CallSuper
override fun dump(fd: FileDescriptor, writer: PrintWriter, args: Array<String>?) {
mapboxDumpHandler.handle(fd, writer, args)
}
}
Loading