WIP: WIP
This commit is contained in:
parent
49559993d8
commit
0a00d13706
6 changed files with 328 additions and 15 deletions
|
@ -16,6 +16,7 @@ android {
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
buildConfigField "String", "MAPBOX_DOWNLOADS_TOKEN", "\"$MAPBOX_DOWNLOADS_TOKEN\""
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
@ -33,6 +34,7 @@ android {
|
||||||
}
|
}
|
||||||
buildFeatures {
|
buildFeatures {
|
||||||
viewBinding true
|
viewBinding true
|
||||||
|
buildConfig = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,8 +5,10 @@ import android.bluetooth.BluetoothAdapter
|
||||||
import android.bluetooth.BluetoothDevice
|
import android.bluetooth.BluetoothDevice
|
||||||
import android.bluetooth.BluetoothSocket
|
import android.bluetooth.BluetoothSocket
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import com.mapbox.navigation.ui.maneuver.model.Maneuver
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import java.util.concurrent.SynchronousQueue
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
|
@ -16,19 +18,79 @@ class Garmin(
|
||||||
val adapter: BluetoothAdapter
|
val adapter: BluetoothAdapter
|
||||||
) {
|
) {
|
||||||
|
|
||||||
private lateinit var thread: ConnectThread
|
private lateinit var connection: ConnectThread
|
||||||
|
private lateinit var processing: ProcessingThread
|
||||||
|
private var stateCache: State = State()
|
||||||
|
|
||||||
fun start() {
|
fun start() {
|
||||||
thread = ConnectThread()
|
connection = ConnectThread()
|
||||||
thread.start()
|
connection.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun close() {
|
fun close() {
|
||||||
thread.close()
|
connection.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun process(maneuver: Maneuver) {
|
||||||
|
processing = ProcessingThread(maneuver)
|
||||||
|
processing.start()
|
||||||
|
processing.join()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private inner class ProcessingThread(val maneuver: Maneuver) : Thread() {
|
||||||
|
override fun run() {
|
||||||
|
send(ManeuverMapper.apply(maneuver))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun send(incoming: eu.ztsh.garmin.State) {
|
||||||
|
if (stateCache.distance != incoming.distance) {
|
||||||
|
setDistance(incoming)
|
||||||
|
}
|
||||||
|
if (stateCache.direction != incoming.direction) {
|
||||||
|
setDirection(stateCache.direction)
|
||||||
|
}
|
||||||
|
stateCache = incoming
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDistance(state: eu.ztsh.garmin.State) {
|
||||||
|
connection.enqueue(intArrayOf(
|
||||||
|
0x03, asDigit(state.distance / 1000), asDigit(state.distance / 100), asDigit(state.distance / 10),
|
||||||
|
0x00, asDigit(state.distance), state.unit.data
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setDirection(direction: Direction) {
|
||||||
|
val param1 = when (direction.outAngle) {
|
||||||
|
OutAngle.LeftDown -> 0x10
|
||||||
|
OutAngle.RightDown -> 0x20
|
||||||
|
else -> direction.outType.data
|
||||||
|
}
|
||||||
|
val param2: Int = if (direction.outType == OutType.RightRoundabout
|
||||||
|
|| direction.outType == OutType.LeftRoundabout) {
|
||||||
|
if (direction.roundabout == OutAngle.AsDirection) direction.outAngle.data else direction.roundabout.data
|
||||||
|
} else {
|
||||||
|
0x00
|
||||||
|
}
|
||||||
|
val param3: Int = if (direction.outAngle == OutAngle.LeftDown || direction.outAngle == OutAngle.RightDown) 0x00 else direction.outAngle.data
|
||||||
|
connection.enqueue(intArrayOf(0x01, param1, param2, param3))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun asDigit(n: Int): Int {
|
||||||
|
if (n == 0) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
val m = n % 10
|
||||||
|
return if (m == 0) 10 else m
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ConnectThread : Thread() {
|
private inner class ConnectThread : Thread() {
|
||||||
|
|
||||||
|
private val queue: SynchronousQueue<IntArray> = SynchronousQueue()
|
||||||
|
private var current: IntArray = intArrayOf()
|
||||||
|
|
||||||
private val socket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
|
private val socket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
context.checkBt()
|
context.checkBt()
|
||||||
device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"))
|
device.createRfcommSocketToServiceRecord(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"))
|
||||||
|
@ -41,10 +103,12 @@ class Garmin(
|
||||||
socket?.connect()
|
socket?.connect()
|
||||||
sleep(3000)
|
sleep(3000)
|
||||||
readAll()
|
readAll()
|
||||||
send(intArrayOf(1, 1, 0, 16))
|
while (true) {
|
||||||
send(intArrayOf(3, 0, 1, 10, 0, 10, 3))
|
val newCurrent = Optional.ofNullable(queue.poll()).orElse(current)
|
||||||
send(intArrayOf(3, 0, 0, 9, 0, 9, 3))
|
current = newCurrent
|
||||||
send(intArrayOf(0x04, 0x01))
|
send(current)
|
||||||
|
sleep(900)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closes the client socket and causes the thread to finish.
|
// Closes the client socket and causes the thread to finish.
|
||||||
|
@ -56,7 +120,15 @@ class Garmin(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readAll() {
|
fun enqueue(data: IntArray) {
|
||||||
|
queue.put(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun send(data: IntArray) {
|
||||||
|
sendRaw(prepareData(data))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun readAll() {
|
||||||
val buffer = ByteArray(64)
|
val buffer = ByteArray(64)
|
||||||
val istr = socket!!.inputStream
|
val istr = socket!!.inputStream
|
||||||
istr!!.let {
|
istr!!.let {
|
||||||
|
@ -66,10 +138,6 @@ class Garmin(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun send(hex: IntArray) {
|
|
||||||
sendRaw(prepareData(hex))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sendRaw(buff: IntArray) {
|
private fun sendRaw(buff: IntArray) {
|
||||||
buff.forEach { socket!!.outputStream.write(it) }
|
buff.forEach { socket!!.outputStream.write(it) }
|
||||||
socket!!.outputStream.flush()
|
socket!!.outputStream.flush()
|
||||||
|
|
|
@ -14,6 +14,15 @@ import androidx.activity.result.ActivityResultCallback
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.core.app.ActivityCompat
|
import androidx.core.app.ActivityCompat
|
||||||
|
import androidx.lifecycle.DefaultLifecycleObserver
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import com.mapbox.navigation.base.formatter.DistanceFormatterOptions
|
||||||
|
import com.mapbox.navigation.base.options.NavigationOptions
|
||||||
|
import com.mapbox.navigation.base.trip.model.RouteProgress
|
||||||
|
import com.mapbox.navigation.core.formatter.MapboxDistanceFormatter
|
||||||
|
import com.mapbox.navigation.core.lifecycle.MapboxNavigationApp
|
||||||
|
import com.mapbox.navigation.core.trip.session.RouteProgressObserver
|
||||||
|
import com.mapbox.navigation.ui.maneuver.api.MapboxManeuverApi
|
||||||
import eu.ztsh.garmin.databinding.ActivityMainBinding
|
import eu.ztsh.garmin.databinding.ActivityMainBinding
|
||||||
|
|
||||||
@SuppressLint("MissingPermission")
|
@SuppressLint("MissingPermission")
|
||||||
|
@ -21,15 +30,66 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
lateinit var garmin: Garmin
|
lateinit var garmin: Garmin
|
||||||
|
|
||||||
private lateinit var binding : ActivityMainBinding
|
private lateinit var binding: ActivityMainBinding
|
||||||
|
private val mapboxObserver = MapboxObserver()
|
||||||
|
|
||||||
|
init {
|
||||||
|
lifecycle.addObserver(object : DefaultLifecycleObserver {
|
||||||
|
override fun onResume(owner: LifecycleOwner) {
|
||||||
|
MapboxNavigationApp.attach(owner)
|
||||||
|
MapboxNavigationApp.registerObserver(mapboxObserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause(owner: LifecycleOwner) {
|
||||||
|
MapboxNavigationApp.detach(owner)
|
||||||
|
MapboxNavigationApp.unregisterObserver(mapboxObserver)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
// bluetoothInit()
|
if (!MapboxNavigationApp.isSetup()) {
|
||||||
|
MapboxNavigationApp.setup {
|
||||||
|
NavigationOptions.Builder(applicationContext)
|
||||||
|
.accessToken(BuildConfig.MAPBOX_DOWNLOADS_TOKEN)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MapboxNavigationApp.current()?.startTripSession()
|
||||||
|
bluetoothInit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
MapboxNavigationApp.current()?.registerRouteProgressObserver(routeProgressObserver)
|
||||||
|
}
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
MapboxNavigationApp.current()?.unregisterRouteProgressObserver(routeProgressObserver)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
MapboxNavigationApp.current()?.stopTripSession()
|
||||||
|
maneuverApi.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Define distance formatter options
|
||||||
|
private val distanceFormatter: DistanceFormatterOptions by lazy {
|
||||||
|
DistanceFormatterOptions.Builder(this).build()
|
||||||
|
}
|
||||||
|
// Create an instance of the Maneuver API
|
||||||
|
private val maneuverApi: MapboxManeuverApi by lazy {
|
||||||
|
MapboxManeuverApi(MapboxDistanceFormatter(distanceFormatter))
|
||||||
|
}
|
||||||
|
|
||||||
|
private val routeProgressObserver =
|
||||||
|
RouteProgressObserver { routeProgress -> maneuverApi.getManeuvers(routeProgress).value?.apply { garmin.process(this[0]) } }
|
||||||
|
|
||||||
private fun bluetoothInit() {
|
private fun bluetoothInit() {
|
||||||
val bluetoothManager: BluetoothManager = getSystemService(BluetoothManager::class.java)
|
val bluetoothManager: BluetoothManager = getSystemService(BluetoothManager::class.java)
|
||||||
val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
|
val bluetoothAdapter: BluetoothAdapter = bluetoothManager.adapter
|
||||||
|
|
66
app/src/main/java/eu/ztsh/garmin/ManeuverMapper.kt
Normal file
66
app/src/main/java/eu/ztsh/garmin/ManeuverMapper.kt
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
package eu.ztsh.garmin
|
||||||
|
|
||||||
|
import com.mapbox.navigation.ui.maneuver.model.Maneuver
|
||||||
|
|
||||||
|
class ManeuverMapper {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun apply(maneuver: Maneuver): State {
|
||||||
|
val state = State()
|
||||||
|
maneuver.apply {
|
||||||
|
this.primary.apply {
|
||||||
|
state.direction = Direction()
|
||||||
|
when (this.type) {
|
||||||
|
"turn" -> {
|
||||||
|
state.direction.outType = OutType.Lane
|
||||||
|
state.direction.roundabout = OutAngle.AsDirection
|
||||||
|
}
|
||||||
|
|
||||||
|
"roundabout" -> {
|
||||||
|
state.direction.outType = OutType.RightRoundabout
|
||||||
|
}
|
||||||
|
|
||||||
|
"arrive" -> {
|
||||||
|
state.flag = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
when (this.modifier) {
|
||||||
|
"right" -> {
|
||||||
|
when (this.type) {
|
||||||
|
"turn" -> state.direction.outAngle = OutAngle.Right
|
||||||
|
"roundabout" -> {
|
||||||
|
when (this.degrees) {
|
||||||
|
137.0 -> state.direction.outAngle = OutAngle.EasyRight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"left" -> {
|
||||||
|
when (this.type) {
|
||||||
|
"turn" -> state.direction.outAngle = OutAngle.Left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.stepDistance.apply {
|
||||||
|
this.distanceRemaining?.apply {
|
||||||
|
distanceFormatter.formatDistance(distanceRemaining!!).split(" ").apply {
|
||||||
|
state.distance = this[0].toInt()
|
||||||
|
state.unit = when (this[1]) {
|
||||||
|
"m" -> Unit.Metres
|
||||||
|
"km" -> Unit.Kilometres
|
||||||
|
else -> {Unit.Any}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
21
app/src/main/java/eu/ztsh/garmin/MapboxObserver.kt
Normal file
21
app/src/main/java/eu/ztsh/garmin/MapboxObserver.kt
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
package eu.ztsh.garmin
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
|
import com.mapbox.navigation.core.MapboxNavigation
|
||||||
|
import com.mapbox.navigation.core.lifecycle.MapboxNavigationObserver
|
||||||
|
|
||||||
|
class MapboxObserver : MapboxNavigationObserver {
|
||||||
|
|
||||||
|
override fun onAttached(mapboxNavigation: MapboxNavigation) {
|
||||||
|
Log.d(TAG, "Attached")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDetached(mapboxNavigation: MapboxNavigation) {
|
||||||
|
Log.d(TAG, "Detached")
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TAG = "MBOXOBS"
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
96
app/src/main/java/eu/ztsh/garmin/Model.kt
Normal file
96
app/src/main/java/eu/ztsh/garmin/Model.kt
Normal file
|
@ -0,0 +1,96 @@
|
||||||
|
package eu.ztsh.garmin
|
||||||
|
|
||||||
|
enum class OutType(val data: Int) {
|
||||||
|
|
||||||
|
Off(0x00),
|
||||||
|
Lane(0x01),
|
||||||
|
LongerLane(0x02),
|
||||||
|
LeftRoundabout(0x04),
|
||||||
|
RightRoundabout(0x08),
|
||||||
|
Flag(0x40),
|
||||||
|
ArrowOnly(0x80);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum class OutAngle(val data: Int) {
|
||||||
|
|
||||||
|
Down(0x01),
|
||||||
|
SharpRight(0x02),
|
||||||
|
Right(0x04),
|
||||||
|
EasyRight(0x08),
|
||||||
|
Straight(0x10),
|
||||||
|
EasyLeft(0x20),
|
||||||
|
Left(0x40),
|
||||||
|
SharpLeft(0x80),
|
||||||
|
LeftDown(0x81),
|
||||||
|
RightDown(0x82),
|
||||||
|
AsDirection(0x00)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Unit(val data: Int) {
|
||||||
|
|
||||||
|
Any(0),
|
||||||
|
Metres(1),
|
||||||
|
Kilometres(3),
|
||||||
|
Miles(5),
|
||||||
|
Foot(8)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class Lane(val data: Int) {
|
||||||
|
|
||||||
|
DotsRight(0x01),
|
||||||
|
OuterRight(0x02),
|
||||||
|
MiddleRight(0x04),
|
||||||
|
InnerRight(0x08),
|
||||||
|
InnerLeft(0x10),
|
||||||
|
MiddleLeft(0x20),
|
||||||
|
OuterLeft(0x40),
|
||||||
|
DotsLeft(0x80)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class State {
|
||||||
|
|
||||||
|
var lineArrows: Int = 0
|
||||||
|
var lineOutlines: Int = 0
|
||||||
|
var direction = Direction()
|
||||||
|
var distance: Int = 0
|
||||||
|
var unit: Unit = Unit.Any
|
||||||
|
var speed: Int = 0
|
||||||
|
var limit: Int = 0
|
||||||
|
var hours: Int = 0
|
||||||
|
var minutes: Int = 0
|
||||||
|
var traffic: Boolean = false
|
||||||
|
var flag: Boolean = false
|
||||||
|
var control: Boolean = false
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class Direction {
|
||||||
|
var outAngle: OutAngle = OutAngle.AsDirection
|
||||||
|
var outType: OutType = OutType.Lane
|
||||||
|
var roundabout: OutAngle = OutAngle.AsDirection
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
if (this === other) return true
|
||||||
|
if (javaClass != other?.javaClass) return false
|
||||||
|
|
||||||
|
other as Direction
|
||||||
|
|
||||||
|
if (outAngle != other.outAngle) return false
|
||||||
|
if (outType != other.outType) return false
|
||||||
|
if (roundabout != other.roundabout) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hashCode(): Int {
|
||||||
|
var result = outAngle.hashCode()
|
||||||
|
result = 31 * result + outType.hashCode()
|
||||||
|
result = 31 * result + roundabout.hashCode()
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue