Handling map loading error in timeline

This commit is contained in:
Maxime NATUREL 2022-08-01 14:45:08 +02:00
parent e0e06c6ac8
commit 6972622bc6
7 changed files with 94 additions and 34 deletions

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MapLoadingErrorView">
<attr name="mapErrorDescription" format="string" />
</declare-styleable>
</resources>

View File

@ -36,6 +36,8 @@ import im.vector.app.core.utils.DimensionConverter
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.home.room.detail.timeline.style.TimelineMessageLayout import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout
import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners import im.vector.app.features.home.room.detail.timeline.style.granularRoundedCorners
import im.vector.app.features.location.MapLoadingErrorView
import im.vector.app.features.location.MapLoadingErrorViewState
abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder>( abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder>(
@LayoutRes layoutId: Int = R.layout.item_timeline_event_base @LayoutRes layoutId: Int = R.layout.item_timeline_event_base
@ -86,8 +88,10 @@ abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder>(
target: Target<Drawable>?, target: Target<Drawable>?,
isFirstResource: Boolean isFirstResource: Boolean
): Boolean { ): Boolean {
holder.staticMapPinImageView.setImageResource(R.drawable.ic_location_pin_failed) holder.staticMapPinImageView.setImageDrawable(null)
holder.staticMapErrorTextView.isVisible = true holder.staticMapLoadingErrorView.isVisible = true
val mapErrorViewState = MapLoadingErrorViewState(imageCornerTransformation)
holder.staticMapLoadingErrorView.render(mapErrorViewState)
holder.staticMapCopyrightTextView.isVisible = false holder.staticMapCopyrightTextView.isVisible = false
return false return false
} }
@ -103,7 +107,7 @@ abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder>(
// we are not using Glide since it does not display it correctly when there is no user photo // we are not using Glide since it does not display it correctly when there is no user photo
holder.staticMapPinImageView.setImageDrawable(pinDrawable) holder.staticMapPinImageView.setImageDrawable(pinDrawable)
} }
holder.staticMapErrorTextView.isVisible = false holder.staticMapLoadingErrorView.isVisible = false
holder.staticMapCopyrightTextView.isVisible = true holder.staticMapCopyrightTextView.isVisible = true
return false return false
} }
@ -115,7 +119,7 @@ abstract class AbsMessageLocationItem<H : AbsMessageLocationItem.Holder>(
abstract class Holder(@IdRes stubId: Int) : AbsMessageItem.Holder(stubId) { abstract class Holder(@IdRes stubId: Int) : AbsMessageItem.Holder(stubId) {
val staticMapImageView by bind<ImageView>(R.id.staticMapImageView) val staticMapImageView by bind<ImageView>(R.id.staticMapImageView)
val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView) val staticMapPinImageView by bind<ImageView>(R.id.staticMapPinImageView)
val staticMapErrorTextView by bind<TextView>(R.id.staticMapErrorTextView) val staticMapLoadingErrorView by bind<MapLoadingErrorView>(R.id.staticMapLoadingError)
val staticMapCopyrightTextView by bind<TextView>(R.id.staticMapCopyrightTextView) val staticMapCopyrightTextView by bind<TextView>(R.id.staticMapCopyrightTextView)
} }
} }

View File

