checkpoint

This commit is contained in:
voussoir 2023-03-09 21:34:02 -08:00
parent df77b089ac
commit 04fa76249b
7 changed files with 197 additions and 191 deletions

View file

@ -58,7 +58,7 @@ class MapFragment : Fragment()
// https://gist.github.com/Dvik/a3de88d39da9d1d6d175025a56c5e797#file-viewextension-kt and // https://gist.github.com/Dvik/a3de88d39da9d1d6d175025a56c5e797#file-viewextension-kt and
// https://proandroiddev.com/android-full-screen-ui-with-transparent-status-bar-ef52f3adde63 // https://proandroiddev.com/android-full-screen-ui-with-transparent-status-bar-ef52f3adde63
// get current best location // get current best location
currentBestLocation = LocationHelper.getLastKnownLocation(activity as Context) currentBestLocation = getLastKnownLocation(activity as Context)
// get saved tracking state // get saved tracking state
trackingState = PreferencesHelper.loadTrackingState() trackingState = PreferencesHelper.loadTrackingState()
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

View file

@ -20,7 +20,9 @@ import YesNoDialog
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.preference.* import androidx.preference.*
@ -118,6 +120,10 @@ class SettingsFragment : PreferenceFragmentCompat(), YesNoDialog.YesNoDialogList
preferenceDeviceID.summary = getString(R.string.pref_device_id_summary) + "\n" + PreferencesHelper.load_device_id() preferenceDeviceID.summary = getString(R.string.pref_device_id_summary) + "\n" + PreferencesHelper.load_device_id()
preferenceDeviceID.setDefaultValue(random_device_id()) preferenceDeviceID.setDefaultValue(random_device_id())
preferenceCategoryGeneral.contains(preferenceDeviceID) preferenceCategoryGeneral.contains(preferenceDeviceID)
preferenceDeviceID.setOnPreferenceChangeListener { preference, newValue ->
preferenceDeviceID.summary = getString(R.string.pref_device_id_summary) + "\n" + newValue
return@setOnPreferenceChangeListener true
}
screen.addPreference(preferenceDeviceID) screen.addPreference(preferenceDeviceID)
val preferenceCategoryAbout: PreferenceCategory = PreferenceCategory(context) val preferenceCategoryAbout: PreferenceCategory = PreferenceCategory(context)

View file

