Rework the location code - WIP
This commit is contained in:
parent
e9b9406bf1
commit
1f53945031
|
@ -34,6 +34,7 @@ import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvid
|
||||||
import im.vector.app.features.home.room.detail.timeline.item.BindingOptions
|
import im.vector.app.features.home.room.detail.timeline.item.BindingOptions
|
||||||
import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess
|
import im.vector.app.features.home.room.detail.timeline.tools.findPillsAndProcess
|
||||||
import im.vector.app.features.location.LocationData
|
import im.vector.app.features.location.LocationData
|
||||||
|
import im.vector.app.features.location.MapState
|
||||||
import im.vector.app.features.location.MapTilerMapView
|
import im.vector.app.features.location.MapTilerMapView
|
||||||
import im.vector.app.features.media.ImageContentRenderer
|
import im.vector.app.features.media.ImageContentRenderer
|
||||||
import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
|
import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
|
||||||
|
@ -99,15 +100,25 @@ abstract class BottomSheetMessagePreviewItem : VectorEpoxyModel<BottomSheetMessa
|
||||||
|
|
||||||
holder.mapView.isVisible = locationData != null
|
holder.mapView.isVisible = locationData != null
|
||||||
holder.body.isVisible = locationData == null
|
holder.body.isVisible = locationData == null
|
||||||
locationData?.let { location ->
|
holder.mapView.initialize()
|
||||||
holder.mapView.initialize {
|
holder.mapView.render(
|
||||||
if (holder.view.isAttachedToWindow) {
|
MapState(
|
||||||
holder.mapView.zoomToLocation(location.latitude, location.longitude, 15.0)
|
zoomOnlyOnce = false,
|
||||||
locationPinProvider?.create(matrixItem.id) { pinDrawable ->
|
pinLocationData = locationData,
|
||||||
holder.mapView.addPinToMap(matrixItem.id, pinDrawable)
|
pinId = matrixItem.id,
|
||||||
holder.mapView.updatePinLocation(matrixItem.id, location.latitude, location.longitude)
|
pinDrawable = null
|
||||||
}
|
)
|
||||||
}
|
)
|
||||||
|
locationPinProvider?.create(matrixItem.id) { pinDrawable ->
|
||||||
|
if (holder.view.isAttachedToWindow) {
|
||||||
|
holder.mapView.render(
|
||||||
|
MapState(
|
||||||
|
zoomOnlyOnce = false,
|
||||||
|
pinLocationData = locationData,
|
||||||
|
pinId = matrixItem.id,
|
||||||
|
pinDrawable = pinDrawable
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import im.vector.app.R
|
||||||
import im.vector.app.core.epoxy.onClick
|
import im.vector.app.core.epoxy.onClick
|
||||||
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
import im.vector.app.features.home.room.detail.timeline.helper.LocationPinProvider
|
||||||
import im.vector.app.features.location.LocationData
|
import im.vector.app.features.location.LocationData
|
||||||
|
import im.vector.app.features.location.MapState
|
||||||
import im.vector.app.features.location.MapTilerMapView
|
import im.vector.app.features.location.MapTilerMapView
|
||||||
|
|
||||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||||
|
@ -56,14 +57,25 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
|
||||||
callback?.onMapClicked()
|
callback?.onMapClicked()
|
||||||
}
|
}
|
||||||
|
|
||||||
holder.mapView.apply {
|
holder.mapView.initialize()
|
||||||
initialize {
|
holder.mapView.render(
|
||||||
zoomToLocation(location.latitude, location.longitude, INITIAL_ZOOM)
|
MapState(
|
||||||
|
zoomOnlyOnce = false,
|
||||||
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
|
pinLocationData = location,
|
||||||
addPinToMap(locationOwnerId, pinDrawable)
|
pinId = locationOwnerId,
|
||||||
updatePinLocation(locationOwnerId, location.latitude, location.longitude)
|
pinDrawable = null
|
||||||
}
|
)
|
||||||
|
)
|
||||||
|
locationPinProvider?.create(locationOwnerId) { pinDrawable ->
|
||||||
|
if (holder.view.isAttachedToWindow) {
|
||||||
|
holder.mapView.render(
|
||||||
|
MapState(
|
||||||
|
zoomOnlyOnce = false,
|
||||||
|
pinLocationData = location,
|
||||||
|
pinId = locationOwnerId,
|
||||||
|
pinDrawable = pinDrawable
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,6 +90,5 @@ abstract class MessageLocationItem : AbsMessageItem<MessageLocationItem.Holder>(
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val STUB_ID = R.id.messageContentLocationStub
|
private const val STUB_ID = R.id.messageContentLocationStub
|
||||||
private const val INITIAL_ZOOM = 15.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.airbnb.mvrx.args
|
import com.airbnb.mvrx.args
|
||||||
import com.mapbox.mapboxsdk.maps.MapView
|
import com.mapbox.mapboxsdk.maps.MapView
|
||||||
import im.vector.app.R
|
import im.vector.app.R
|
||||||
|
@ -52,11 +53,8 @@ class LocationPreviewFragment @Inject constructor(
|
||||||
|
|
||||||
mapView = WeakReference(views.mapView)
|
mapView = WeakReference(views.mapView)
|
||||||
views.mapView.onCreate(savedInstanceState)
|
views.mapView.onCreate(savedInstanceState)
|
||||||
views.mapView.initialize {
|
views.mapView.initialize()
|
||||||
if (isAdded) {
|
loadPinDrawable()
|
||||||
onMapReady()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
|
@ -112,17 +110,20 @@ class LocationPreviewFragment @Inject constructor(
|
||||||
openLocation(requireActivity(), location.latitude, location.longitude)
|
openLocation(requireActivity(), location.latitude, location.longitude)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun onMapReady() {
|
private fun loadPinDrawable() {
|
||||||
if (!isAdded) return
|
|
||||||
|
|
||||||
val location = args.initialLocationData ?: return
|
val location = args.initialLocationData ?: return
|
||||||
val userId = args.locationOwnerId
|
val userId = args.locationOwnerId
|
||||||
|
|
||||||
locationPinProvider.create(userId) { pinDrawable ->
|
locationPinProvider.create(userId) { pinDrawable ->
|
||||||
views.mapView.apply {
|
lifecycleScope.launchWhenResumed {
|
||||||
zoomToLocation(location.latitude, location.longitude, INITIAL_MAP_ZOOM)
|
views.mapView.render(
|
||||||
addPinToMap(userId, pinDrawable)
|
MapState(
|
||||||
updatePinLocation(userId, location.latitude, location.longitude)
|
zoomOnlyOnce = true,
|
||||||
|
pinLocationData = location,
|
||||||
|
pinId = args.locationOwnerId,
|
||||||
|
pinDrawable = pinDrawable
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,8 +39,6 @@ class LocationSharingFragment @Inject constructor(
|
||||||
|
|
||||||
private val viewModel: LocationSharingViewModel by fragmentViewModel()
|
private val viewModel: LocationSharingViewModel by fragmentViewModel()
|
||||||
|
|
||||||
private var lastZoomValue: Double = -1.0
|
|
||||||
|
|
||||||
// Keep a ref to handle properly the onDestroy callback
|
// Keep a ref to handle properly the onDestroy callback
|
||||||
private var mapView: WeakReference<MapView>? = null
|
private var mapView: WeakReference<MapView>? = null
|
||||||
|
|
||||||
|
@ -115,7 +113,7 @@ class LocationSharingFragment @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun invalidate() = withState(viewModel) { state ->
|
override fun invalidate() = withState(viewModel) { state ->
|
||||||
views.mapView.render(state)
|
views.mapView.render(state.toMapState())
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
|
@ -38,3 +38,10 @@ data class LocationSharingViewState(
|
||||||
mode = locationSharingArgs.mode
|
mode = locationSharingArgs.mode
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun LocationSharingViewState.toMapState() = MapState(
|
||||||
|
zoomOnlyOnce = true,
|
||||||
|
pinLocationData = lastKnownLocation,
|
||||||
|
pinId = LocationSharingFragment.USER_PIN_NAME,
|
||||||
|
pinDrawable = pinDrawable
|
||||||
|
)
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2022 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
|
||||||
|
|
||||||
|
data class MapState(
|
||||||
|
val zoomOnlyOnce: Boolean,
|
||||||
|
val pinLocationData: LocationData? = null,
|
||||||
|
val pinId: String,
|
||||||
|
val pinDrawable: Drawable? = null
|
||||||
|
)
|
|
@ -17,7 +17,6 @@
|
||||||
package im.vector.app.features.location
|
package im.vector.app.features.location
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.drawable.Drawable
|
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import com.mapbox.mapboxsdk.camera.CameraPosition
|
import com.mapbox.mapboxsdk.camera.CameraPosition
|
||||||
import com.mapbox.mapboxsdk.geometry.LatLng
|
import com.mapbox.mapboxsdk.geometry.LatLng
|
||||||
|
@ -29,6 +28,7 @@ import com.mapbox.mapboxsdk.plugins.annotation.SymbolOptions
|
||||||
import com.mapbox.mapboxsdk.style.layers.Property
|
import com.mapbox.mapboxsdk.style.layers.Property
|
||||||
import im.vector.app.BuildConfig
|
import im.vector.app.BuildConfig
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
class MapTilerMapView @JvmOverloads constructor(
|
class MapTilerMapView @JvmOverloads constructor(
|
||||||
context: Context,
|
context: Context,
|
||||||
|
@ -36,7 +36,7 @@ class MapTilerMapView @JvmOverloads constructor(
|
||||||
defStyleAttr: Int = 0
|
defStyleAttr: Int = 0
|
||||||
) : MapView(context, attrs, defStyleAttr) {
|
) : MapView(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
private var pendingState: LocationSharingViewState? = null
|
private var pendingState: MapState? = null
|
||||||
|
|
||||||
data class MapRefs(
|
data class MapRefs(
|
||||||
val map: MapboxMap,
|
val map: MapboxMap,
|
||||||
|
@ -44,43 +44,18 @@ class MapTilerMapView @JvmOverloads constructor(
|
||||||
val style: Style
|
val style: Style
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private var isInitializing = AtomicBoolean(false)
|
||||||
private var mapRefs: MapRefs? = null
|
private var mapRefs: MapRefs? = null
|
||||||
private var initZoomDone = false
|
private var initZoomDone = false
|
||||||
|
|
||||||
// TODO Kept only for the bottom sheet usage
|
|
||||||
fun initialize(onMapReady: () -> Unit) {
|
|
||||||
getMapAsync { map ->
|
|
||||||
map.setStyle(styleUrl) { style ->
|
|
||||||
mapRefs = MapRefs(
|
|
||||||
map,
|
|
||||||
SymbolManager(this, map, style),
|
|
||||||
style
|
|
||||||
)
|
|
||||||
onMapReady()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Kept only for the bottom sheet usage
|
|
||||||
fun addPinToMap(pinId: String, image: Drawable) {
|
|
||||||
mapRefs?.style?.addImage(pinId, image)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
.withIconAnchor(Property.ICON_ANCHOR_BOTTOM)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For location fragments
|
* For location fragments
|
||||||
*/
|
*/
|
||||||
fun initialize() {
|
fun initialize() {
|
||||||
Timber.d("## Location: initialize")
|
Timber.d("## Location: initialize $isInitializing")
|
||||||
|
if (isInitializing.getAndSet(true)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
getMapAsync { map ->
|
getMapAsync { map ->
|
||||||
map.setStyle(styleUrl) { style ->
|
map.setStyle(styleUrl) { style ->
|
||||||
|
@ -95,20 +70,21 @@ class MapTilerMapView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun render(data: LocationSharingViewState) {
|
fun render(state: MapState) {
|
||||||
val safeMapRefs = mapRefs ?: return Unit.also {
|
val safeMapRefs = mapRefs ?: return Unit.also {
|
||||||
pendingState = data
|
pendingState = state
|
||||||
}
|
}
|
||||||
|
|
||||||
data.pinDrawable?.let { pinDrawable ->
|
state.pinDrawable?.let { pinDrawable ->
|
||||||
if (safeMapRefs.style.getImage(LocationSharingFragment.USER_PIN_NAME) == null) {
|
if (safeMapRefs.style.isFullyLoaded &&
|
||||||
|
safeMapRefs.style.getImage(LocationSharingFragment.USER_PIN_NAME) == null) {
|
||||||
safeMapRefs.style.addImage(LocationSharingFragment.USER_PIN_NAME, pinDrawable)
|
safeMapRefs.style.addImage(LocationSharingFragment.USER_PIN_NAME, pinDrawable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data.lastKnownLocation?.let { locationData ->
|
state.pinLocationData?.let { locationData ->
|
||||||
if (!initZoomDone) {
|
if (!initZoomDone || !state.zoomOnlyOnce) {
|
||||||
zoomToLocation(locationData.latitude, locationData.longitude, INITIAL_MAP_ZOOM)
|
zoomToLocation(locationData.latitude, locationData.longitude)
|
||||||
initZoomDone = true
|
initZoomDone = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,11 +97,11 @@ class MapTilerMapView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun zoomToLocation(latitude: Double, longitude: Double, zoom: Double) {
|
private fun zoomToLocation(latitude: Double, longitude: Double) {
|
||||||
Timber.d("## Location: zoomToLocation")
|
Timber.d("## Location: zoomToLocation")
|
||||||
mapRefs?.map?.cameraPosition = CameraPosition.Builder()
|
mapRefs?.map?.cameraPosition = CameraPosition.Builder()
|
||||||
.target(LatLng(latitude, longitude))
|
.target(LatLng(latitude, longitude))
|
||||||
.zoom(zoom)
|
.zoom(INITIAL_MAP_ZOOM)
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue