mirror of
https://github.com/element-hq/element-android
synced 2024-11-25 02:45:37 +03:00
Rework the location code - WIP
This commit is contained in:
parent
e3242f0deb
commit
e9b9406bf1
10 changed files with 247 additions and 159 deletions
|
@ -28,6 +28,7 @@ import im.vector.app.core.glide.GlideApp
|
|||
import im.vector.app.core.utils.DimensionConverter
|
||||
import im.vector.app.features.home.AvatarRenderer
|
||||
import org.matrix.android.sdk.api.util.toMatrixItem
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
@ -54,22 +55,36 @@ class LocationPinProvider @Inject constructor(
|
|||
val size = dimensionConverter.dpToPx(44)
|
||||
avatarRenderer.render(glideRequests, it, object : CustomTarget<Drawable>(size, size) {
|
||||
override fun onResourceReady(resource: Drawable, transition: Transition<in Drawable>?) {
|
||||
val bgUserPin = ContextCompat.getDrawable(context, R.drawable.bg_map_user_pin)!!
|
||||
val layerDrawable = LayerDrawable(arrayOf(bgUserPin, resource))
|
||||
val horizontalInset = dimensionConverter.dpToPx(4)
|
||||
val topInset = dimensionConverter.dpToPx(4)
|
||||
val bottomInset = dimensionConverter.dpToPx(8)
|
||||
layerDrawable.setLayerInset(1, horizontalInset, topInset, horizontalInset, bottomInset)
|
||||
|
||||
cache[userId] = layerDrawable
|
||||
|
||||
callback(layerDrawable)
|
||||
Timber.d("## Location: onResourceReady")
|
||||
val pinDrawable = createPinDrawable(resource)
|
||||
cache[userId] = pinDrawable
|
||||
callback(pinDrawable)
|
||||
}
|
||||
|
||||
override fun onLoadCleared(placeholder: Drawable?) {
|
||||
// Is it possible? Put placeholder instead?
|
||||
// FIXME The doc says it has to be implemented and should free resources
|
||||
Timber.d("## Location: onLoadCleared")
|
||||
}
|
||||
|
||||
override fun onLoadFailed(errorDrawable: Drawable?) {
|
||||
Timber.w("## Location: onLoadFailed")
|
||||
errorDrawable ?: return
|
||||
val pinDrawable = createPinDrawable(errorDrawable)
|
||||
cache[userId] = pinDrawable
|
||||
callback(pinDrawable)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun createPinDrawable(drawable: Drawable): Drawable {
|
||||
val bgUserPin = ContextCompat.getDrawable(context, R.drawable.bg_map_user_pin)!!
|
||||
val layerDrawable = LayerDrawable(arrayOf(bgUserPin, drawable))
|
||||
val horizontalInset = dimensionConverter.dpToPx(4)
|
||||
val topInset = dimensionConverter.dpToPx(4)
|
||||
val bottomInset = dimensionConverter.dpToPx(8)
|
||||
layerDrawable.setLayerInset(1, horizontalInset, topInset, horizontalInset, bottomInset)
|
||||
return layerDrawable
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,5 +17,5 @@
|
|||
package im.vector.app.features.location
|
||||
|
||||
const val INITIAL_MAP_ZOOM = 15.0
|
||||
const val MIN_TIME_MILLIS_TO_UPDATE_LOCATION = 1 * 60 * 1000L // every 1 minute
|
||||
const val MIN_DISTANCE_METERS_TO_UPDATE_LOCATION = 10f
|
||||
const val MIN_TIME_TO_UPDATE_LOCATION_MILLIS = 5 * 1_000L // every 5 seconds
|
||||
const val MIN_DISTANCE_TO_UPDATE_LOCATION_METERS = 10f
|
||||
|
|
|
@ -22,19 +22,27 @@ import android.view.MenuItem
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.args
|
||||
import com.mapbox.mapboxsdk.maps.MapView
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.core.utils.openLocation
|
||||
import im.vector.app.databinding.FragmentLocationPreviewBinding
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* TODO Move locationPinProvider to a ViewModel
|
||||
*/
|
||||
class LocationPreviewFragment @Inject constructor(
|
||||
private val locationPinProvider: LocationPinProvider
|
||||
) : VectorBaseFragment<FragmentLocationPreviewBinding>() {
|
||||
|
||||
private val args: LocationSharingArgs by args()
|
||||
|
||||
// Keep a ref to handle properly the onDestroy callback
|
||||
private var mapView: WeakReference<MapView>? = null
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationPreviewBinding {
|
||||
return FragmentLocationPreviewBinding.inflate(layoutInflater, container, false)
|
||||
}
|
||||
|
@ -42,6 +50,8 @@ class LocationPreviewFragment @Inject constructor(
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mapView = WeakReference(views.mapView)
|
||||
views.mapView.onCreate(savedInstanceState)
|
||||
views.mapView.initialize {
|
||||
if (isAdded) {
|
||||
onMapReady()
|
||||
|
@ -49,16 +59,42 @@ class LocationPreviewFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
views.mapView.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
views.mapView.onPause()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onLowMemory() {
|
||||
views.mapView.onLowMemory()
|
||||
super.onLowMemory()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
views.mapView.onStart()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
views.mapView.onStop()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
views.mapView.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
mapView?.get()?.onDestroy()
|
||||
mapView?.clear()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun getMenuRes() = R.menu.menu_location_preview
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
@ -85,7 +121,6 @@ class LocationPreviewFragment @Inject constructor(
|
|||
locationPinProvider.create(userId) { pinDrawable ->
|
||||
views.mapView.apply {
|
||||
zoomToLocation(location.latitude, location.longitude, INITIAL_MAP_ZOOM)
|
||||
deleteAllPins()
|
||||
addPinToMap(userId, pinDrawable)
|
||||
updatePinLocation(userId, location.latitude, location.longitude)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,5 @@ package im.vector.app.features.location
|
|||
import im.vector.app.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class LocationSharingAction : VectorViewModelAction {
|
||||
data class OnLocationUpdate(val locationData: LocationData) : LocationSharingAction()
|
||||
object OnShareLocation : LocationSharingAction()
|
||||
object OnLocationProviderIsNotAvailable : LocationSharingAction()
|
||||
}
|
||||
|
|
|
@ -21,29 +21,29 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.mapbox.mapboxsdk.maps.MapView
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
import im.vector.app.databinding.FragmentLocationSharingBinding
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* We should consider using SupportMapFragment for a out of the box lifecycle handling
|
||||
*/
|
||||
class LocationSharingFragment @Inject constructor(
|
||||
private val locationTracker: LocationTracker,
|
||||
private val session: Session,
|
||||
private val locationPinProvider: LocationPinProvider
|
||||
) : VectorBaseFragment<FragmentLocationSharingBinding>(), LocationTracker.Callback {
|
||||
|
||||
init {
|
||||
locationTracker.callback = this
|
||||
}
|
||||
) : VectorBaseFragment<FragmentLocationSharingBinding>() {
|
||||
|
||||
private val viewModel: LocationSharingViewModel by fragmentViewModel()
|
||||
|
||||
private var lastZoomValue: Double = -1.0
|
||||
|
||||
// Keep a ref to handle properly the onDestroy callback
|
||||
private var mapView: WeakReference<MapView>? = null
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding {
|
||||
return FragmentLocationSharingBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
@ -51,11 +51,9 @@ class LocationSharingFragment @Inject constructor(
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
views.mapView.initialize {
|
||||
if (isAdded) {
|
||||
onMapReady()
|
||||
}
|
||||
}
|
||||
mapView = WeakReference(views.mapView)
|
||||
views.mapView.onCreate(savedInstanceState)
|
||||
views.mapView.initialize()
|
||||
|
||||
views.shareLocationContainer.debouncedClicks {
|
||||
viewModel.handle(LocationSharingAction.OnShareLocation)
|
||||
|
@ -63,54 +61,48 @@ class LocationSharingFragment @Inject constructor(
|
|||
|
||||
viewModel.observeViewEvents {
|
||||
when (it) {
|
||||
LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError()
|
||||
LocationSharingViewEvents.Close -> activity?.finish()
|
||||
LocationSharingViewEvents.LocationNotAvailableError -> handleLocationNotAvailableError()
|
||||
LocationSharingViewEvents.Close -> activity?.finish()
|
||||
}.exhaustive
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
views.mapView.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
views.mapView.onPause()
|
||||
super.onPause()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
views.mapView.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
views.mapView.onStart()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
views.mapView.onStop()
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onLowMemory() {
|
||||
super.onLowMemory()
|
||||
views.mapView.onLowMemory()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
locationTracker.stop()
|
||||
mapView?.get()?.onDestroy()
|
||||
mapView?.clear()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun onMapReady() {
|
||||
if (!isAdded) return
|
||||
|
||||
locationPinProvider.create(session.myUserId) {
|
||||
views.mapView.addPinToMap(
|
||||
pinId = USER_PIN_NAME,
|
||||
image = it,
|
||||
)
|
||||
// All set, start location tracker
|
||||
locationTracker.start()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLocationUpdate(locationData: LocationData) {
|
||||
lastZoomValue = if (lastZoomValue == -1.0) INITIAL_MAP_ZOOM else views.mapView.getCurrentZoom() ?: INITIAL_MAP_ZOOM
|
||||
|
||||
views.mapView.zoomToLocation(locationData.latitude, locationData.longitude, lastZoomValue)
|
||||
views.mapView.deleteAllPins()
|
||||
views.mapView.updatePinLocation(USER_PIN_NAME, locationData.latitude, locationData.longitude)
|
||||
|
||||
viewModel.handle(LocationSharingAction.OnLocationUpdate(locationData))
|
||||
}
|
||||
|
||||
override fun onLocationProviderIsNotAvailable() {
|
||||
viewModel.handle(LocationSharingAction.OnLocationProviderIsNotAvailable)
|
||||
}
|
||||
|
||||
private fun handleLocationNotAvailableError() {
|
||||
MaterialAlertDialogBuilder(requireActivity())
|
||||
.setTitle(R.string.location_not_available_dialog_title)
|
||||
|
@ -122,6 +114,10 @@ class LocationSharingFragment @Inject constructor(
|
|||
.show()
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
views.mapView.render(state)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val USER_PIN_NAME = "USER_PIN_NAME"
|
||||
}
|
||||
|
|
|
@ -24,12 +24,15 @@ import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
|||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||
import im.vector.app.core.extensions.exhaustive
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
||||
import org.matrix.android.sdk.api.session.Session
|
||||
|
||||
class LocationSharingViewModel @AssistedInject constructor(
|
||||
@Assisted private val initialState: LocationSharingViewState,
|
||||
session: Session
|
||||
) : VectorViewModel<LocationSharingViewState, LocationSharingAction, LocationSharingViewEvents>(initialState) {
|
||||
private val locationTracker: LocationTracker,
|
||||
private val locationPinProvider: LocationPinProvider,
|
||||
private val session: Session
|
||||
) : VectorViewModel<LocationSharingViewState, LocationSharingAction, LocationSharingViewEvents>(initialState), LocationTracker.Callback {
|
||||
|
||||
private val room = session.getRoom(initialState.roomId)!!
|
||||
|
||||
|
@ -38,14 +41,31 @@ class LocationSharingViewModel @AssistedInject constructor(
|
|||
override fun create(initialState: LocationSharingViewState): LocationSharingViewModel
|
||||
}
|
||||
|
||||
companion object : MavericksViewModelFactory<LocationSharingViewModel, LocationSharingViewState> by hiltMavericksViewModelFactory() {
|
||||
companion object : MavericksViewModelFactory<LocationSharingViewModel, LocationSharingViewState> by hiltMavericksViewModelFactory()
|
||||
|
||||
init {
|
||||
locationTracker.start(this)
|
||||
createPin()
|
||||
}
|
||||
|
||||
private fun createPin() {
|
||||
locationPinProvider.create(session.myUserId) {
|
||||
setState {
|
||||
copy(
|
||||
pinDrawable = it
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
locationTracker.stop()
|
||||
}
|
||||
|
||||
override fun handle(action: LocationSharingAction) {
|
||||
when (action) {
|
||||
is LocationSharingAction.OnLocationUpdate -> handleLocationUpdate(action.locationData)
|
||||
LocationSharingAction.OnShareLocation -> handleShareLocation()
|
||||
LocationSharingAction.OnLocationProviderIsNotAvailable -> handleLocationProviderIsNotAvailable()
|
||||
LocationSharingAction.OnShareLocation -> handleShareLocation()
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
|
@ -62,13 +82,13 @@ class LocationSharingViewModel @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleLocationUpdate(locationData: LocationData) {
|
||||
override fun onLocationUpdate(locationData: LocationData) {
|
||||
setState {
|
||||
copy(lastKnownLocation = locationData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleLocationProviderIsNotAvailable() {
|
||||
override fun onLocationProviderIsNotAvailable() {
|
||||
_viewEvents.post(LocationSharingViewEvents.LocationNotAvailableError)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package im.vector.app.features.location
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
import androidx.annotation.StringRes
|
||||
import com.airbnb.mvrx.MavericksState
|
||||
import im.vector.app.R
|
||||
|
@ -28,7 +29,8 @@ enum class LocationSharingMode(@StringRes val titleRes: Int) {
|
|||
data class LocationSharingViewState(
|
||||
val roomId: String,
|
||||
val mode: LocationSharingMode,
|
||||
val lastKnownLocation: LocationData? = null
|
||||
val lastKnownLocation: LocationData? = null,
|
||||
val pinDrawable: Drawable? = null
|
||||
) : MavericksState {
|
||||
|
||||
constructor(locationSharingArgs: LocationSharingArgs) : this(
|
||||
|
|
|
@ -27,63 +27,73 @@ import timber.log.Timber
|
|||
import javax.inject.Inject
|
||||
|
||||
class LocationTracker @Inject constructor(
|
||||
private val context: Context
|
||||
context: Context
|
||||
) : LocationListenerCompat {
|
||||
|
||||
private val locationManager = context.getSystemService<LocationManager>()
|
||||
|
||||
interface Callback {
|
||||
fun onLocationUpdate(locationData: LocationData)
|
||||
fun onLocationProviderIsNotAvailable()
|
||||
}
|
||||
|
||||
private var locationManager: LocationManager? = null
|
||||
var callback: Callback? = null
|
||||
private var callback: Callback? = null
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
|
||||
fun start() {
|
||||
val locationManager = context.getSystemService<LocationManager>()
|
||||
fun start(callback: Callback?) {
|
||||
Timber.d("## LocationTracker. start()")
|
||||
this.callback = callback
|
||||
|
||||
locationManager?.let {
|
||||
val isGpsEnabled = it.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||
val isNetworkEnabled = it.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||
|
||||
val provider = when {
|
||||
isGpsEnabled -> LocationManager.GPS_PROVIDER
|
||||
isNetworkEnabled -> LocationManager.NETWORK_PROVIDER
|
||||
else -> {
|
||||
callback?.onLocationProviderIsNotAvailable()
|
||||
Timber.v("## LocationTracker. There is no location provider available")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Send last known location without waiting location updates
|
||||
it.getLastKnownLocation(provider)?.let { lastKnownLocation ->
|
||||
callback?.onLocationUpdate(lastKnownLocation.toLocationData())
|
||||
}
|
||||
|
||||
it.requestLocationUpdates(
|
||||
provider,
|
||||
MIN_TIME_MILLIS_TO_UPDATE_LOCATION,
|
||||
MIN_DISTANCE_METERS_TO_UPDATE_LOCATION,
|
||||
this
|
||||
)
|
||||
} ?: run {
|
||||
if (locationManager == null) {
|
||||
callback?.onLocationProviderIsNotAvailable()
|
||||
Timber.v("## LocationTracker. LocationManager is not available")
|
||||
return
|
||||
}
|
||||
|
||||
val isGpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
|
||||
val isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
|
||||
|
||||
Timber.d("## LocationTracker. isGpsEnabled: $isGpsEnabled - isNetworkEnabled: $isNetworkEnabled")
|
||||
|
||||
val provider = when {
|
||||
isGpsEnabled -> LocationManager.GPS_PROVIDER
|
||||
isNetworkEnabled -> LocationManager.NETWORK_PROVIDER
|
||||
else -> {
|
||||
callback?.onLocationProviderIsNotAvailable()
|
||||
Timber.v("## LocationTracker. There is no location provider available")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Send last known location without waiting location updates
|
||||
locationManager.getLastKnownLocation(provider)?.let { lastKnownLocation ->
|
||||
Timber.d("## LocationTracker. lastKnownLocation")
|
||||
callback?.onLocationUpdate(lastKnownLocation.toLocationData())
|
||||
}
|
||||
|
||||
Timber.d("## LocationTracker. track location using $provider")
|
||||
locationManager.requestLocationUpdates(
|
||||
provider,
|
||||
MIN_TIME_TO_UPDATE_LOCATION_MILLIS,
|
||||
MIN_DISTANCE_TO_UPDATE_LOCATION_METERS,
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
@RequiresPermission(anyOf = [Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION])
|
||||
fun stop() {
|
||||
Timber.d("## LocationTracker. stop()")
|
||||
locationManager?.removeUpdates(this)
|
||||
callback = null
|
||||
}
|
||||
|
||||
override fun onLocationChanged(location: Location) {
|
||||
Timber.d("## LocationTracker. onLocationChanged")
|
||||
callback?.onLocationUpdate(location.toLocationData())
|
||||
}
|
||||
|
||||
override fun onProviderDisabled(provider: String) {
|
||||
Timber.d("## LocationTracker. onProviderDisabled: $provider")
|
||||
callback?.onLocationProviderIsNotAvailable()
|
||||
}
|
||||
|
||||
|
|
|
@ -28,34 +28,47 @@ import com.mapbox.mapboxsdk.plugins.annotation.SymbolManager
|
|||
import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions
|
||||
import com.mapbox.mapboxsdk.style.layers.Property
|
||||
import im.vector.app.BuildConfig
|
||||
import timber.log.Timber
|
||||
|
||||
class MapTilerMapView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : MapView(context, attrs, defStyleAttr), VectorMapView {
|
||||
) : MapView(context, attrs, defStyleAttr) {
|
||||
|
||||
private var map: MapboxMap? = null
|
||||
private var symbolManager: SymbolManager? = null
|
||||
private var style: Style? = null
|
||||
private var pendingState: LocationSharingViewState? = null
|
||||
|
||||
override fun initialize(onMapReady: () -> Unit) {
|
||||
data class MapRefs(
|
||||
val map: MapboxMap,
|
||||
val symbolManager: SymbolManager,
|
||||
val style: Style
|
||||
)
|
||||
|
||||
private var mapRefs: MapRefs? = null
|
||||
private var initZoomDone = false
|
||||
|
||||
// TODO Kept only for the bottom sheet usage
|
||||
fun initialize(onMapReady: () -> Unit) {
|
||||
getMapAsync { map ->
|
||||
map.setStyle(styleUrl) { style ->
|
||||
this.symbolManager = SymbolManager(this, map, style)
|
||||
this.map = map
|
||||
this.style = style
|
||||
mapRefs = MapRefs(
|
||||
map,
|
||||
SymbolManager(this, map, style),
|
||||
style
|
||||
)
|
||||
onMapReady()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun addPinToMap(pinId: String, image: Drawable) {
|
||||
style?.addImage(pinId, image)
|
||||
// TODO Kept only for the bottom sheet usage
|
||||
fun addPinToMap(pinId: String, image: Drawable) {
|
||||
mapRefs?.style?.addImage(pinId, image)
|
||||
}
|
||||
|
||||
override fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) {
|
||||
symbolManager?.create(
|
||||
// TODO Kept only for the bottom sheet usage
|
||||
fun updatePinLocation(pinId: String, latitude: Double, longitude: Double) {
|
||||
mapRefs?.symbolManager?.create(
|
||||
SymbolOptions()
|
||||
.withLatLng(LatLng(latitude, longitude))
|
||||
.withIconImage(pinId)
|
||||
|
@ -63,28 +76,59 @@ class MapTilerMapView @JvmOverloads constructor(
|
|||
)
|
||||
}
|
||||
|
||||
override fun deleteAllPins() {
|
||||
symbolManager?.deleteAll()
|
||||
/**
|
||||
* For location fragments
|
||||
*/
|
||||
fun initialize() {
|
||||
Timber.d("## Location: initialize")
|
||||
|
||||
getMapAsync { map ->
|
||||
map.setStyle(styleUrl) { style ->
|
||||
mapRefs = MapRefs(
|
||||
map,
|
||||
SymbolManager(this, map, style),
|
||||
style
|
||||
)
|
||||
pendingState?.let { render(it) }
|
||||
pendingState = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) {
|
||||
map?.cameraPosition = CameraPosition.Builder()
|
||||
fun render(data: LocationSharingViewState) {
|
||||
val safeMapRefs = mapRefs ?: return Unit.also {
|
||||
pendingState = data
|
||||
}
|
||||
|
||||
data.pinDrawable?.let { pinDrawable ->
|
||||
if (safeMapRefs.style.getImage(LocationSharingFragment.USER_PIN_NAME) == null) {
|
||||
safeMapRefs.style.addImage(LocationSharingFragment.USER_PIN_NAME, pinDrawable)
|
||||
}
|
||||
}
|
||||
|
||||
data.lastKnownLocation?.let { locationData ->
|
||||
if (!initZoomDone) {
|
||||
zoomToLocation(locationData.latitude, locationData.longitude, INITIAL_MAP_ZOOM)
|
||||
initZoomDone = true
|
||||
}
|
||||
|
||||
safeMapRefs.symbolManager.create(
|
||||
SymbolOptions()
|
||||
.withLatLng(LatLng(locationData.latitude, locationData.longitude))
|
||||
.withIconImage(LocationSharingFragment.USER_PIN_NAME)
|
||||
.withIconAnchor(Property.ICON_ANCHOR_BOTTOM)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) {
|
||||
Timber.d("## Location: zoomToLocation")
|
||||
mapRefs?.map?.cameraPosition = CameraPosition.Builder()
|
||||
.target(LatLng(latitude, longitude))
|
||||
.zoom(zoom)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun getCurrentZoom(): Double? {
|
||||
return map?.cameraPosition?.zoom
|
||||
}
|
||||
|
||||
override fun onClick(callback: () -> Unit) {
|
||||
map?.addOnMapClickListener {
|
||||
callback()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val styleUrl = "https://api.maptiler.com/maps/streets/style.json?key=${BuildConfig.mapTilerKey}"
|
||||
}
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021 New Vector Ltd
|
||||
*
|
||||
* 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 im.vector.app.features.location
|
||||
|
||||
import android.graphics.drawable.Drawable
|
||||
|
||||
interface VectorMapView {
|
||||
fun initialize(onMapReady: () -> Unit)
|
||||
|
||||
fun addPinToMap(pinId: String, image: Drawable)
|
||||
fun updatePinLocation(pinId: String, latitude: Double, longitude: Double)
|
||||
fun deleteAllPins()
|
||||
|
||||
fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double)
|
||||
fun getCurrentZoom(): Double?
|
||||
|
||||
fun onClick(callback: () -> Unit)
|
||||
}
|
Loading…
Reference in a new issue