Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation mode #3335

Merged
merged 7 commits into from
Oct 2, 2021
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
47 changes: 47 additions & 0 deletions app/src/main/assets/map_theme/jawg/streetcomplete.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
global:
geometry_color: '#44D14000' # accent color + alpha
track_color: '#44536dfe'
old_track_color: '#22536dfe'

textures:
pins:
Expand Down Expand Up @@ -40,6 +42,9 @@ styles:
geometry-points:
base: points
blend: overlay
track-lines:
base: lines
blend: overlay

layers:
streetcomplete_selected_pins:
Expand Down Expand Up @@ -114,3 +119,45 @@ layers:
size: 32px
collide: false
order: 1000
# streetcomplete_track and streetcomplete_track2 layers are exactly the same except the source.
# It is not possible in tangram to define a layer for several sources.
streetcomplete_track:
data: { source: streetcomplete_track }
current:
filter: { old: [false] }
draw:
track-lines:
color: global.track_color
width: [[14, 6px],[18, 12px]]
collide: false
join: round
order: 1000
old:
filter: { old: [true] }
draw:
track-lines:
color: global.old_track_color
width: [[14, 6px],[18, 12px]]
collide: false
join: round
order: 1000
streetcomplete_track2:
data: { source: streetcomplete_track2 }
current:
filter: { old: [false] }
draw:
track-lines:
color: global.track_color
width: [[14, 6px],[18, 12px]]
collide: false
join: round
order: 1000
old:
filter: { old: [true] }
draw:
track-lines:
color: global.old_track_color
width: [[14, 6px],[18, 12px]]
collide: false
join: round
order: 1000
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ class FineLocationManager(private val mgr: LocationManager, private var location

// taken from https://developer.android.com/guide/topics/location/strategies.html#kotlin

private const val TWO_MINUTES: Long = 1000 * 60 * 2
private const val TWO_MINUTES = 1000L * 60 * 2

/** Determines whether one Location reading is better than the current Location fix
* @param location The new Location that you want to evaluate
Expand All @@ -74,19 +74,19 @@ private fun isBetterLocation(location: Location, currentBestLocation: Location?)
}

// Check whether the new location fix is newer or older
val timeDelta: Long = location.time - currentBestLocation.time
val isSignificantlyNewer: Boolean = timeDelta > TWO_MINUTES
val isSignificantlyOlder:Boolean = timeDelta < -TWO_MINUTES
val isNewer: Boolean = timeDelta > 0L
val timeDelta = location.time - currentBestLocation.time
val isSignificantlyNewer = timeDelta > TWO_MINUTES
val isSignificantlyOlder = timeDelta < -TWO_MINUTES
val isNewer = timeDelta > 0L

// Check whether the new location fix is more or less accurate
val accuracyDelta: Float = location.accuracy - currentBestLocation.accuracy
val isLessAccurate: Boolean = accuracyDelta > 0f
val isMoreAccurate: Boolean = accuracyDelta < 0f
val isSignificantlyLessAccurate: Boolean = accuracyDelta > 200f
val accuracyDelta = location.accuracy - currentBestLocation.accuracy
val isLessAccurate = accuracyDelta > 0f
val isMoreAccurate = accuracyDelta < 0f
val isSignificantlyLessAccurate = accuracyDelta > 200f

// Check if the old and new location are from the same provider
val isFromSameProvider: Boolean = location.provider == currentBestLocation.provider
val isFromSameProvider = location.provider == currentBestLocation.provider

// Determine location quality using a combination of timeliness and accuracy
return when {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ enum class LocationState {

// receiving location updates
val isEnabled: Boolean get() = ordinal >= ENABLED.ordinal
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import android.view.View
import androidx.annotation.Keep
import androidx.appcompat.widget.AppCompatImageButton
import de.westnordost.streetcomplete.R
import java.util.Arrays
import de.westnordost.streetcomplete.location.LocationState.*
westnordost marked this conversation as resolved.
Show resolved Hide resolved

/**
* An image button which shows the current location state
Expand All @@ -23,37 +23,44 @@ class LocationStateButton @JvmOverloads constructor(
) : AppCompatImageButton(context, attrs, defStyle) {

var state: LocationState
get() = _state ?: LocationState.DENIED
get() = _state ?: DENIED
set(value) { _state = value }

// this is necessary because state is accessed before it is initialized (in contructor of super)
// this is necessary because state is accessed before it is initialized (in constructor of super)
private var _state: LocationState? = null
set(value) {
if (field != value) {
field = value
refreshDrawableState()
}
}

private val tint: ColorStateList?

var isNavigation: Boolean = false
set(value) {
if (field != value) {
field = value
refreshDrawableState()
}
}
westnordost marked this conversation as resolved.
Show resolved Hide resolved


init {
val a = context.obtainStyledAttributes(attrs, R.styleable.LocationStateButton)
state = determineStateFrom(a)
tint = a.getColorStateList(R.styleable.LocationStateButton_tint)
isNavigation = a.getBoolean(R.styleable.LocationStateButton_is_navigation, false)

a.recycle()
}

private fun determineStateFrom(a: TypedArray): LocationState {
if (a.getBoolean(R.styleable.LocationStateButton_state_updating,false))
return LocationState.UPDATING
if (a.getBoolean(R.styleable.LocationStateButton_state_searching,false))
return LocationState.SEARCHING
if (a.getBoolean(R.styleable.LocationStateButton_state_enabled,false))
return LocationState.ENABLED
if (a.getBoolean(R.styleable.LocationStateButton_state_allowed,false))
return LocationState.ALLOWED
else
return LocationState.DENIED
private fun determineStateFrom(a: TypedArray): LocationState = when {
a.getBoolean(R.styleable.LocationStateButton_state_updating,false) -> UPDATING
a.getBoolean(R.styleable.LocationStateButton_state_searching,false) -> SEARCHING
a.getBoolean(R.styleable.LocationStateButton_state_enabled,false) -> ENABLED
a.getBoolean(R.styleable.LocationStateButton_state_allowed,false) -> ALLOWED
else -> DENIED
}

override fun drawableStateChanged() {
Expand All @@ -69,19 +76,22 @@ class LocationStateButton @JvmOverloads constructor(
}

override fun onCreateDrawableState(extraSpace: Int): IntArray {
val additionalLength = STATES.size + 1
val drawableState = super.onCreateDrawableState(extraSpace + additionalLength)
val arrPos = state.ordinal
val additionalArray = Arrays.copyOf(Arrays.copyOf(STATES, arrPos), additionalLength)
View.mergeDrawableStates(drawableState, additionalArray)
val attributes = ArrayList<Int>()
attributes += state.styleableAttributes
if (isNavigation) attributes += R.attr.is_navigation

val drawableState = super.onCreateDrawableState(extraSpace + attributes.size)

View.mergeDrawableStates(drawableState, attributes.toIntArray())
return drawableState
}

public override fun onSaveInstanceState(): Parcelable? {
public override fun onSaveInstanceState(): Parcelable {
val superState = super.onSaveInstanceState()
val ss = SavedState(superState)
ss.state = state
ss.activated = isActivated
ss.navigation = isNavigation
return ss
}

Expand All @@ -90,23 +100,27 @@ class LocationStateButton @JvmOverloads constructor(
super.onRestoreInstanceState(ss.superState)
state = ss.state
isActivated = ss.activated
isNavigation = ss.navigation
requestLayout()
}

internal class SavedState : BaseSavedState {
var state: LocationState = LocationState.DENIED
var state: LocationState = DENIED
var activated = false
var navigation = false

constructor(superState: Parcelable?) : super(superState)
constructor(parcel: Parcel) : super(parcel) {
state = LocationState.valueOf(parcel.readString()!!)
activated = parcel.readInt() == 1
navigation = parcel.readInt() == 1
}

override fun writeToParcel(out: Parcel, flags: Int) {
super.writeToParcel(out, flags)
out.writeString(state.name)
out.writeInt(if (activated) 1 else 0)
out.writeInt(if (navigation) 1 else 0)
}

companion object {
Expand All @@ -117,14 +131,12 @@ class LocationStateButton @JvmOverloads constructor(
}
}
}

companion object {
// must be defined in the same order as the LocationState enum (but minus the first)
private val STATES = intArrayOf(
R.attr.state_allowed,
R.attr.state_enabled,
R.attr.state_searching,
R.attr.state_updating
)
}
}

private val LocationState.styleableAttributes: List<Int> get() =
listOf(
R.attr.state_allowed,
R.attr.state_enabled,
R.attr.state_searching,
R.attr.state_updating
).subList(0, ordinal)
15 changes: 15 additions & 0 deletions app/src/main/java/de/westnordost/streetcomplete/map/Bearing.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package de.westnordost.streetcomplete.map

import android.location.Location

/** Utility functions to estimate current bearing from a track. This is necessary because
* Location.bearingAccuracy doesn't exist on Android versions below Android API 26, otherwise
* a solution based on this would be less code. E.g. take bearing if accuracy < X */

fun getTrackBearing(track: List<Location>): Float? {
val last = track.lastOrNull() ?: return null
val point = track.findLast { it.distanceTo(last) > MIN_TRACK_DISTANCE_FOR_BEARING } ?: return null
return point.bearingTo(last)
}

private const val MIN_TRACK_DISTANCE_FOR_BEARING = 15f // 15 meters
Loading