Skip to content

Commit

Permalink
Only animate the camera when device location is updated or user clicks (
Browse files Browse the repository at this point in the history
  • Loading branch information
shobhitagarwal1612 authored Oct 2, 2023
1 parent c34ca12 commit 5cf918c
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 31 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import com.google.android.ground.system.GeocodingManager
import com.google.android.ground.system.PermissionDeniedException
import com.google.android.ground.system.SettingsChangeRequestCanceled
import com.google.android.ground.ui.home.mapcontainer.MapTypeDialogFragmentDirections
import com.google.android.ground.ui.map.CameraPosition
import com.google.android.ground.ui.map.CameraUpdateRequest
import com.google.android.ground.ui.map.MapFragment
import javax.inject.Inject
import kotlin.math.max
Expand All @@ -47,6 +47,7 @@ abstract class AbstractMapContainerFragment : AbstractFragment() {
super.onViewCreated(view, savedInstanceState)
map.attachToParent(this, R.id.map) { onMapAttached(it) }
}

private fun launchWhenStarted(fn: suspend () -> Unit) {
lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { fn.invoke() } }
}
Expand Down Expand Up @@ -125,8 +126,10 @@ abstract class AbstractMapContainerFragment : AbstractFragment() {
Toast.makeText(context, messageId, Toast.LENGTH_LONG).show()
}

