checkpoint
This commit is contained in:
parent
04fa76249b
commit
5dad3d6209
2 changed files with 283 additions and 323 deletions
|
@ -17,20 +17,39 @@
|
|||
package org.y20k.trackbook
|
||||
|
||||
import android.Manifest
|
||||
import android.app.Activity
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.location.Location
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.activity.result.contract.ActivityResultContracts.RequestPermission
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.Fragment
|
||||
import org.y20k.trackbook.Track
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.osmdroid.api.IMapController
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.FolderOverlay
|
||||
import org.osmdroid.views.overlay.ItemizedIconOverlay
|
||||
import org.osmdroid.views.overlay.OverlayItem
|
||||
import org.osmdroid.views.overlay.Polygon
|
||||
import org.osmdroid.views.overlay.TilesOverlay
|
||||
import org.osmdroid.views.overlay.compass.CompassOverlay
|
||||
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
||||
import org.y20k.trackbook.helpers.*
|
||||
import org.y20k.trackbook.ui.MapFragmentLayoutHolder
|
||||
|
||||
/*
|
||||
* MapFragment class
|
||||
|
@ -48,11 +67,25 @@ class MapFragment : Fragment()
|
|||
private var networkProviderActive: Boolean = false
|
||||
private lateinit var track: Track
|
||||
private lateinit var currentBestLocation: Location
|
||||
private lateinit var layout: MapFragmentLayoutHolder
|
||||
private lateinit var trackerService: TrackerService
|
||||
|
||||
lateinit var rootView: View
|
||||
var userInteraction: Boolean = false
|
||||
lateinit var currentLocationButton: FloatingActionButton
|
||||
lateinit var mainButton: ExtendedFloatingActionButton
|
||||
private lateinit var mapView: MapView
|
||||
private lateinit var current_position_folder: FolderOverlay
|
||||
private var currentTrackOverlay: SimpleFastPointOverlay? = null
|
||||
private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>? = null
|
||||
private lateinit var locationErrorBar: Snackbar
|
||||
private lateinit var controller: IMapController
|
||||
private var zoomLevel: Double = Keys.DEFAULT_ZOOM_LEVEL
|
||||
private lateinit var homepoints_overlay_folder: FolderOverlay
|
||||
|
||||
/* Overrides onCreate from Fragment */
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
override fun onCreate(savedInstanceState: Bundle?)
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onCreate")
|
||||
super.onCreate(savedInstanceState)
|
||||
// TODO make only MapFragment's status bar transparent - see:
|
||||
// https://gist.github.com/Dvik/a3de88d39da9d1d6d175025a56c5e797#file-viewextension-kt and
|
||||
|
@ -61,28 +94,84 @@ class MapFragment : Fragment()
|
|||
currentBestLocation = getLastKnownLocation(activity as Context)
|
||||
// get saved tracking state
|
||||
trackingState = PreferencesHelper.loadTrackingState()
|
||||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
|
||||
}
|
||||
|
||||
/* Overrides onStop from Fragment */
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
// initialize layout
|
||||
val statusBarHeight: Int = UiHelper.getStatusBarHeight(activity as Context)
|
||||
layout = MapFragmentLayoutHolder(activity as Context, inflater, container, statusBarHeight, currentBestLocation, trackingState)
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onCreateView")
|
||||
val context = activity as Context
|
||||
// find views
|
||||
rootView = inflater.inflate(R.layout.fragment_map, container, false)
|
||||
mapView = rootView.findViewById(R.id.map)
|
||||
currentLocationButton = rootView.findViewById(R.id.location_button)
|
||||
mainButton = rootView.findViewById(R.id.main_button)
|
||||
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
||||
|
||||
// basic map setup
|
||||
controller = mapView.controller
|
||||
mapView.isTilesScaledToDpi = true
|
||||
mapView.setTileSource(TileSourceFactory.MAPNIK)
|
||||
mapView.setMultiTouchControls(true)
|
||||
mapView.zoomController.setVisibility(org.osmdroid.views.CustomZoomButtonsController.Visibility.NEVER)
|
||||
zoomLevel = PreferencesHelper.loadZoomLevel()
|
||||
controller.setZoom(zoomLevel)
|
||||
|
||||
// set dark map tiles, if necessary
|
||||
if (AppThemeHelper.isDarkModeOn(requireActivity()))
|
||||
{
|
||||
mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS)
|
||||
}
|
||||
|
||||
// store Density Scaling Factor
|
||||
val densityScalingFactor: Float = UiHelper.getDensityScalingFactor(context)
|
||||
|
||||
// add compass to map
|
||||
val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
|
||||
compassOverlay.enableCompass()
|
||||
// compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / densityScalingFactor)) // TODO uncomment when transparent status bar is re-implemented
|
||||
val screen_width = Resources.getSystem().displayMetrics.widthPixels;
|
||||
compassOverlay.setCompassCenter((screen_width / densityScalingFactor) - 36f, 36f)
|
||||
mapView.overlays.add(compassOverlay)
|
||||
|
||||
val app: Trackbook = (context.applicationContext as Trackbook)
|
||||
app.load_homepoints()
|
||||
homepoints_overlay_folder = FolderOverlay()
|
||||
mapView.overlays.add(homepoints_overlay_folder)
|
||||
createHomepointOverlays(context, mapView, app.homepoints)
|
||||
|
||||
// add my location overlay
|
||||
current_position_folder = FolderOverlay()
|
||||
mapView.overlays.add(current_position_folder)
|
||||
|
||||
|
||||
centerMap(currentBestLocation)
|
||||
|
||||
// initialize track overlays
|
||||
currentTrackOverlay = null
|
||||
currentTrackSpecialMarkerOverlay = null
|
||||
|
||||
// initialize main button state
|
||||
updateMainButton(trackingState)
|
||||
|
||||
// listen for user interaction
|
||||
addInteractionListener()
|
||||
|
||||
// set up buttons
|
||||
layout.currentLocationButton.setOnClickListener {
|
||||
layout.centerMap(currentBestLocation, animated = true)
|
||||
currentLocationButton.setOnClickListener {
|
||||
centerMap(currentBestLocation, animated = true)
|
||||
}
|
||||
layout.mainButton.setOnClickListener {
|
||||
mainButton.setOnClickListener {
|
||||
handleTrackingManagementMenu()
|
||||
}
|
||||
|
||||
return layout.rootView
|
||||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
return rootView
|
||||
}
|
||||
|
||||
/* Overrides onStart from Fragment */
|
||||
override fun onStart() {
|
||||
override fun onStart()
|
||||
{
|
||||
super.onStart()
|
||||
// request location permission if denied
|
||||
if (ContextCompat.checkSelfPermission(activity as Context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {
|
||||
|
@ -93,27 +182,34 @@ class MapFragment : Fragment()
|
|||
}
|
||||
|
||||
/* Overrides onResume from Fragment */
|
||||
override fun onResume() {
|
||||
override fun onResume()
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onResume")
|
||||
super.onResume()
|
||||
// if (bound) {
|
||||
// trackerService.addGpsLocationListener()
|
||||
// trackerService.addNetworkLocationListener()
|
||||
// }
|
||||
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
// if (bound) {
|
||||
// trackerService.addGpsLocationListener()
|
||||
// trackerService.addNetworkLocationListener()
|
||||
// }
|
||||
}
|
||||
|
||||
/* Overrides onPause from Fragment */
|
||||
override fun onPause() {
|
||||
override fun onPause()
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onPause")
|
||||
super.onPause()
|
||||
layout.saveState(currentBestLocation)
|
||||
saveBestLocationState(currentBestLocation)
|
||||
if (bound && trackingState != Keys.STATE_TRACKING_ACTIVE) {
|
||||
trackerService.removeGpsLocationListener()
|
||||
trackerService.removeNetworkLocationListener()
|
||||
trackerService.trackbook.database.commit()
|
||||
}
|
||||
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
|
||||
/* Overrides onStop from Fragment */
|
||||
override fun onStop() {
|
||||
override fun onStop()
|
||||
{
|
||||
super.onStop()
|
||||
// unbind from TrackerService
|
||||
if (bound)
|
||||
|
@ -123,6 +219,13 @@ class MapFragment : Fragment()
|
|||
}
|
||||
}
|
||||
|
||||
override fun onDestroy()
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragment.onDestroy")
|
||||
super.onDestroy()
|
||||
requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
}
|
||||
|
||||
/* Register the permission launcher for requesting location */
|
||||
private val requestLocationPermissionLauncher = registerForActivityResult(RequestPermission()) { isGranted: Boolean ->
|
||||
if (isGranted) {
|
||||
|
@ -134,26 +237,24 @@ class MapFragment : Fragment()
|
|||
// permission denied - unbind service
|
||||
activity?.unbindService(connection)
|
||||
}
|
||||
layout.toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
||||
toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
||||
}
|
||||
|
||||
/* Register the permission launcher for starting the tracking service */
|
||||
private val startTrackingPermissionLauncher = registerForActivityResult(RequestPermission()) { isGranted: Boolean ->
|
||||
logPermissionRequestResult(isGranted)
|
||||
if (isGranted)
|
||||
{
|
||||
LogHelper.i(TAG, "Request result: Activity Recognition permission has been granted.")
|
||||
}
|
||||
else
|
||||
{
|
||||
LogHelper.i(TAG, "Request result: Activity Recognition permission has NOT been granted.")
|
||||
}
|
||||
// start service via intent so that it keeps running after unbind
|
||||
startTrackerService()
|
||||
trackerService.startTracking()
|
||||
}
|
||||
|
||||
/* Logs the request result of the Activity Recognition permission launcher */
|
||||
private fun logPermissionRequestResult(isGranted: Boolean) {
|
||||
if (isGranted) {
|
||||
LogHelper.i(TAG, "Request result: Activity Recognition permission has been granted.")
|
||||
} else {
|
||||
LogHelper.i(TAG, "Request result: Activity Recognition permission has NOT been granted.")
|
||||
}
|
||||
}
|
||||
|
||||
/* Start recording waypoints */
|
||||
private fun startTracking() {
|
||||
// request activity recognition permission on Android Q+ if denied
|
||||
|
@ -211,11 +312,153 @@ class MapFragment : Fragment()
|
|||
if (activity != null)
|
||||
{
|
||||
trackingState = PreferencesHelper.loadTrackingState()
|
||||
layout.updateMainButton(trackingState)
|
||||
updateMainButton(trackingState)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun addInteractionListener() {
|
||||
mapView.setOnTouchListener { v, event ->
|
||||
userInteraction = true
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun centerMap(location: Location, animated: Boolean = false) {
|
||||
val position = GeoPoint(location.latitude, location.longitude)
|
||||
when (animated) {
|
||||
true -> controller.animateTo(position)
|
||||
false -> controller.setCenter(position)
|
||||
}
|
||||
userInteraction = false
|
||||
}
|
||||
|
||||
fun saveBestLocationState(currentBestLocation: Location) {
|
||||
PreferencesHelper.saveCurrentBestLocation(currentBestLocation)
|
||||
PreferencesHelper.saveZoomLevel(mapView.zoomLevelDouble)
|
||||
// reset user interaction state
|
||||
userInteraction = false
|
||||
}
|
||||
|
||||
/* Mark current position on map */
|
||||
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED)
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition")
|
||||
val locationIsOld: Boolean = !(isRecentEnough(location))
|
||||
|
||||
// create marker
|
||||
val newMarker: Drawable
|
||||
val fillcolor: Int
|
||||
if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
||||
{
|
||||
fillcolor = Color.argb(64, 220, 61, 51)
|
||||
if (locationIsOld)
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_black_24dp)!!
|
||||
}
|
||||
else
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_red_24dp)!!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fillcolor = Color.argb(64, 60, 152, 219)
|
||||
if(locationIsOld)
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_black_24dp)!!
|
||||
}
|
||||
else
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(requireContext(), R.drawable.ic_marker_location_blue_24dp)!!
|
||||
}
|
||||
}
|
||||
|
||||
current_position_folder.items.clear()
|
||||
|
||||
val current_location_radius = Polygon()
|
||||
current_location_radius.points = Polygon.pointsAsCircle(GeoPoint(location.latitude, location.longitude), location.accuracy.toDouble())
|
||||
current_location_radius.fillPaint.color = fillcolor
|
||||
current_location_radius.outlinePaint.color = Color.argb(0, 0, 0, 0)
|
||||
current_position_folder.add(current_location_radius)
|
||||
|
||||
val overlayItems: java.util.ArrayList<OverlayItem> = java.util.ArrayList<OverlayItem>()
|
||||
val overlayItem: OverlayItem = createOverlayItem(requireContext(), location.latitude, location.longitude, location.accuracy, location.provider.toString(), location.time)
|
||||
overlayItem.setMarker(newMarker)
|
||||
overlayItems.add(overlayItem)
|
||||
current_position_folder.add(createOverlay(requireContext(), overlayItems))
|
||||
}
|
||||
|
||||
fun createHomepointOverlays(context: Context, map_view: MapView, homepoints: List<Homepoint>)
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.createHomepointOverlays")
|
||||
val overlayItems: java.util.ArrayList<OverlayItem> = java.util.ArrayList<OverlayItem>()
|
||||
|
||||
val newMarker: Drawable = ContextCompat.getDrawable(context, R.drawable.ic_homepoint_24dp)!!
|
||||
|
||||
homepoints_overlay_folder.items.clear()
|
||||
|
||||
for (homepoint in homepoints)
|
||||
{
|
||||
val overlayItem: OverlayItem = createOverlayItem(context, homepoint.location.latitude, homepoint.location.longitude, homepoint.location.accuracy, homepoint.location.provider.toString(), homepoint.location.time)
|
||||
overlayItem.setMarker(newMarker)
|
||||
overlayItems.add(overlayItem)
|
||||
homepoints_overlay_folder.add(createOverlay(context, overlayItems))
|
||||
|
||||
val p = Polygon()
|
||||
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.outlinePaint.color = Color.argb(0, 0, 0, 0)
|
||||
homepoints_overlay_folder.add(p)
|
||||
}
|
||||
}
|
||||
|
||||
/* Overlay current track on map */
|
||||
fun overlayCurrentTrack(track: Track, trackingState: Int) {
|
||||
if (currentTrackOverlay != null) {
|
||||
mapView.overlays.remove(currentTrackOverlay)
|
||||
}
|
||||
if (currentTrackSpecialMarkerOverlay != null) {
|
||||
mapView.overlays.remove(currentTrackSpecialMarkerOverlay)
|
||||
}
|
||||
if (track.trkpts.isNotEmpty()) {
|
||||
createTrackOverlay(requireContext(), mapView, track, trackingState)
|
||||
createSpecialMakersTrackOverlay(requireContext(), mapView, track, trackingState)
|
||||
}
|
||||
}
|
||||
|
||||
/* Toggles state of main button and additional buttons (save & resume) */
|
||||
fun updateMainButton(trackingState: Int)
|
||||
{
|
||||
when (trackingState) {
|
||||
Keys.STATE_TRACKING_STOPPED -> {
|
||||
mainButton.setIconResource(R.drawable.ic_fiber_manual_record_inactive_24dp)
|
||||
mainButton.text = requireContext().getString(R.string.button_start)
|
||||
mainButton.contentDescription = requireContext().getString(R.string.descr_button_start)
|
||||
currentLocationButton.isVisible = true
|
||||
}
|
||||
Keys.STATE_TRACKING_ACTIVE -> {
|
||||
mainButton.setIconResource(R.drawable.ic_fiber_manual_stop_24dp)
|
||||
mainButton.text = requireContext().getString(R.string.button_pause)
|
||||
mainButton.contentDescription = requireContext().getString(R.string.descr_button_pause)
|
||||
currentLocationButton.isVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleLocationErrorBar(gpsProviderActive: Boolean, networkProviderActive: Boolean) {
|
||||
if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {
|
||||
// CASE: Location permission not granted
|
||||
locationErrorBar.setText(R.string.snackbar_message_location_permission_denied)
|
||||
if (!locationErrorBar.isShown) locationErrorBar.show()
|
||||
} else if (!gpsProviderActive && !networkProviderActive) {
|
||||
// CASE: Location setting is off
|
||||
locationErrorBar.setText(R.string.snackbar_message_location_offline)
|
||||
if (!locationErrorBar.isShown) locationErrorBar.show()
|
||||
} else {
|
||||
if (locationErrorBar.isShown) locationErrorBar.dismiss()
|
||||
}
|
||||
}
|
||||
/*
|
||||
* End of declaration
|
||||
*/
|
||||
|
@ -231,7 +474,7 @@ class MapFragment : Fragment()
|
|||
trackerService = binder.service
|
||||
// get state of tracking and update button if necessary
|
||||
trackingState = trackerService.trackingState
|
||||
layout.updateMainButton(trackingState)
|
||||
updateMainButton(trackingState)
|
||||
// register listener for changes in shared preferences
|
||||
PreferencesHelper.registerPreferenceChangeListener(sharedPreferenceChangeListener)
|
||||
// start listening for location updates
|
||||
|
@ -259,15 +502,15 @@ class MapFragment : Fragment()
|
|||
networkProviderActive = trackerService.networkProviderActive
|
||||
trackingState = trackerService.trackingState
|
||||
// update location and track
|
||||
layout.markCurrentPosition(currentBestLocation, trackingState)
|
||||
layout.overlayCurrentTrack(track, trackingState)
|
||||
markCurrentPosition(currentBestLocation, trackingState)
|
||||
overlayCurrentTrack(track, trackingState)
|
||||
// center map, if it had not been dragged/zoomed before
|
||||
if (!layout.userInteraction)
|
||||
if (!userInteraction)
|
||||
{
|
||||
layout.centerMap(currentBestLocation, true)
|
||||
centerMap(currentBestLocation, true)
|
||||
}
|
||||
// show error snackbar if necessary
|
||||
layout.toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
||||
toggleLocationErrorBar(gpsProviderActive, networkProviderActive)
|
||||
// use the handler to start runnable again after specified delay
|
||||
handler.postDelayed(this, Keys.REQUEST_CURRENT_LOCATION_INTERVAL)
|
||||
}
|
||||
|
|
|
@ -1,283 +0,0 @@
|
|||
/*
|
||||
* MapFragmentLayoutHolder.kt
|
||||
* Implements the MapFragmentLayoutHolder class
|
||||
* A MapFragmentLayoutHolder hold references to the main views of a map fragment
|
||||
*
|
||||
* This file is part of
|
||||
* TRACKBOOK - Movement Recorder for Android
|
||||
*
|
||||
* Copyright (c) 2016-22 - Y20K.org
|
||||
* Licensed under the MIT-License
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
* Trackbook uses osmdroid - OpenStreetMap-Tools for Android
|
||||
* https://github.com/osmdroid/osmdroid
|
||||
*/
|
||||
|
||||
package org.y20k.trackbook.ui
|
||||
|
||||
import android.Manifest
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.location.Location
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import org.osmdroid.api.IMapController
|
||||
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
|
||||
import org.osmdroid.util.GeoPoint
|
||||
import org.osmdroid.views.MapView
|
||||
import org.osmdroid.views.overlay.ItemizedIconOverlay
|
||||
import org.osmdroid.views.overlay.OverlayItem
|
||||
import org.osmdroid.views.overlay.Polygon
|
||||
import org.osmdroid.views.overlay.TilesOverlay
|
||||
import org.osmdroid.views.overlay.compass.CompassOverlay
|
||||
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
|
||||
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
|
||||
import org.y20k.trackbook.Homepoint
|
||||
import org.y20k.trackbook.Keys
|
||||
import org.y20k.trackbook.R
|
||||
import org.y20k.trackbook.Trackbook
|
||||
import org.y20k.trackbook.Track
|
||||
import org.y20k.trackbook.helpers.*
|
||||
|
||||
/*
|
||||
* MapFragmentLayoutHolder class
|
||||
*/
|
||||
data class MapFragmentLayoutHolder(
|
||||
private var context: Context,
|
||||
private var inflater: LayoutInflater,
|
||||
private var container: ViewGroup?,
|
||||
private var statusBarHeight: Int,
|
||||
private val startLocation: Location,
|
||||
private val trackingState: Int
|
||||
)
|
||||
{
|
||||
val rootView: View
|
||||
var userInteraction: Boolean = false
|
||||
val currentLocationButton: FloatingActionButton
|
||||
val mainButton: ExtendedFloatingActionButton
|
||||
private val mapView: MapView
|
||||
private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem>
|
||||
private var current_location_radius: Polygon = Polygon()
|
||||
private var currentTrackOverlay: SimpleFastPointOverlay?
|
||||
private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>?
|
||||
private var locationErrorBar: Snackbar
|
||||
private var controller: IMapController
|
||||
private var zoomLevel: Double
|
||||
|
||||
init
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.init")
|
||||
// find views
|
||||
rootView = inflater.inflate(R.layout.fragment_map, container, false)
|
||||
mapView = rootView.findViewById(R.id.map)
|
||||
currentLocationButton = rootView.findViewById(R.id.location_button)
|
||||
mainButton = rootView.findViewById(R.id.main_button)
|
||||
locationErrorBar = Snackbar.make(mapView, String(), Snackbar.LENGTH_INDEFINITE)
|
||||
|
||||
// basic map setup
|
||||
controller = mapView.controller
|
||||
mapView.isTilesScaledToDpi = true
|
||||
mapView.setTileSource(TileSourceFactory.MAPNIK)
|
||||
mapView.setMultiTouchControls(true)
|
||||
mapView.zoomController.setVisibility(org.osmdroid.views.CustomZoomButtonsController.Visibility.NEVER)
|
||||
zoomLevel = PreferencesHelper.loadZoomLevel()
|
||||
controller.setZoom(zoomLevel)
|
||||
|
||||
// set dark map tiles, if necessary
|
||||
if (AppThemeHelper.isDarkModeOn(context as Activity)) {
|
||||
mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS)
|
||||
}
|
||||
|
||||
// store Density Scaling Factor
|
||||
val densityScalingFactor: Float = UiHelper.getDensityScalingFactor(context)
|
||||
|
||||
// add compass to map
|
||||
val compassOverlay = CompassOverlay(context, InternalCompassOrientationProvider(context), mapView)
|
||||
compassOverlay.enableCompass()
|
||||
// compassOverlay.setCompassCenter(36f, 36f + (statusBarHeight / densityScalingFactor)) // TODO uncomment when transparent status bar is re-implemented
|
||||
val screen_width = Resources.getSystem().getDisplayMetrics().widthPixels;
|
||||
compassOverlay.setCompassCenter((screen_width / densityScalingFactor) - 36f, 36f)
|
||||
mapView.overlays.add(compassOverlay)
|
||||
|
||||
val app: Trackbook = (context.applicationContext as Trackbook)
|
||||
app.load_homepoints()
|
||||
createHomepointOverlays(context, mapView, app.homepoints)
|
||||
|
||||
// add my location overlay
|
||||
currentPositionOverlay = createOverlay(context, ArrayList<OverlayItem>())
|
||||
mapView.overlays.add(currentPositionOverlay)
|
||||
centerMap(startLocation)
|
||||
|
||||
// initialize track overlays
|
||||
currentTrackOverlay = null
|
||||
currentTrackSpecialMarkerOverlay = null
|
||||
|
||||
// initialize main button state
|
||||
updateMainButton(trackingState)
|
||||
|
||||
// listen for user interaction
|
||||
addInteractionListener()
|
||||
}
|
||||
|
||||
/* Listen for user interaction */
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private fun addInteractionListener() {
|
||||
mapView.setOnTouchListener { v, event ->
|
||||
userInteraction = true
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/* Set map center */
|
||||
fun centerMap(location: Location, animated: Boolean = false) {
|
||||
val position = GeoPoint(location.latitude, location.longitude)
|
||||
when (animated) {
|
||||
true -> controller.animateTo(position)
|
||||
false -> controller.setCenter(position)
|
||||
}
|
||||
userInteraction = false
|
||||
}
|
||||
|
||||
/* Save current best location and state of map to shared preferences */
|
||||
fun saveState(currentBestLocation: Location) {
|
||||
PreferencesHelper.saveCurrentBestLocation(currentBestLocation)
|
||||
PreferencesHelper.saveZoomLevel(mapView.zoomLevelDouble)
|
||||
// reset user interaction state
|
||||
userInteraction = false
|
||||
}
|
||||
|
||||
/* Mark current position on map */
|
||||
fun markCurrentPosition(location: Location, trackingState: Int = Keys.STATE_TRACKING_STOPPED)
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.markCurrentPosition")
|
||||
val locationIsOld: Boolean = !(isRecentEnough(location))
|
||||
|
||||
// create marker
|
||||
val newMarker: Drawable
|
||||
val fillcolor: Int
|
||||
if (trackingState == Keys.STATE_TRACKING_ACTIVE)
|
||||
{
|
||||
fillcolor = Color.argb(64, 220, 61, 51)
|
||||
if (locationIsOld)
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_black_24dp)!!
|
||||
}
|
||||
else
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_red_24dp)!!
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fillcolor = Color.argb(64, 60, 152, 219)
|
||||
if(locationIsOld)
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_black_24dp)!!
|
||||
}
|
||||
else
|
||||
{
|
||||
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_location_blue_24dp)!!
|
||||
}
|
||||
}
|
||||
|
||||
// add marker to list of overlay items
|
||||
val overlayItem: OverlayItem = createOverlayItem(context, location.latitude, location.longitude, location.accuracy, location.provider.toString(), location.time)
|
||||
overlayItem.setMarker(newMarker)
|
||||
currentPositionOverlay.removeAllItems()
|
||||
currentPositionOverlay.addItem(overlayItem)
|
||||
|
||||
if (current_location_radius in mapView.overlays)
|
||||
{
|
||||
mapView.overlays.remove(current_location_radius)
|
||||
}
|
||||
current_location_radius = Polygon()
|
||||
current_location_radius.points = Polygon.pointsAsCircle(GeoPoint(location.latitude, location.longitude), location.accuracy.toDouble())
|
||||
current_location_radius.fillPaint.color = fillcolor
|
||||
current_location_radius.outlinePaint.color = Color.argb(0, 0, 0, 0)
|
||||
mapView.overlays.add(current_location_radius)
|
||||
}
|
||||
|
||||
fun createHomepointOverlays(context: Context, map_view: MapView, homepoints: List<Homepoint>)
|
||||
{
|
||||
Log.i("VOUSSOIR", "MapFragmentLayoutHolder.createHomepointOverlays")
|
||||
val overlayItems: java.util.ArrayList<OverlayItem> = java.util.ArrayList<OverlayItem>()
|
||||
|
||||
val newMarker: Drawable = ContextCompat.getDrawable(context, R.drawable.ic_homepoint_24dp)!!
|
||||
|
||||
for (homepoint in homepoints)
|
||||
{
|
||||
val overlayItem: OverlayItem = createOverlayItem(context, homepoint.location.latitude, homepoint.location.longitude, homepoint.location.accuracy, homepoint.location.provider.toString(), homepoint.location.time)
|
||||
overlayItem.setMarker(newMarker)
|
||||
overlayItems.add(overlayItem)
|
||||
map_view.overlays.add(createOverlay(context, overlayItems))
|
||||
|
||||
val p = Polygon()
|
||||
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.outlinePaint.color = Color.argb(0, 0, 0, 0)
|
||||
map_view.overlays.add(p)
|
||||
}
|
||||
}
|
||||
|
||||
/* Overlay current track on map */
|
||||
fun overlayCurrentTrack(track: Track, trackingState: Int) {
|
||||
if (currentTrackOverlay != null) {
|
||||
mapView.overlays.remove(currentTrackOverlay)
|
||||
}
|
||||
if (currentTrackSpecialMarkerOverlay != null) {
|
||||
mapView.overlays.remove(currentTrackSpecialMarkerOverlay)
|
||||
}
|
||||
if (track.trkpts.isNotEmpty()) {
|
||||
createTrackOverlay(context, mapView, track, trackingState)
|
||||
createSpecialMakersTrackOverlay(context, mapView, track, trackingState)
|
||||
}
|
||||
}
|
||||
|
||||
/* Toggles state of main button and additional buttons (save & resume) */
|
||||
fun updateMainButton(trackingState: Int)
|
||||
{
|
||||
when (trackingState) {
|
||||
Keys.STATE_TRACKING_STOPPED -> {
|
||||
mainButton.setIconResource(R.drawable.ic_fiber_manual_record_inactive_24dp)
|
||||
mainButton.text = context.getString(R.string.button_start)
|
||||
mainButton.contentDescription = context.getString(R.string.descr_button_start)
|
||||
currentLocationButton.isVisible = true
|
||||
}
|
||||
Keys.STATE_TRACKING_ACTIVE -> {
|
||||
mainButton.setIconResource(R.drawable.ic_fiber_manual_stop_24dp)
|
||||
mainButton.text = context.getString(R.string.button_pause)
|
||||
mainButton.contentDescription = context.getString(R.string.descr_button_pause)
|
||||
currentLocationButton.isVisible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Toggles content and visibility of the location error snackbar */
|
||||
fun toggleLocationErrorBar(gpsProviderActive: Boolean, networkProviderActive: Boolean) {
|
||||
if (ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_DENIED) {
|
||||
// CASE: Location permission not granted
|
||||
locationErrorBar.setText(R.string.snackbar_message_location_permission_denied)
|
||||
if (!locationErrorBar.isShown) locationErrorBar.show()
|
||||
} else if (!gpsProviderActive && !networkProviderActive) {
|
||||
// CASE: Location setting is off
|
||||
locationErrorBar.setText(R.string.snackbar_message_location_offline)
|
||||
if (!locationErrorBar.isShown) locationErrorBar.show()
|
||||
} else {
|
||||
if (locationErrorBar.isShown) locationErrorBar.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue