Handling map loading error in sharing and preview fragment
This commit is contained in:
parent
87ca9606b3
commit
e0e06c6ac8
|
@ -54,6 +54,7 @@ import im.vector.app.features.home.room.list.RoomListViewModel
|
|||
import im.vector.app.features.home.room.list.home.HomeRoomListViewModel
|
||||
import im.vector.app.features.homeserver.HomeServerCapabilitiesViewModel
|
||||
import im.vector.app.features.invite.InviteUsersToRoomViewModel
|
||||
import im.vector.app.features.location.LocationPreviewViewModel
|
||||
import im.vector.app.features.location.LocationSharingViewModel
|
||||
import im.vector.app.features.location.live.map.LiveLocationMapViewModel
|
||||
import im.vector.app.features.login.LoginViewModel
|
||||
|
@ -605,6 +606,11 @@ interface MavericksViewModelModule {
|
|||
@MavericksViewModelKey(LocationSharingViewModel::class)
|
||||
fun createLocationSharingViewModelFactory(factory: LocationSharingViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@MavericksViewModelKey(LocationPreviewViewModel::class)
|
||||
fun createLocationPreviewViewModelFactory(factory: LocationPreviewViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@MavericksViewModelKey(VectorAttachmentViewerViewModel::class)
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 im.vector.app.core.platform.VectorViewModelAction
|
||||
|
||||
sealed class LocationPreviewAction : VectorViewModelAction {
|
||||
object ShowMapLoadingError : LocationPreviewAction()
|
||||
}
|
|
@ -21,8 +21,11 @@ import android.view.LayoutInflater
|
|||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.airbnb.mvrx.args
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
import com.airbnb.mvrx.withState
|
||||
import com.mapbox.mapboxsdk.maps.MapView
|
||||
import im.vector.app.R
|
||||
import im.vector.app.core.platform.VectorBaseFragment
|
||||
|
@ -44,9 +47,13 @@ class LocationPreviewFragment @Inject constructor(
|
|||
|
||||
private val args: LocationSharingArgs by args()
|
||||
|
||||
private val viewModel: LocationPreviewViewModel by fragmentViewModel()
|
||||
|
||||
// Keep a ref to handle properly the onDestroy callback
|
||||
private var mapView: WeakReference<MapView>? = null
|
||||
|
||||
private var mapLoadingErrorListener: MapView.OnDidFailLoadingMapListener? = null
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationPreviewBinding {
|
||||
return FragmentLocationPreviewBinding.inflate(layoutInflater, container, false)
|
||||
}
|
||||
|
@ -55,6 +62,9 @@ class LocationPreviewFragment @Inject constructor(
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mapView = WeakReference(views.mapView)
|
||||
mapLoadingErrorListener = MapView.OnDidFailLoadingMapListener {
|
||||
viewModel.handle(LocationPreviewAction.ShowMapLoadingError)
|
||||
}.also { views.mapView.addOnDidFailLoadingMapListener(it) }
|
||||
views.mapView.onCreate(savedInstanceState)
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
|
@ -63,6 +73,12 @@ class LocationPreviewFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
mapLoadingErrorListener?.let { mapView?.get()?.removeOnDidFailLoadingMapListener(it) }
|
||||
mapLoadingErrorListener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
views.mapView.onResume()
|
||||
|
@ -99,6 +115,10 @@ class LocationPreviewFragment @Inject constructor(
|
|||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun invalidate() = withState(viewModel) { state ->
|
||||
views.mapPreviewLoadingError.isVisible = state.loadingMapHasFailed
|
||||
}
|
||||
|
||||
override fun getMenuRes() = R.menu.menu_location_preview
|
||||
|
||||
override fun handleMenuItemSelected(item: MenuItem): Boolean {
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* 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 com.airbnb.mvrx.MavericksViewModelFactory
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import im.vector.app.core.di.MavericksAssistedViewModelFactory
|
||||
import im.vector.app.core.di.hiltMavericksViewModelFactory
|
||||
import im.vector.app.core.platform.EmptyViewEvents
|
||||
import im.vector.app.core.platform.VectorViewModel
|
||||
|
||||
class LocationPreviewViewModel @AssistedInject constructor(
|
||||
@Assisted private val initialState: LocationPreviewViewState,
|
||||
) : VectorViewModel<LocationPreviewViewState, LocationPreviewAction, EmptyViewEvents>(initialState) {
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : MavericksAssistedViewModelFactory<LocationPreviewViewModel, LocationPreviewViewState> {
|
||||
override fun create(initialState: LocationPreviewViewState): LocationPreviewViewModel
|
||||
}
|
||||
|
||||
companion object : MavericksViewModelFactory<LocationPreviewViewModel, LocationPreviewViewState> by hiltMavericksViewModelFactory()
|
||||
|
||||
override fun handle(action: LocationPreviewAction) {
|
||||
when (action) {
|
||||
LocationPreviewAction.ShowMapLoadingError -> handleShowMapLoadingError()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleShowMapLoadingError() {
|
||||
setState { copy(loadingMapHasFailed = true) }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* 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 com.airbnb.mvrx.MavericksState
|
||||
|
||||
data class LocationPreviewViewState(
|
||||
val loadingMapHasFailed: Boolean = false
|
||||
) : MavericksState
|
|
@ -25,4 +25,5 @@ sealed class LocationSharingAction : VectorViewModelAction {
|
|||
object ZoomToUserLocation : LocationSharingAction()
|
||||
object LiveLocationSharingRequested : LocationSharingAction()
|
||||
data class StartLiveLocationSharing(val durationMillis: Long) : LocationSharingAction()
|
||||
object ShowMapLoadingError : LocationSharingAction()
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.setFragmentResultListener
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.airbnb.mvrx.fragmentViewModel
|
||||
|
@ -69,6 +70,7 @@ class LocationSharingFragment @Inject constructor(
|
|||
private var mapView: WeakReference<MapView>? = null
|
||||
|
||||
private var hasRenderedUserAvatar = false
|
||||
private var mapLoadingErrorListener: MapView.OnDidFailLoadingMapListener? = null
|
||||
|
||||
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentLocationSharingBinding {
|
||||
return FragmentLocationSharingBinding.inflate(inflater, container, false)
|
||||
|
@ -87,6 +89,9 @@ class LocationSharingFragment @Inject constructor(
|
|||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mapView = WeakReference(views.mapView)
|
||||
mapLoadingErrorListener = MapView.OnDidFailLoadingMapListener {
|
||||
viewModel.handle(LocationSharingAction.ShowMapLoadingError)
|
||||
}.also { views.mapView.addOnDidFailLoadingMapListener(it) }
|
||||
views.mapView.onCreate(savedInstanceState)
|
||||
|
||||
lifecycleScope.launchWhenCreated {
|
||||
|
@ -112,6 +117,12 @@ class LocationSharingFragment @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
mapLoadingErrorListener?.let { mapView?.get()?.removeOnDidFailLoadingMapListener(it) }
|
||||
mapLoadingErrorListener = null
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
views.mapView.onResume()
|
||||
|
@ -256,20 +267,27 @@ class LocationSharingFragment @Inject constructor(
|
|||
}
|
||||
|
||||
private fun updateMap(state: LocationSharingViewState) {
|
||||
// first, update the options view
|
||||
val options: Set<LocationSharingOption> = when (state.areTargetAndUserLocationEqual) {
|
||||
true -> setOf(LocationSharingOption.USER_CURRENT, LocationSharingOption.USER_LIVE)
|
||||
false -> setOf(LocationSharingOption.PINNED)
|
||||
else -> emptySet()
|
||||
}
|
||||
views.shareLocationOptionsPicker.render(options)
|
||||
if (state.loadingMapHasFailed) {
|
||||
views.shareLocationOptionsPicker.render(emptySet())
|
||||
views.shareLocationMapLoadingError.isVisible = true
|
||||
} else {
|
||||
// first, update the options view
|
||||
val options: Set<LocationSharingOption> = when (state.areTargetAndUserLocationEqual) {
|
||||
true -> setOf(LocationSharingOption.USER_CURRENT, LocationSharingOption.USER_LIVE)
|
||||
false -> setOf(LocationSharingOption.PINNED)
|
||||
else -> emptySet()
|
||||
}
|
||||
views.shareLocationOptionsPicker.render(options)
|
||||
|
||||
// then, update the map using the height of the options view after it has been rendered
|
||||
views.shareLocationOptionsPicker.post {
|
||||
val mapState = state
|
||||
.toMapState()
|
||||
.copy(logoMarginBottom = views.shareLocationOptionsPicker.height)
|
||||
views.mapView.render(mapState)
|
||||
// then, update the map using the height of the options view after it has been rendered
|
||||
views.shareLocationOptionsPicker.post {
|
||||
val mapState = state
|
||||
.toMapState()
|
||||
.copy(logoMarginBottom = views.shareLocationOptionsPicker.height)
|
||||
views.mapView.render(mapState)
|
||||
}
|
||||
|
||||
views.shareLocationMapLoadingError.isGone = true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ class LocationSharingViewModel @AssistedInject constructor(
|
|||
LocationSharingAction.ZoomToUserLocation -> handleZoomToUserLocationAction()
|
||||
LocationSharingAction.LiveLocationSharingRequested -> handleLiveLocationSharingRequestedAction()
|
||||
is LocationSharingAction.StartLiveLocationSharing -> handleStartLiveLocationSharingAction(action.durationMillis)
|
||||
LocationSharingAction.ShowMapLoadingError -> handleShowMapLoadingError()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,6 +212,10 @@ class LocationSharingViewModel @AssistedInject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun handleShowMapLoadingError() {
|
||||
setState { copy(loadingMapHasFailed = true) }
|
||||
}
|
||||
|
||||
private fun onLocationUpdate(locationData: LocationData) {
|
||||
Timber.d("onLocationUpdate()")
|
||||
setState {
|
||||
|
|
|
@ -36,6 +36,7 @@ data class LocationSharingViewState(
|
|||
val lastKnownUserLocation: LocationData? = null,
|
||||
val locationTargetDrawable: Drawable? = null,
|
||||
val canShareLiveLocation: Boolean = false,
|
||||
val loadingMapHasFailed: Boolean = false
|
||||
) : MavericksState {
|
||||
|
||||
constructor(locationSharingArgs: LocationSharingArgs) : this(
|
||||
|
|
|
@ -35,16 +35,6 @@
|
|||
app:layout_constraintStart_toStartOf="@id/mapView"
|
||||
app:layout_constraintTop_toTopOf="@id/mapView" />
|
||||
|
||||
<im.vector.app.features.location.MapLoadingErrorView
|
||||
android:id="@+id/shareLocationMapLoadingError"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/shareLocationOptionsPicker"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<im.vector.app.features.location.option.LocationSharingOptionPickerView
|
||||
android:id="@+id/shareLocationOptionsPicker"
|
||||
android:layout_width="0dp"
|
||||
|
@ -62,4 +52,14 @@
|
|||
app:layout_constraintBottom_toBottomOf="@id/shareLocationOptionsPicker"
|
||||
app:layout_constraintEnd_toEndOf="@id/shareLocationOptionsPicker" />
|
||||
|
||||
<im.vector.app.features.location.MapLoadingErrorView
|
||||
android:id="@+id/shareLocationMapLoadingError"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/shareLocationOptionsPicker"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
|
Loading…
Reference in New Issue