private fun onCameraUpdateRequest(newPosition: CameraPosition, map: MapFragment) {
Timber.v("Update camera: %s", newPosition)
private fun onCameraUpdateRequest(cameraUpdateRequest: CameraUpdateRequest, map: MapFragment) {
Timber.v("Update camera: $cameraUpdateRequest")
val newPosition = cameraUpdateRequest.cameraPosition
val shouldAnimate = cameraUpdateRequest.shouldAnimate
val bounds = newPosition.bounds
val target = newPosition.target
var zoomLevel = newPosition.zoomLevel
Expand All @@ -137,11 +140,11 @@ abstract class AbstractMapContainerFragment : AbstractFragment() {

// TODO(#1712): Fix this once CameraPosition is refactored to not contain duplicated state
if (bounds != null) {
map.moveCamera(bounds)
map.moveCamera(bounds, shouldAnimate)
} else if (target != null && zoomLevel != null) {
map.moveCamera(target, zoomLevel)
map.moveCamera(target, zoomLevel, shouldAnimate)
} else if (target != null) {
map.moveCamera(target)
map.moveCamera(target, shouldAnimate)
} else {
error("Must have either target or bounds set")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import com.google.android.ground.system.PermissionDeniedException
import com.google.android.ground.system.PermissionsManager
import com.google.android.ground.system.SettingsManager
import com.google.android.ground.ui.map.CameraPosition
import com.google.android.ground.ui.map.CameraUpdateRequest
import com.google.android.ground.ui.map.MapType
import com.google.android.ground.ui.map.gms.GmsExt.toBounds
import com.google.android.ground.ui.map.gms.toCoordinates
Expand Down Expand Up @@ -71,7 +72,7 @@ constructor(
@IoDispatcher private val ioDispatcher: CoroutineDispatcher,
) : AbstractViewModel() {

private val _cameraPosition = MutableStateFlow<CameraPosition?>(null)
private val _cameraUpdateRequests = MutableStateFlow<CameraUpdateRequest?>(null)

val locationLock: MutableStateFlow<Result<Boolean>> =
MutableStateFlow(Result.success(mapStateRepository.isLocationLockEnabled))
Expand Down Expand Up @@ -191,7 +192,7 @@ constructor(
}

/** Emits a stream of camera update requests. */
fun getCameraUpdateRequests(): Flow<CameraPosition> = _cameraPosition.filterNotNull()
fun getCameraUpdateRequests(): Flow<CameraUpdateRequest> = _cameraUpdateRequests.filterNotNull()

/** Emits a stream of current camera position. */
fun getCurrentCameraPosition(): Flow<CameraPosition> = currentCameraPosition.filterNotNull()
Expand Down Expand Up @@ -220,7 +221,7 @@ constructor(
surveyRepository.activeSurveyFlow
.filterNotNull()
.transform { getLastSavedPositionOrDefaultBounds(it)?.apply { emit(this) } }
.collect { updateMapCamera(it) }
.collect { setCameraPosition(it, false) }
}

private suspend fun getLastSavedPositionOrDefaultBounds(survey: Survey): CameraPosition? {
Expand All @@ -235,18 +236,22 @@ constructor(
return geometries.toBounds()?.let { CameraPosition(bounds = it) }
}

/** Requests moving the map camera to [coordinates]. */
private fun panCamera(coordinates: Coordinates) {
updateMapCamera(CameraPosition(coordinates))
setCameraPosition(CameraPosition(coordinates), true)
}

/** Requests moving the map camera to [coordinates] with zoom level [DEFAULT_LOI_ZOOM_LEVEL]. */
fun panAndZoomCamera(coordinates: Coordinates) {
updateMapCamera(CameraPosition(coordinates, DEFAULT_LOI_ZOOM_LEVEL))
private fun panAndZoomCamera(coordinates: Coordinates) {
setCameraPosition(CameraPosition(coordinates, DEFAULT_LOI_ZOOM_LEVEL), true)
}

private fun updateMapCamera(cameraPosition: CameraPosition) {
_cameraPosition.value = cameraPosition
/**
* Requests moving the map camera to the given position.
*
* @param cameraPosition new position
* @param shouldAnimate whether to animate the map camera or not
*/
fun setCameraPosition(cameraPosition: CameraPosition, shouldAnimate: Boolean) {
_cameraUpdateRequests.value = CameraUpdateRequest(cameraPosition, shouldAnimate)
}

/** Called when the map camera is moved. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SnapHelper
import com.google.android.ground.Config
import com.google.android.ground.R
import com.google.android.ground.coroutines.IoDispatcher
import com.google.android.ground.databinding.BasemapLayoutBinding
Expand All @@ -41,6 +42,7 @@ import com.google.android.ground.ui.home.HomeScreenFragmentDirections
import com.google.android.ground.ui.home.HomeScreenViewModel
import com.google.android.ground.ui.home.mapcontainer.cards.MapCardAdapter
import com.google.android.ground.ui.home.mapcontainer.cards.MapCardUiData
import com.google.android.ground.ui.map.CameraPosition
import com.google.android.ground.ui.map.MapFragment
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
Expand Down Expand Up @@ -216,7 +218,12 @@ class HomeScreenMapContainerFragment : Hilt_HomeScreenMapContainerFragment() {
state.locationOfInterest
?.geometry
?.takeIf { it is Point }
?.let { mapContainerViewModel.panAndZoomCamera(it.center()) }
?.let {
mapContainerViewModel.setCameraPosition(
CameraPosition(it.center(), Config.DEFAULT_LOI_ZOOM_LEVEL),
false
)
}
}
BottomSheetState.Visibility.HIDDEN -> {
map.enableGestures()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright 2023 Google LLC
*
* 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
*
* https://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 com.google.android.ground.ui.map

data class CameraUpdateRequest(val cameraPosition: CameraPosition, val shouldAnimate: Boolean)
Original file line number Diff line number Diff line change
Expand Up @@ -66,18 +66,18 @@ interface MapFragment {
fun disableGestures()

/** Centers the map viewport around the specified [Coordinates]. */
fun moveCamera(coordinates: Coordinates)
fun moveCamera(coordinates: Coordinates, shouldAnimate: Boolean)

/**
* Centers the map viewport around the specified [Coordinates] and updates the map's current zoom
* level.
*/
fun moveCamera(coordinates: Coordinates, zoomLevel: Float)
fun moveCamera(coordinates: Coordinates, zoomLevel: Float, shouldAnimate: Boolean)

/**
* Centers the map viewport around the specified [Bounds] are included at the least zoom level.
*/
fun moveCamera(bounds: Bounds)
fun moveCamera(bounds: Bounds, shouldAnimate: Boolean)

/** Displays user location indicator on the map. */
@SuppressLint("MissingPermission") fun enableCurrentLocationIndicator()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import androidx.annotation.IdRes
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.lifecycle.lifecycleScope
import com.google.android.gms.maps.CameraUpdate
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.GoogleMap.OnCameraMoveStartedListener
Expand All @@ -41,7 +42,6 @@ import com.google.android.ground.model.imagery.TileSource.Type.TILED_WEB_MAP
import com.google.android.ground.ui.common.AbstractFragment
import com.google.android.ground.ui.map.*
import com.google.android.ground.ui.map.CameraPosition
import com.google.android.ground.ui.map.MapFragment
import com.google.android.ground.ui.map.gms.GmsExt.toBounds
import com.google.android.ground.ui.map.gms.mog.MogCollection
import com.google.android.ground.ui.map.gms.mog.MogTileProvider
Expand Down Expand Up @@ -152,7 +152,7 @@ class GoogleMapsFragment : Hilt_GoogleMapsFragment(), MapFragment {
viewGroup: ViewGroup?,
bundle: Bundle?
): View =
super.onCreateView(layoutInflater, viewGroup, bundle)!!.apply {
super.onCreateView(layoutInflater, viewGroup, bundle).apply {
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
onApplyWindowInsets(view, insets)
}
Expand Down Expand Up @@ -209,7 +209,7 @@ class GoogleMapsFragment : Hilt_GoogleMapsFragment(), MapFragment {

private fun onClusterItemClick(cluster: Cluster<FeatureClusterItem>): Boolean {
// Move the camera to point to LOIs within the current cluster
cluster.items.map { it.feature.geometry }.toBounds()?.let { moveCamera(it) }
cluster.items.map { it.feature.geometry }.toBounds()?.let { moveCamera(it, true) }
return true
}

Expand All @@ -226,17 +226,20 @@ class GoogleMapsFragment : Hilt_GoogleMapsFragment(), MapFragment {

override fun disableGestures() = map.uiSettings.setAllGesturesEnabled(false)

override fun moveCamera(coordinates: Coordinates) =
map.animateCamera(CameraUpdateFactory.newLatLng(coordinates.toGoogleMapsObject()))
override fun moveCamera(coordinates: Coordinates, shouldAnimate: Boolean) =
moveCamera(CameraUpdateFactory.newLatLng(coordinates.toGoogleMapsObject()), shouldAnimate)

override fun moveCamera(coordinates: Coordinates, zoomLevel: Float) =
map.animateCamera(
CameraUpdateFactory.newLatLngZoom(coordinates.toGoogleMapsObject(), zoomLevel)
override fun moveCamera(coordinates: Coordinates, zoomLevel: Float, shouldAnimate: Boolean) =
moveCamera(
CameraUpdateFactory.newLatLngZoom(coordinates.toGoogleMapsObject(), zoomLevel),
shouldAnimate
)

override fun moveCamera(bounds: Bounds) {
map.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds.toGoogleMapsObject(), 100))
}
override fun moveCamera(bounds: Bounds, shouldAnimate: Boolean) =
moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.toGoogleMapsObject(), 100), shouldAnimate)

private fun moveCamera(cameraUpdate: CameraUpdate, shouldAnimate: Boolean) =
if (shouldAnimate) map.animateCamera(cameraUpdate) else map.moveCamera(cameraUpdate)

private fun getCustomCap(): CustomCap {
if (customCap == null) {
Expand Down

0 comments on commit 5cf918c

Please sign in to comment.