@ -57,7 +57,7 @@ class TrackerService: Service(), SensorEventListener
var device_id: String = random_device_id() var device_id: String = random_device_id()
var recording_started: Date = GregorianCalendar.getInstance().time var recording_started: Date = GregorianCalendar.getInstance().time
var commitInterval: Int = Keys.COMMIT_INTERVAL var commitInterval: Int = Keys.COMMIT_INTERVAL
var currentBestLocation: Location = LocationHelper.getDefaultLocation() var currentBestLocation: Location = getDefaultLocation()
var lastCommit: Date = Keys.DEFAULT_DATE var lastCommit: Date = Keys.DEFAULT_DATE
var stepCountOffset: Float = 0f var stepCountOffset: Float = 0f
lateinit var track: Track lateinit var track: Track
@ -74,7 +74,6 @@ class TrackerService: Service(), SensorEventListener
private lateinit var gpsLocationListener: LocationListener private lateinit var gpsLocationListener: LocationListener
private lateinit var networkLocationListener: LocationListener private lateinit var networkLocationListener: LocationListener
/* Adds a GPS location listener to location manager */
private fun addGpsLocationListener() private fun addGpsLocationListener()
{ {
if (gpsLocationListenerRegistered) if (gpsLocationListenerRegistered)
@ -83,7 +82,7 @@ class TrackerService: Service(), SensorEventListener
return return
} }
gpsProviderActive = LocationHelper.isGpsEnabled(locationManager) gpsProviderActive = isGpsEnabled(locationManager)
if (! gpsProviderActive) if (! gpsProviderActive)
{ {
LogHelper.w(TAG, "Device GPS is not enabled.") LogHelper.w(TAG, "Device GPS is not enabled.")
@ -107,7 +106,6 @@ class TrackerService: Service(), SensorEventListener
LogHelper.v(TAG, "Added GPS location listener.") LogHelper.v(TAG, "Added GPS location listener.")
} }
/* Adds a Network location listener to location manager */
private fun addNetworkLocationListener() private fun addNetworkLocationListener()
{ {
if (gpsOnly) if (gpsOnly)
@ -122,7 +120,7 @@ class TrackerService: Service(), SensorEventListener
return return
} }
networkProviderActive = LocationHelper.isNetworkEnabled(locationManager) networkProviderActive = isNetworkEnabled(locationManager)
if (!networkProviderActive) if (!networkProviderActive)
{ {
LogHelper.w(TAG, "Unable to add Network location listener.") LogHelper.w(TAG, "Unable to add Network location listener.")
@ -151,7 +149,7 @@ class TrackerService: Service(), SensorEventListener
return object : LocationListener { return object : LocationListener {
override fun onLocationChanged(location: Location) override fun onLocationChanged(location: Location)
{ {
if (LocationHelper.isBetterLocation(location, currentBestLocation)) { if (isBetterLocation(location, currentBestLocation)) {
currentBestLocation = location currentBestLocation = location
} }
} }
@ -159,16 +157,16 @@ class TrackerService: Service(), SensorEventListener
{ {
LogHelper.v(TAG, "onProviderEnabled $provider") LogHelper.v(TAG, "onProviderEnabled $provider")
when (provider) { when (provider) {
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager) LocationManager.GPS_PROVIDER -> gpsProviderActive = isGpsEnabled(locationManager)
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager) LocationManager.NETWORK_PROVIDER -> networkProviderActive = isNetworkEnabled(locationManager)
} }
} }
override fun onProviderDisabled(provider: String) override fun onProviderDisabled(provider: String)
{ {
LogHelper.v(TAG, "onProviderDisabled $provider") LogHelper.v(TAG, "onProviderDisabled $provider")
when (provider) { when (provider) {
LocationManager.GPS_PROVIDER -> gpsProviderActive = LocationHelper.isGpsEnabled(locationManager) LocationManager.GPS_PROVIDER -> gpsProviderActive = isGpsEnabled(locationManager)
LocationManager.NETWORK_PROVIDER -> networkProviderActive = LocationHelper.isNetworkEnabled(locationManager) LocationManager.NETWORK_PROVIDER -> networkProviderActive = isNetworkEnabled(locationManager)
} }
} }
override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?) override fun onStatusChanged(p0: String?, p1: Int, p2: Bundle?)
@ -179,7 +177,8 @@ class TrackerService: Service(), SensorEventListener
} }
/* Displays or updates notification */ /* Displays or updates notification */
private fun displayNotification(): Notification { private fun displayNotification(): Notification
{
val notification: Notification = notificationHelper.createNotification( val notification: Notification = notificationHelper.createNotification(
trackingState, trackingState,
iso8601(GregorianCalendar.getInstance().time) iso8601(GregorianCalendar.getInstance().time)
@ -189,7 +188,8 @@ class TrackerService: Service(), SensorEventListener
} }
/* Overrides onAccuracyChanged from SensorEventListener */ /* Overrides onAccuracyChanged from SensorEventListener */
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) { override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int)
{
LogHelper.v(TAG, "Accuracy changed: $accuracy") LogHelper.v(TAG, "Accuracy changed: $accuracy")
} }
@ -220,24 +220,24 @@ class TrackerService: Service(), SensorEventListener
sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager sensorManager = this.getSystemService(Context.SENSOR_SERVICE) as SensorManager
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationHelper = NotificationHelper(this) notificationHelper = NotificationHelper(this)
gpsProviderActive = LocationHelper.isGpsEnabled(locationManager) gpsProviderActive = isGpsEnabled(locationManager)
networkProviderActive = LocationHelper.isNetworkEnabled(locationManager) networkProviderActive = isNetworkEnabled(locationManager)
gpsLocationListener = createLocationListener() gpsLocationListener = createLocationListener()
networkLocationListener = createLocationListener() networkLocationListener = createLocationListener()
trackingState = PreferencesHelper.loadTrackingState() trackingState = PreferencesHelper.loadTrackingState()
currentBestLocation = LocationHelper.getLastKnownLocation(this) currentBestLocation = getLastKnownLocation(this)
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener) PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
} }
/* Overrides onDestroy from Service */ /* Overrides onDestroy from Service */
override fun onDestroy() { override fun onDestroy() {
LogHelper.i("VOUSSOIR", "TrackerService.onDestroy.")
super.onDestroy() super.onDestroy()
LogHelper.i(TAG, "onDestroy called.")
if (trackingState == Keys.STATE_TRACKING_ACTIVE) if (trackingState == Keys.STATE_TRACKING_ACTIVE)
{ {
pauseTracking() pauseTracking()
} }
stopForeground(true) stopForeground(STOP_FOREGROUND_REMOVE)
notificationManager.cancel(Keys.TRACKER_SERVICE_NOTIFICATION_ID) // this call was not necessary prior to Android 12 notificationManager.cancel(Keys.TRACKER_SERVICE_NOTIFICATION_ID) // this call was not necessary prior to Android 12
PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener) PreferencesHelper.unregisterPreferenceChangeListener(sharedPreferenceChangeListener)
removeGpsLocationListener() removeGpsLocationListener()
@ -316,7 +316,6 @@ class TrackerService: Service(), SensorEventListener
} }
} }
/* Adds location listeners to location manager */
fun removeNetworkLocationListener() fun removeNetworkLocationListener()
{ {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
@ -365,15 +364,14 @@ class TrackerService: Service(), SensorEventListener
stopForeground(STOP_FOREGROUND_DETACH) stopForeground(STOP_FOREGROUND_DETACH)
} }
/*
* Defines the listener for changes in shared preferences
*/
private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key -> private val sharedPreferenceChangeListener = SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
when (key) { when (key)
{
Keys.PREF_GPS_ONLY -> Keys.PREF_GPS_ONLY ->
{ {
gpsOnly = PreferencesHelper.loadGpsOnly() gpsOnly = PreferencesHelper.loadGpsOnly()
when (gpsOnly) { when (gpsOnly)
{
true -> removeNetworkLocationListener() true -> removeNetworkLocationListener()
false -> addNetworkLocationListener() false -> addNetworkLocationListener()
} }
@ -418,12 +416,12 @@ class TrackerService: Service(), SensorEventListener
Log.i("VOUSSOIR", "Omitting due to 0,0 location.") Log.i("VOUSSOIR", "Omitting due to 0,0 location.")
return false return false
} }
if (! LocationHelper.isRecentEnough(location)) if (! isRecentEnough(location))
{ {
Log.i("VOUSSOIR", "Omitting due to not recent enough.") Log.i("VOUSSOIR", "Omitting due to not recent enough.")
return false return false
} }
if (! LocationHelper.isAccurateEnough(location, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY)) if (! isAccurateEnough(location, Keys.DEFAULT_THRESHOLD_LOCATION_ACCURACY))
{ {
Log.i("VOUSSOIR", "Omitting due to not accurate enough.") Log.i("VOUSSOIR", "Omitting due to not accurate enough.")
return false return false
@ -440,7 +438,7 @@ class TrackerService: Service(), SensorEventListener
{ {
return true return true
} }
if (! LocationHelper.isDifferentEnough(track.trkpts.last().toLocation(), location, omitRests)) if (! isDifferentEnough(track.trkpts.last().toLocation(), location, omitRests))
{ {
Log.i("VOUSSOIR", "Omitting due to too close to previous.") Log.i("VOUSSOIR", "Omitting due to too close to previous.")
return false return false

View file

@ -17,7 +17,7 @@
package org.y20k.trackbook package org.y20k.trackbook
import android.location.Location import android.location.Location
import org.y20k.trackbook.helpers.LocationHelper import org.y20k.trackbook.helpers.getNumberOfSatellites
import java.util.* import java.util.*
/* /*
@ -42,7 +42,7 @@ data class Trkpt(
altitude=location.altitude, altitude=location.altitude,
accuracy=location.accuracy, accuracy=location.accuracy,
time=location.time, time=location.time,
numberSatellites=LocationHelper.getNumberOfSatellites(location), numberSatellites=getNumberOfSatellites(location),
) )
/* Converts WayPoint into Location */ /* Converts WayPoint into Location */

View file

@ -27,16 +27,9 @@ import androidx.core.content.ContextCompat
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import kotlin.math.pow import kotlin.math.pow
/* /* Get default location */
* Keys object fun getDefaultLocation(): Location
*/ {
object LocationHelper {
/* Define log tag */
private val TAG: String = LogHelper.makeLogTag(LocationHelper::class.java)
/* Get default location */
fun getDefaultLocation(): Location {
val defaultLocation: Location = Location(LocationManager.NETWORK_PROVIDER) val defaultLocation: Location = Location(LocationManager.NETWORK_PROVIDER)
defaultLocation.latitude = Keys.DEFAULT_LATITUDE defaultLocation.latitude = Keys.DEFAULT_LATITUDE
defaultLocation.longitude = Keys.DEFAULT_LONGITUDE defaultLocation.longitude = Keys.DEFAULT_LONGITUDE
@ -44,10 +37,11 @@ object LocationHelper {
defaultLocation.altitude = Keys.DEFAULT_ALTITUDE defaultLocation.altitude = Keys.DEFAULT_ALTITUDE
defaultLocation.time = Keys.DEFAULT_DATE.time defaultLocation.time = Keys.DEFAULT_DATE.time
return defaultLocation return defaultLocation
} }
/* Tries to return the last location that the system has stored */ /* Tries to return the last location that the system has stored */
fun getLastKnownLocation(context: Context): Location { fun getLastKnownLocation(context: Context): Location
{
// get last location that Trackbook has stored // get last location that Trackbook has stored
var lastKnownLocation: Location = PreferencesHelper.loadCurrentBestLocation() var lastKnownLocation: Location = PreferencesHelper.loadCurrentBestLocation()
// try to get the last location the system has stored - it is probably more recent // try to get the last location the system has stored - it is probably more recent
@ -61,14 +55,15 @@ object LocationHelper {
} }
} }
return lastKnownLocation return lastKnownLocation
} }
/* Determines whether one location reading is better than the current location fix */ /* Determines whether one location reading is better than the current location fix */
fun isBetterLocation(location: Location, currentBestLocation: Location?): Boolean fun isBetterLocation(location: Location, currentBestLocation: Location?): Boolean
{ {
// Credit: https://developer.android.com/guide/topics/location/strategies.html#BestEstimate // Credit: https://developer.android.com/guide/topics/location/strategies.html#BestEstimate
if (currentBestLocation == null) { if (currentBestLocation == null)
{
// a new location is always better than no location // a new location is always better than no location
return true return true
} }
@ -102,11 +97,11 @@ object LocationHelper {
isNewer && !isSignificantlyLessAccurate && isFromSameProvider -> true isNewer && !isSignificantlyLessAccurate && isFromSameProvider -> true
else -> false else -> false
} }
} }
/* Checks if GPS location provider is available and enabled */ /* Checks if GPS location provider is available and enabled */
fun isGpsEnabled(locationManager: LocationManager): Boolean fun isGpsEnabled(locationManager: LocationManager): Boolean
{ {
if (locationManager.allProviders.contains(LocationManager.GPS_PROVIDER)) if (locationManager.allProviders.contains(LocationManager.GPS_PROVIDER))
{ {
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
@ -115,36 +110,45 @@ object LocationHelper {
{ {
return false return false
} }
} }
/* Checks if Network location provider is available and enabled */ /* Checks if Network location provider is available and enabled */
fun isNetworkEnabled(locationManager: LocationManager): Boolean { fun isNetworkEnabled(locationManager: LocationManager): Boolean
if (locationManager.allProviders.contains(LocationManager.NETWORK_PROVIDER)) { {
if (locationManager.allProviders.contains(LocationManager.NETWORK_PROVIDER))
{
return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} else { }
else
{
return false return false
} }
} }
/* Checks if given location is new */ /* Checks if given location is new */
fun isRecentEnough(location: Location): Boolean { fun isRecentEnough(location: Location): Boolean
{
val locationAge: Long = SystemClock.elapsedRealtimeNanos() - location.elapsedRealtimeNanos val locationAge: Long = SystemClock.elapsedRealtimeNanos() - location.elapsedRealtimeNanos
return locationAge < Keys.DEFAULT_THRESHOLD_LOCATION_AGE return locationAge < Keys.DEFAULT_THRESHOLD_LOCATION_AGE
} }
/* Checks if given location is accurate */ /* Checks if given location is accurate */
fun isAccurateEnough(location: Location, locationAccuracyThreshold: Int): Boolean { fun isAccurateEnough(location: Location, locationAccuracyThreshold: Int): Boolean
val isAccurate: Boolean {
when (location.provider) { if (location.provider == LocationManager.GPS_PROVIDER)
LocationManager.GPS_PROVIDER -> isAccurate = location.accuracy < locationAccuracyThreshold {
else -> isAccurate = location.accuracy < locationAccuracyThreshold + 10 // a bit more relaxed when location comes from network provider return location.accuracy < locationAccuracyThreshold
} }
return isAccurate else
{
return location.accuracy < locationAccuracyThreshold + 10 // a bit more relaxed when location comes from network provider
} }
}
/* Checks if given location is different enough compared to previous location */ /* Checks if given location is different enough compared to previous location */
fun isDifferentEnough(previousLocation: Location?, location: Location, omitRests: Boolean): Boolean { fun isDifferentEnough(previousLocation: Location?, location: Location, omitRests: Boolean): Boolean
{
// check if previous location is (not) available // check if previous location is (not) available
if (previousLocation == null) if (previousLocation == null)
{ {
@ -155,6 +159,7 @@ object LocationHelper {
{ {
return true return true
} }
// location.accuracy is given as 1 standard deviation, with a 68% chance // location.accuracy is given as 1 standard deviation, with a 68% chance
// that the true position is within a circle of this radius. // that the true position is within a circle of this radius.
// These formulas determine if the difference between the last point and // These formulas determine if the difference between the last point and
@ -168,10 +173,11 @@ object LocationHelper {
// different. We can multiply this number to increase confidence but // different. We can multiply this number to increase confidence but
// decrease point recording frequency if needed. // decrease point recording frequency if needed.
return distance > accuracyDelta return distance > accuracyDelta
} }
/* Get number of satellites from Location extras */ /* Get number of satellites from Location extras */
fun getNumberOfSatellites(location: Location): Int { fun getNumberOfSatellites(location: Location): Int
{
val numberOfSatellites: Int val numberOfSatellites: Int
val extras: Bundle? = location.extras val extras: Bundle? = location.extras
if (extras != null && extras.containsKey("satellites")) { if (extras != null && extras.containsKey("satellites")) {
@ -180,6 +186,4 @@ object LocationHelper {
numberOfSatellites = 0 numberOfSatellites = 0
} }
return numberOfSatellites return numberOfSatellites
}
} }

View file

@ -134,8 +134,6 @@ fun createSpecialMakersTrackOverlay(context: Context, map_view: MapView, track:
map_view.overlays.add(createOverlay(context, overlayItems)) map_view.overlays.add(createOverlay(context, overlayItems))
} }
fun createOverlayItem(context: Context, latitude: Double, longitude: Double, accuracy: Float, provider: String, time: Long): OverlayItem fun createOverlayItem(context: Context, latitude: Double, longitude: Double, accuracy: Float, provider: String, time: Long): OverlayItem
{ {
val title: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)}" val title: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(time)}"

View file

@ -65,7 +65,6 @@ data class MapFragmentLayoutHolder(
private val trackingState: Int private val trackingState: Int
) )
{ {
/* Main class variables */
val rootView: View val rootView: View
var userInteraction: Boolean = false var userInteraction: Boolean = false
val currentLocationButton: FloatingActionButton val currentLocationButton: FloatingActionButton
@ -75,12 +74,13 @@ data class MapFragmentLayoutHolder(
private var current_location_radius: Polygon = Polygon() private var current_location_radius: Polygon = Polygon()
private var currentTrackOverlay: SimpleFastPointOverlay? private var currentTrackOverlay: SimpleFastPointOverlay?
private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>? private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>?
private val useImperial: Boolean = PreferencesHelper.loadUseImperialUnits()
private var locationErrorBar: Snackbar private var locationErrorBar: Snackbar
private var controller: IMapController private var controller: IMapController
private var zoomLevel: Double private var zoomLevel: Double
init { init
{
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.init")
// find views // find views
rootView = inflater.inflate(R.layout.fragment_map, container, false) rootView = inflater.inflate(R.layout.fragment_map, container, false)
mapView = rootView.findViewById(R.id.map) mapView = rootView.findViewById(R.id.map)
@ -164,7 +164,7 @@ data class MapFragmentLayoutHolder(
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED) fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED)
{ {
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition") Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition")
val locationIsOld: Boolean = !(LocationHelper.isRecentEnough(location)) val locationIsOld: Boolean = !(isRecentEnough(location))
// create marker // create marker
val newMarker: Drawable val newMarker: Drawable
@ -228,7 +228,7 @@ data class MapFragmentLayoutHolder(
val p = Polygon() val p = Polygon()
p.points = Polygon.pointsAsCircle(GeoPoint(homepoint.location.latitude, homepoint.location.longitude), homepoint.location.accuracy.toDouble()) p.points = Polygon.pointsAsCircle(GeoPoint(homepoint.location.latitude, homepoint.location.longitude), homepoint.location.accuracy.toDouble())
p.fillPaint.color = Color.argb(64, 255, 193, 7) p.fillPaint.color = Color.argb(64, 255, 193, 7)
p.outlinePaint.color = Color.argb(128, 255, 193, 7) p.outlinePaint.color = Color.argb(0, 0, 0, 0)
map_view.overlays.add(p) map_view.overlays.add(p)
} }
} }