Initial commit
This commit is contained in:
4
common/data/src/main/AndroidManifest.xml
Normal file
4
common/data/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.kouros.navigation.data
|
||||
|
||||
enum class ManeuverType(val value: Int) {
|
||||
None(0),
|
||||
Start(1),
|
||||
StartRight(2),
|
||||
StartLeft(3),
|
||||
Destination(4),
|
||||
DestinationRight(5),
|
||||
DestinationLeft(6),
|
||||
Becomes(7),
|
||||
Continue(8),
|
||||
SlightRight(9),
|
||||
Right(10),
|
||||
SharpRight(11),
|
||||
UturnRight(12),
|
||||
UturnLeft(13),
|
||||
SharpLeft(14),
|
||||
Left(15),
|
||||
SlightLeft(16),
|
||||
RampStraight(17),
|
||||
RampRight(18),
|
||||
RampLeft(19),
|
||||
ExitRight(20),
|
||||
ExitLeft(21),
|
||||
StayStraight(22),
|
||||
StayRight(23),
|
||||
StayLeft(24),
|
||||
Merge(25),
|
||||
RoundaboutEnter(26),
|
||||
RoundaboutExit(27),
|
||||
FerryEnter(28),
|
||||
FerryExit(29),
|
||||
Transit(30),
|
||||
TransitTransfer(31),
|
||||
TransitRemainOn(32),
|
||||
TransitConnectionStart(33),
|
||||
TransitConnectionTransfer(34),
|
||||
TransitConnectionDestination(35),
|
||||
PostTransitConnectionDestination(36),
|
||||
MergeRight(37),
|
||||
MergeLeft(38),
|
||||
ElevatorEnter(39),
|
||||
StepsEnter(40),
|
||||
EscalatorEnter(41),
|
||||
BuildingEnter(42),
|
||||
BuildingExit(43),
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kouros.navigation.data
|
||||
|
||||
import android.location.Location
|
||||
import org.json.JSONArray
|
||||
import java.net.Authenticator
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.PasswordAuthentication
|
||||
import java.net.URL
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
|
||||
class NavigationRepository {
|
||||
|
||||
private val placesUrl = "https://kouros-online.de/maps/placespwd";
|
||||
|
||||
private val routeUrl = "https://kouros-online.de/valhalla/route?json="
|
||||
|
||||
fun getRoute(currentLocation : Location, location: Location): String {
|
||||
val vLocation = listOf(
|
||||
Locations(lat = currentLocation.latitude, lon = currentLocation.longitude),
|
||||
Locations(lat = location.latitude, lon = location.longitude)
|
||||
)
|
||||
val valhallaLocation = ValhallaLocation(
|
||||
locations = vLocation,
|
||||
costing = "auto",
|
||||
units = "km",
|
||||
id = "my_work_route",
|
||||
language = "de-DE"
|
||||
)
|
||||
val routeLocation = Json.encodeToString(valhallaLocation)
|
||||
return fetchUrl(routeUrl + routeLocation)
|
||||
}
|
||||
|
||||
fun getPlaces(): List<Place> {
|
||||
val places: MutableList<Place> = ArrayList()
|
||||
val placesStr = fetchUrl(placesUrl)
|
||||
val jArray = JSONArray(placesStr)
|
||||
for (i in 0..<jArray.length()) {
|
||||
val json = jArray.getJSONObject(i)
|
||||
val place = Place(
|
||||
json.getString("id").toLong(),
|
||||
json.getString("name"),
|
||||
category = json.getString("category"),
|
||||
latitude = json.getDouble("latitude"),
|
||||
longitude = json.getDouble("longitude"),
|
||||
postalCode = json.getString("postalCode"),
|
||||
city = json.getString("city"),
|
||||
street = json.getString("street"),
|
||||
)
|
||||
places.add(place)
|
||||
}
|
||||
return places
|
||||
}
|
||||
|
||||
|
||||
private fun fetchUrl(url: String): String {
|
||||
try {
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||
return PasswordAuthentication(
|
||||
"kouros",
|
||||
"eo7sbjyWpmjSVFyELgbfrryqJ6ddNeq9".toCharArray()
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
println(url)
|
||||
val httpURLConnection = URL(url).openConnection() as HttpURLConnection
|
||||
httpURLConnection.setRequestProperty(
|
||||
"Accept",
|
||||
"application/json"
|
||||
) // The format of response we want to get from the server
|
||||
httpURLConnection.requestMethod = "GET"
|
||||
val responseCode = httpURLConnection.responseCode
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
val response = httpURLConnection.inputStream.bufferedReader()
|
||||
.use { it.readText() } // defaults to UTF-8
|
||||
return response
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println(e.message)
|
||||
}
|
||||
return ""
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.kouros.navigation.data
|
||||
|
||||
import android.content.Context
|
||||
import com.kouros.navigation.data.MyObjectBox
|
||||
import io.objectbox.BoxStore
|
||||
|
||||
/**
|
||||
* Singleton to keep BoxStore reference.
|
||||
*/
|
||||
object ObjectBox {
|
||||
|
||||
lateinit var boxStore: BoxStore
|
||||
private set
|
||||
|
||||
fun init(context: Context) {
|
||||
try {
|
||||
boxStore = MyObjectBox.builder().androidContext(context.applicationContext).build()
|
||||
} catch (e: Exception) {
|
||||
println(e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
138
common/data/src/main/java/com/kouros/navigation/data/Place.kt
Normal file
138
common/data/src/main/java/com/kouros/navigation/data/Place.kt
Normal file
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.kouros.navigation.data
|
||||
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import android.net.Uri
|
||||
import io.objectbox.annotation.Entity
|
||||
import io.objectbox.annotation.Id
|
||||
import io.objectbox.annotation.Index
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
data class Category(
|
||||
val id: String,
|
||||
val name: String,
|
||||
)
|
||||
|
||||
@Entity
|
||||
data class Place(
|
||||
@Id
|
||||
var id: Long = 0,
|
||||
var name: String? = null,
|
||||
var category: String? = null,
|
||||
var latitude: Double = 0.0,
|
||||
var longitude: Double = 0.0,
|
||||
var postalCode: String? = null,
|
||||
var city: String? = null,
|
||||
var street: String? = null,
|
||||
var distance: Float = 0F,
|
||||
@Transient
|
||||
var avatar: Uri? = null
|
||||
)
|
||||
|
||||
data class ContactData(
|
||||
val contactId: Long,
|
||||
val name: String,
|
||||
val address: String,
|
||||
val avatar: Uri?
|
||||
)
|
||||
|
||||
|
||||
//val places = mutableListOf<Place>()
|
||||
/* Place(
|
||||
id = 0,
|
||||
name = "Vogelhartstr. 17",
|
||||
category = "Favorites",
|
||||
latitude = 48.1857475,
|
||||
longitude = 11.5793627,
|
||||
postalCode = "80807",
|
||||
city = "München",
|
||||
street = "Vogelhartstr. 17"
|
||||
|
||||
),
|
||||
Place(
|
||||
id = 0,
|
||||
name = "Hohenwaldeckstr. 27",
|
||||
category = "Recent",
|
||||
latitude = 48.1165005,
|
||||
longitude = 11.594349,
|
||||
postalCode = "81541",
|
||||
city = "München",
|
||||
street = "Hohenwaldeckstr. 27",
|
||||
)
|
||||
) */
|
||||
|
||||
// GeoJSON data classes
|
||||
@Serializable
|
||||
data class GeoJsonLineString(
|
||||
val type: String,
|
||||
val coordinates: List<List<Double>>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GeoJsonFeature(
|
||||
val type: String,
|
||||
val geometry: GeoJsonLineString
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GeoJsonFeatureCollection(
|
||||
val type: String,
|
||||
val features: List<GeoJsonFeature>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class Locations (
|
||||
var lat : Double,
|
||||
var lon : Double,
|
||||
var street : String = ""
|
||||
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ValhallaLocation (
|
||||
var locations: List<Locations>,
|
||||
var costing: String,
|
||||
var units: String,
|
||||
var id: String,
|
||||
var language: String
|
||||
)
|
||||
|
||||
object Constants {
|
||||
|
||||
const val TAG: String = "Navigation"
|
||||
|
||||
const val CONTACTS: String = "Contacts"
|
||||
|
||||
const val RECENT: String = "Recent"
|
||||
|
||||
/** The initial location to use as an anchor for searches. */
|
||||
val homeLocation: Location = Location(LocationManager.GPS_PROVIDER)
|
||||
val home2Location: Location = Location(LocationManager.GPS_PROVIDER)
|
||||
|
||||
init {
|
||||
// Vogelhartstr. 17
|
||||
homeLocation.latitude = 48.185749
|
||||
homeLocation.longitude = 11.5793748
|
||||
// Hohenwaldeckstr. 27
|
||||
home2Location.latitude = 48.1164817
|
||||
home2Location.longitude = 11.594322
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.kouros.navigation.model
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.provider.ContactsContract
|
||||
import com.kouros.navigation.data.ContactData
|
||||
|
||||
|
||||
class Contacts(private var context: Context) {
|
||||
|
||||
@SuppressLint("Range")
|
||||
fun retrieveContacts(): List<ContactData> {
|
||||
val contentResolver = context.contentResolver
|
||||
val projection: Array<out String> = arrayOf(
|
||||
ContactsContract.Data.CONTACT_ID,
|
||||
ContactsContract.Data.MIMETYPE,
|
||||
ContactsContract.Data.DATA1,
|
||||
ContactsContract.Data.DATA2,
|
||||
ContactsContract.Contacts.DISPLAY_NAME,
|
||||
)
|
||||
val cursor = contentResolver.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
projection,
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
var address = ""
|
||||
val result: MutableList<ContactData> = mutableListOf()
|
||||
cursor?.apply {
|
||||
while (moveToNext()) {
|
||||
val contactId = getLong(getColumnIndex(ContactsContract.Data.CONTACT_ID))
|
||||
val name = getString(getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME))
|
||||
if (name.contains("Jola") || name.contains("Dominic")
|
||||
|| name.contains("Μεντή")
|
||||
|| name.contains("David")) {
|
||||
val mimeType: String = getString(getColumnIndex(ContactsContract.Data.MIMETYPE))
|
||||
if (mimeType == ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE) {
|
||||
address = getString(getColumnIndex(ContactsContract.Data.DATA1))
|
||||
val avatar = retrieveAvatar(context, contactId)
|
||||
result.add(ContactData(contactId, name, address, avatar))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
cursor?.close()
|
||||
return result
|
||||
}
|
||||
|
||||
private fun retrieveAvatar(context: Context, contactId: Long): Uri? {
|
||||
return context.contentResolver.query(
|
||||
ContactsContract.Data.CONTENT_URI,
|
||||
null,
|
||||
"${ContactsContract.Data.CONTACT_ID} =? AND ${ContactsContract.Data.MIMETYPE} = '${ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE}'",
|
||||
arrayOf(contactId.toString()),
|
||||
null
|
||||
)?.use {
|
||||
if (it.moveToFirst()) {
|
||||
val contactUri = ContentUris.withAppendedId(
|
||||
ContactsContract.Contacts.CONTENT_URI,
|
||||
contactId
|
||||
)
|
||||
Uri.withAppendedPath(
|
||||
contactUri,
|
||||
ContactsContract.Contacts.Photo.CONTENT_DIRECTORY
|
||||
)
|
||||
} else null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package com.kouros.navigation.model
|
||||
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.utils.NavigationUtils.Utils.createGeoJson
|
||||
import com.kouros.navigation.utils.NavigationUtils.Utils.decodePolyline
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
|
||||
open class RouteModel {
|
||||
|
||||
var polylineLocations: List<List<Double>> = emptyList()
|
||||
|
||||
lateinit var maneuvers: JSONArray
|
||||
lateinit var locations: JSONArray
|
||||
lateinit var summary: JSONObject
|
||||
|
||||
lateinit var destination: Place
|
||||
var navigating = false
|
||||
|
||||
var arrived = false
|
||||
|
||||
var maneuverIndex = 0
|
||||
|
||||
var maneuverType = 0
|
||||
|
||||
var currentIndex = 0
|
||||
|
||||
var distanceToStepEnd = 0F
|
||||
|
||||
var beginIndex = 0
|
||||
|
||||
var endIndex = 0
|
||||
var routingManeuvers = mutableListOf<JSONObject>()
|
||||
|
||||
var distanceToRoute = 0F
|
||||
|
||||
var geoJson = ""
|
||||
|
||||
private fun decodeValhallaRoute(route: String) {
|
||||
if (route.isEmpty() || route == "[]") {
|
||||
return;
|
||||
}
|
||||
val jObject = JSONObject(route)
|
||||
val trip = jObject.getJSONObject("trip")
|
||||
locations = trip.getJSONArray("locations")
|
||||
val legs = trip.getJSONArray("legs")
|
||||
summary = trip.getJSONObject("summary")
|
||||
maneuvers = legs.getJSONObject(0).getJSONArray("maneuvers")
|
||||
val shape = legs.getJSONObject(0).getString("shape")
|
||||
polylineLocations = decodePolyline(shape)
|
||||
}
|
||||
|
||||
fun createNavigationRoute(route: String) {
|
||||
decodeValhallaRoute(route)
|
||||
for (i in 0..<maneuvers.length()) {
|
||||
val maneuver = (maneuvers[i] as JSONObject)
|
||||
routingManeuvers.add(maneuver)
|
||||
}
|
||||
geoJson = createGeoJson(polylineLocations)
|
||||
navigating = true
|
||||
}
|
||||
|
||||
val currentDistance: Double
|
||||
/** Returns the current [Step] with information such as the cue text and images. */
|
||||
get() {
|
||||
return ((leftStepDistance() * 1000).roundToInt().toDouble() / 10.0).roundToInt() * 10.0
|
||||
}
|
||||
|
||||
fun findManeuver(location: Location) {
|
||||
var nearestDistance = 100000.0f
|
||||
maneuverIndex = -1
|
||||
// find maneuver
|
||||
for (i in 0..<maneuvers.length()) {
|
||||
val maneuver = (maneuvers[i] as JSONObject)
|
||||
val beginShapeIndex = maneuver.getString("begin_shape_index").toInt()
|
||||
val endShapeIndex = maneuver.getString("end_shape_index").toInt()
|
||||
val distance = calculateDistance(beginShapeIndex, endShapeIndex, location)
|
||||
if (distance < nearestDistance) {
|
||||
nearestDistance = distance
|
||||
maneuverIndex = i
|
||||
calculateCurrentIndex(beginShapeIndex, endShapeIndex, location)
|
||||
}
|
||||
}
|
||||
distanceToRoute = nearestDistance
|
||||
}
|
||||
|
||||
|
||||
/** Calculates the index in a maneuver. */
|
||||
private fun calculateCurrentIndex(
|
||||
beginShapeIndex: Int,
|
||||
endShapeIndex: Int,
|
||||
location: Location
|
||||
) {
|
||||
var nearestLocation = 100000.0f
|
||||
for (i in beginShapeIndex..endShapeIndex) {
|
||||
val polylineLocation = Location(LocationManager.GPS_PROVIDER)
|
||||
polylineLocation.longitude = polylineLocations[i][0]
|
||||
polylineLocation.latitude = polylineLocations[i][1]
|
||||
val distance: Float = location.distanceTo(polylineLocation)
|
||||
if (distance < nearestLocation) {
|
||||
nearestLocation = distance
|
||||
currentIndex = i
|
||||
beginIndex = beginShapeIndex
|
||||
endIndex = endShapeIndex
|
||||
distanceToStepEnd = 0F
|
||||
for (j in i + 1..endShapeIndex) {
|
||||
val loc1 = Location(LocationManager.GPS_PROVIDER)
|
||||
val loc2 = Location(LocationManager.GPS_PROVIDER)
|
||||
loc1.longitude = polylineLocations[j - 1][0]
|
||||
loc1.latitude = polylineLocations[j - 1][1]
|
||||
loc2.longitude = polylineLocations[j][0]
|
||||
loc2.latitude = polylineLocations[j][1]
|
||||
distanceToStepEnd += loc1.distanceTo(loc2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateDistance(
|
||||
beginShapeIndex: Int,
|
||||
endShapeIndex: Int,
|
||||
location: Location
|
||||
): Float {
|
||||
var nearestLocation = 100000.0f
|
||||
for (i in beginShapeIndex..endShapeIndex) {
|
||||
val polylineLocation = Location(LocationManager.GPS_PROVIDER)
|
||||
polylineLocation.longitude = polylineLocations[i][0]
|
||||
polylineLocation.latitude = polylineLocations[i][1]
|
||||
val distance: Float = location.distanceTo(polylineLocation)
|
||||
if (distance < nearestLocation) {
|
||||
nearestLocation = distance
|
||||
}
|
||||
}
|
||||
return nearestLocation
|
||||
}
|
||||
|
||||
fun travelLeftTime(): Double {
|
||||
var timeLeft = 0.0
|
||||
for (i in maneuverIndex + 1..<routingManeuvers.size) {
|
||||
val maneuver = routingManeuvers[i]
|
||||
timeLeft += maneuver.getDouble("time")
|
||||
}
|
||||
if (endIndex > 0) {
|
||||
val maneuver = routingManeuvers[maneuverIndex]
|
||||
val curTime = maneuver.getDouble("time")
|
||||
val percent = 100 * (endIndex - currentIndex) / (endIndex - beginIndex)
|
||||
val time = curTime * percent / 100
|
||||
timeLeft += time
|
||||
}
|
||||
return timeLeft
|
||||
}
|
||||
|
||||
/** Returns the current [Step] left distance in km. */
|
||||
fun leftStepDistance(): Double {
|
||||
val maneuver = routingManeuvers[maneuverIndex]
|
||||
var leftDistance = maneuver.getDouble("length")
|
||||
if (endIndex > 0) {
|
||||
val percent = 100 * (endIndex - currentIndex) / (endIndex - beginIndex)
|
||||
//leftDistance = leftDistance * percent / 100
|
||||
leftDistance = (distanceToStepEnd / 1000).toDouble()
|
||||
}
|
||||
return leftDistance
|
||||
}
|
||||
|
||||
fun travelLeftDistance(): Double {
|
||||
var leftDistance = 0.0
|
||||
for (i in maneuverIndex + 1..<routingManeuvers.size) {
|
||||
val maneuver = routingManeuvers[i]
|
||||
leftDistance += maneuver.getDouble("length")
|
||||
}
|
||||
if (endIndex > 0) {
|
||||
val maneuver = routingManeuvers[maneuverIndex]
|
||||
val curDistance = maneuver.getDouble("length")
|
||||
val percent = 100 * (endIndex - currentIndex) / (endIndex - beginIndex)
|
||||
val time = curDistance * percent / 100
|
||||
leftDistance += time
|
||||
}
|
||||
return leftDistance
|
||||
}
|
||||
|
||||
fun isNavigating(): Boolean {
|
||||
return navigating
|
||||
}
|
||||
|
||||
fun isArrived(): Boolean {
|
||||
return arrived
|
||||
}
|
||||
|
||||
fun stopNavigating() {
|
||||
navigating = false
|
||||
polylineLocations = mutableListOf()
|
||||
routingManeuvers = mutableListOf()
|
||||
geoJson = ""
|
||||
maneuverIndex = 0
|
||||
currentIndex = 0
|
||||
distanceToStepEnd = 0F
|
||||
distanceToRoute = 0F
|
||||
beginIndex = 0
|
||||
endIndex = 0
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.kouros.navigation.model
|
||||
|
||||
import android.content.Context
|
||||
import android.location.Geocoder
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.kouros.navigation.data.Constants
|
||||
import com.kouros.navigation.data.NavigationRepository
|
||||
import com.kouros.navigation.data.ObjectBox.boxStore
|
||||
import com.kouros.navigation.data.Place
|
||||
import com.kouros.navigation.data.Place_
|
||||
import io.objectbox.kotlin.boxFor
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class ViewModel(private val repository: NavigationRepository) : ViewModel() {
|
||||
|
||||
val route: MutableLiveData<String> by lazy {
|
||||
MutableLiveData<String>()
|
||||
}
|
||||
|
||||
val previewRoute: MutableLiveData<String> by lazy {
|
||||
MutableLiveData<String>()
|
||||
}
|
||||
|
||||
val places: MutableLiveData<List<Place>> by lazy {
|
||||
MutableLiveData<List<Place>>()
|
||||
}
|
||||
|
||||
val contactAddress: MutableLiveData<List<Place>> by lazy {
|
||||
MutableLiveData<List<Place>>()
|
||||
}
|
||||
|
||||
|
||||
fun loadPlaces(location: Location) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val pl = mutableListOf<Place>()
|
||||
val placeBox = boxStore.boxFor(Place::class)
|
||||
pl.addAll(placeBox.all)
|
||||
for (place in pl) {
|
||||
val plLocation = Location(LocationManager.GPS_PROVIDER)
|
||||
plLocation.longitude = place.longitude
|
||||
plLocation.latitude = place.latitude
|
||||
val distance: Float = location.distanceTo(plLocation)
|
||||
place.distance = distance / 1000
|
||||
}
|
||||
places.postValue(pl)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadRoute(currentLocation: Location, location: Location) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
route.postValue(repository.getRoute(currentLocation, location))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadPreviewRoute(currentLocation: Location, location: Location) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
previewRoute.postValue(repository.getRoute(currentLocation, location))
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun loadContacts(context: Context) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val geocoder = Geocoder(context)
|
||||
val contactList = mutableListOf<Place>()
|
||||
val contacts = Contacts(context = context)
|
||||
val addresses = contacts.retrieveContacts()
|
||||
for (address in addresses) {
|
||||
val lines = address.address.split("\n")
|
||||
geocoder.getFromLocationName(
|
||||
address.address, 5) {
|
||||
for (adr in it) {
|
||||
contactList.add(
|
||||
Place(
|
||||
id = address.contactId,
|
||||
name = address.name + " " + lines[0] + " " + lines[1],
|
||||
Constants.CONTACTS,
|
||||
street = lines[0],
|
||||
city = lines[1],
|
||||
latitude = adr.latitude,
|
||||
longitude = adr.longitude,
|
||||
avatar = address.avatar
|
||||
)
|
||||
)
|
||||
}
|
||||
contactAddress.postValue(contactList)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun saveRecent(place: Place) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
place.category = Constants.RECENT
|
||||
try {
|
||||
val placeBox = boxStore.boxFor(Place::class)
|
||||
val query = placeBox
|
||||
.query(Place_.name.equal(place.name!!))
|
||||
.build()
|
||||
val results = query.find()
|
||||
query.close()
|
||||
if (results.isEmpty()) {
|
||||
placeBox.put(place)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteRecent(place: Place) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
place.category = Constants.RECENT
|
||||
try {
|
||||
val placeBox = boxStore.boxFor(Place::class)
|
||||
val query = placeBox
|
||||
.query(Place_.name.equal(place.name!!))
|
||||
.build()
|
||||
val results = query.find()
|
||||
query.close()
|
||||
if (results.isNotEmpty()) {
|
||||
placeBox.remove(place)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.kouros.navigation.utils
|
||||
|
||||
import android.location.Location
|
||||
import android.location.LocationManager
|
||||
import com.kouros.navigation.data.GeoJsonFeature
|
||||
import com.kouros.navigation.data.GeoJsonFeatureCollection
|
||||
import com.kouros.navigation.data.GeoJsonLineString
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.lang.Math.toDegrees
|
||||
import java.lang.Math.toRadians
|
||||
import kotlin.math.asin
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sin
|
||||
|
||||
class NavigationUtils() {
|
||||
object Utils {
|
||||
fun decodePolyline(encoded: String, vararg precisionOptional: Int): List<List<Double>> {
|
||||
val precision = if (precisionOptional.isNotEmpty()) precisionOptional[0] else 6
|
||||
val factor = 10.0.pow(precision)
|
||||
|
||||
var lat = 0
|
||||
var lng = 0
|
||||
val coordinates = mutableListOf<List<Double>>()
|
||||
var index = 0
|
||||
|
||||
while (index < encoded.length) {
|
||||
var byte = 0x20
|
||||
var shift = 0
|
||||
var result = 0
|
||||
while (byte >= 0x20) {
|
||||
byte = encoded[index].code - 63
|
||||
result = result or ((byte and 0x1f) shl shift)
|
||||
shift += 5
|
||||
index++
|
||||
}
|
||||
lat += if ((result and 1) > 0) (result shr 1).inv() else (result shr 1)
|
||||
|
||||
byte = 0x20
|
||||
shift = 0
|
||||
result = 0
|
||||
while (byte >= 0x20) {
|
||||
byte = encoded[index].code - 63
|
||||
result = result or ((byte and 0x1f) shl shift)
|
||||
shift += 5
|
||||
index++
|
||||
}
|
||||
lng += if ((result and 1) > 0) (result shr 1).inv() else (result shr 1)
|
||||
coordinates.add(listOf(lng.toDouble() / factor, lat.toDouble() / factor))
|
||||
}
|
||||
|
||||
return coordinates
|
||||
}
|
||||
|
||||
fun createGeoJson(lineCoordinates: List<List<Double>>): String {
|
||||
val lineString = GeoJsonLineString(type = "LineString", coordinates = lineCoordinates)
|
||||
val feature = GeoJsonFeature(type = "Feature", geometry = lineString)
|
||||
val featureCollection =
|
||||
GeoJsonFeatureCollection(type = "FeatureCollection", features = listOf(feature))
|
||||
val jsonString = Json.Default.encodeToString(featureCollection)
|
||||
return jsonString
|
||||
}
|
||||
|
||||
fun getBoundingBox(
|
||||
lat: Double,
|
||||
lon: Double,
|
||||
radius: Double
|
||||
): Map<String, Map<String, Double>> {
|
||||
val earthRadius = 6371.0
|
||||
val maxLat = lat + Math.toDegrees(radius / earthRadius)
|
||||
val minLat = lat - Math.toDegrees(radius / earthRadius)
|
||||
val maxLon = lon + Math.toDegrees(radius / earthRadius / cos(Math.toRadians(lat)))
|
||||
val minLon = lon - Math.toDegrees(radius / earthRadius / cos(Math.toRadians(lat)))
|
||||
|
||||
return mapOf(
|
||||
"nw" to mapOf("lat" to maxLat, "lon" to minLon),
|
||||
"ne" to mapOf("lat" to maxLat, "lon" to maxLon),
|
||||
"sw" to mapOf("lat" to minLat, "lon" to minLon),
|
||||
"se" to mapOf("lat" to minLat, "lon" to maxLon)
|
||||
)
|
||||
}
|
||||
|
||||
fun computeOffset(from: Location, distance: Double, heading: Double): Location {
|
||||
val earthRadius = 6371009.0
|
||||
var distance = distance
|
||||
var heading = heading
|
||||
distance /= earthRadius
|
||||
heading = toRadians(heading)
|
||||
val fromLat: Double = toRadians(from.latitude)
|
||||
val fromLng: Double = toRadians(from.longitude)
|
||||
val cosDistance: Double = cos(distance)
|
||||
val sinDistance = sin(distance)
|
||||
val sinFromLat = sin(fromLat)
|
||||
val cosFromLat: Double = cos(fromLat)
|
||||
val sinLat: Double = cosDistance * sinFromLat + sinDistance * cosFromLat * cos(heading)
|
||||
val dLng: Double = atan2(
|
||||
sinDistance * cosFromLat * sin(heading),
|
||||
cosDistance - sinFromLat * sinLat
|
||||
)
|
||||
val snap = Location(LocationManager.GPS_PROVIDER)
|
||||
snap.latitude = toDegrees(asin(sinLat))
|
||||
snap.longitude = toDegrees(fromLng + dLng)
|
||||
return snap
|
||||
//return LatLng(toDegrees(asin(sinLat)), toDegrees(fromLng + dLng))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user