@ -17,10 +17,16 @@
package im.vector.app.features.location package im.vector.app.features.location
import android.content.Context import android.content.Context
import android.content.res.TypedArray
import android.graphics.drawable.ColorDrawable
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.res.use
import im.vector.app.R
import im.vector.app.core.glide.GlideApp
import im.vector.app.databinding.ViewMapLoadingErrorBinding import im.vector.app.databinding.ViewMapLoadingErrorBinding
import im.vector.app.features.themes.ThemeUtils
/** /**
* Custom view to display an error when map fails to load. * Custom view to display an error when map fails to load.
@ -29,10 +35,35 @@ class MapLoadingErrorView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0 context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr) { ) : ConstraintLayout(context, attrs, defStyleAttr) {
init { private val binding = ViewMapLoadingErrorBinding.inflate(
ViewMapLoadingErrorBinding.inflate(
LayoutInflater.from(context), LayoutInflater.from(context),
this this
) )
init {
context.obtainStyledAttributes(
attrs,
R.styleable.MapLoadingErrorView,
0,
0
).use {
setErrorDescription(it)
}
}
private fun setErrorDescription(typedArray: TypedArray) {
val description = typedArray.getString(R.styleable.MapLoadingErrorView_mapErrorDescription)
if(description.isNullOrEmpty()) {
binding.mapLoadingErrorDescription.setText(R.string.location_share_loading_map_error)
} else {
binding.mapLoadingErrorDescription.text = description
}
}
fun render(mapLoadingErrorViewState: MapLoadingErrorViewState) {
GlideApp.with(binding.mapLoadingErrorBackground)
.load(ColorDrawable(ThemeUtils.getColor(context, R.attr.vctr_system)))
.transform(mapLoadingErrorViewState.backgroundTransformation)
.into(binding.mapLoadingErrorBackground)
} }
} }

View File

@ -0,0 +1,21 @@
/*
* 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.bumptech.glide.load.resource.bitmap.BitmapTransformation
data class MapLoadingErrorViewState(val backgroundTransformation: BitmapTransformation)

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="70dp"
android:height="70dp"
android:viewportWidth="70"
android:viewportHeight="70">
<path
android:pathData="M34.9997,5.8335C23.7122,5.8335 14.583,15.2112 14.583,26.8059C14.583,39.2995 27.4747,56.5269 32.783,63.0882C33.9497,64.5264 36.0788,64.5264 37.2455,63.0882C42.5247,56.5269 55.4163,39.2995 55.4163,26.8059C55.4163,15.2112 46.2872,5.8335 34.9997,5.8335ZM34.9997,34.2961C30.9747,34.2961 27.708,30.9405 27.708,26.8059C27.708,22.6714 30.9747,19.3158 34.9997,19.3158C39.0247,19.3158 42.2913,22.6714 42.2913,26.8059C42.2913,30.9405 39.0247,34.2961 34.9997,34.2961Z"
android:fillColor="#C1C6CD"/>
</vector>

View File

@ -29,21 +29,16 @@
app:layout_constraintTop_toTopOf="@id/staticMapImageView" app:layout_constraintTop_toTopOf="@id/staticMapImageView"
app:layout_constraintVertical_bias="1.0" /> app:layout_constraintVertical_bias="1.0" />
<TextView <im.vector.app.features.location.MapLoadingErrorView
android:id="@+id/staticMapErrorTextView" android:id="@+id/staticMapLoadingError"
style="@style/Widget.Vector.TextView.Subtitle" android:layout_width="0dp"
android:layout_width="wrap_content" android:layout_height="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginTop="16dp"
android:layout_marginBottom="54dp"
android:text="@string/location_timeline_failed_to_load_map"
android:textColor="?vctr_content_tertiary"
android:visibility="gone" android:visibility="gone"
app:layout_constraintEnd_toEndOf="@id/staticMapPinImageView" app:layout_constraintBottom_toTopOf="@id/locationLiveRunningBanner"
app:layout_constraintStart_toStartOf="@id/staticMapPinImageView" app:layout_constraintEnd_toEndOf="@id/staticMapImageView"
app:layout_constraintTop_toBottomOf="@id/staticMapPinImageView" app:layout_constraintStart_toStartOf="@id/staticMapImageView"
tools:visibility="visible" /> app:layout_constraintTop_toTopOf="@id/staticMapImageView"
app:mapErrorDescription="@string/location_timeline_failed_to_load_map" />
<im.vector.app.features.location.live.LiveLocationRunningBannerView <im.vector.app.features.location.live.LiveLocationRunningBannerView
android:id="@+id/liveLocationRunningBanner" android:id="@+id/liveLocationRunningBanner"

View File

@ -6,11 +6,21 @@
android:layout_height="match_parent" android:layout_height="match_parent"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout"> tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<ImageView
android:id="@+id/mapLoadingErrorBackground"
android:layout_width="0dp"
android:layout_height="0dp"
android:src="?vctr_system"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription" />
<androidx.constraintlayout.helper.widget.Flow <androidx.constraintlayout.helper.widget.Flow
android:id="@+id/mapLoadingErrorContainer" android:id="@+id/mapLoadingErrorContainer"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
android:background="?vctr_system"
android:orientation="vertical" android:orientation="vertical"
app:constraint_referenced_ids="mapLoadingErrorIcon,mapLoadingErrorDescription" app:constraint_referenced_ids="mapLoadingErrorIcon,mapLoadingErrorDescription"
app:flow_verticalGap="12dp" app:flow_verticalGap="12dp"
@ -24,8 +34,8 @@
android:id="@+id/mapLoadingErrorIcon" android:id="@+id/mapLoadingErrorIcon"
android:layout_width="28dp" android:layout_width="28dp"
android:layout_height="28dp" android:layout_height="28dp"
android:contentDescription="@string/a11y_location_share_option_pinned_icon" android:src="@drawable/ic_warning_badge"
android:src="@drawable/ic_warning_badge" /> tools:ignore="ContentDescription" />
<TextView <TextView
android:id="@+id/mapLoadingErrorDescription" android:id="@+id/mapLoadingErrorDescription"