Merge pull request #608 from vector-im/feature/a11y_review

Feature/a11y review
This commit is contained in:
Benoit Marty 2019-10-08 11:16:08 +02:00 committed by GitHub
commit d6e5c5a857
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 106 additions and 117 deletions

View file

@ -168,21 +168,21 @@ Mode details here: https://medium.com/@RiotChat/introducing-the-riotx-beta-for-a
Changes in RiotX 0.0.0 (2019-XX-XX)
===================================================
Features:
Features:
-
Improvements:
Improvements 🙌:
-
Other changes:
-
Bugfix:
Bugfix 🐛:
-
Translations:
Translations 🗣:
-
Build:
Build 🧱:
-

View file

@ -21,22 +21,11 @@ package im.vector.riotx.core.ui.views
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import androidx.core.view.updateLayoutParams
import butterknife.ButterKnife
import com.airbnb.epoxy.VisibilityState
import com.google.android.material.internal.ViewUtils.dpToPx
import im.vector.riotx.R
import im.vector.riotx.features.themes.ThemeUtils
import kotlinx.android.synthetic.main.view_jump_to_read_marker.view.*
import me.gujun.android.span.span
import me.saket.bettermovementmethod.BetterLinkMovementMethod
import timber.log.Timber
class JumpToReadMarkerView @JvmOverloads constructor(
context: Context,

View file

@ -28,7 +28,7 @@ import im.vector.riotx.features.home.room.detail.timeline.item.ReadReceiptData
import kotlinx.android.synthetic.main.view_read_receipts.view.*
private const val MAX_RECEIPT_DISPLAYED = 5
private const val MAX_RECEIPT_DESCRIBED = 4
private const val MAX_RECEIPT_DESCRIBED = 3
class ReadReceiptsView @JvmOverloads constructor(
context: Context,
@ -52,7 +52,6 @@ class ReadReceiptsView @JvmOverloads constructor(
setOnClickListener(clickListener)
if (readReceipts.isNotEmpty()) {
isVisible = true
val displayNames = arrayListOf<String>()
for (index in 0 until MAX_RECEIPT_DISPLAYED) {
val receiptData = readReceipts.getOrNull(index)
if (receiptData == null) {
@ -60,11 +59,14 @@ class ReadReceiptsView @JvmOverloads constructor(
} else {
receiptAvatars[index].visibility = View.VISIBLE
avatarRenderer.render(receiptData.avatarUrl, receiptData.userId, receiptData.displayName, receiptAvatars[index])
if (null !=receiptData.displayName && displayNames.size <MAX_RECEIPT_DESCRIBED) {
displayNames.add(receiptData.displayName);
}
}
}
val displayNames = readReceipts
.mapNotNull { it.displayName }
.filter { it.isNotBlank() }
.take(MAX_RECEIPT_DESCRIBED)
if (readReceipts.size > MAX_RECEIPT_DISPLAYED) {
receiptMore.visibility = View.VISIBLE
receiptMore.text = context.getString(
@ -73,13 +75,31 @@ class ReadReceiptsView @JvmOverloads constructor(
} else {
receiptMore.visibility = View.GONE
}
when (displayNames.size) {
0 -> setContentDescription(context.getResources().getQuantityString(R.plurals.fallback_users_read, readReceipts.size))
1 -> setContentDescription(context.getString(R.string.one_user_read, displayNames.get(0)))
2 -> setContentDescription(context.getString(R.string.two_users_read, displayNames.get(0), displayNames.get(1)))
3 -> setContentDescription(context.getString(R.string.three_users_read, displayNames.get(0), displayNames.get(1), displayNames.get(2)))
else -> setContentDescription(context.getString(R.string.two_and_some_others_read,
displayNames.get(0), displayNames.get(1), (readReceipts.size -2)))
contentDescription = when (readReceipts.size) {
1 ->
if (displayNames.size == 1) {
context.getString(R.string.one_user_read, displayNames[0])
} else {
context.resources.getQuantityString(R.plurals.fallback_users_read, readReceipts.size)
}
2 ->
if (displayNames.size == 2) {
context.getString(R.string.two_users_read, displayNames[0], displayNames[1])
} else {
context.resources.getQuantityString(R.plurals.fallback_users_read, readReceipts.size)
}
3 ->
if (displayNames.size == 3) {
context.getString(R.string.three_users_read, displayNames[0], displayNames[1], displayNames[2])
} else {
context.resources.getQuantityString(R.plurals.fallback_users_read, readReceipts.size)
}
else ->
if (displayNames.size >= 2) {
context.getString(R.string.two_and_some_others_read, displayNames[0], displayNames[1], (readReceipts.size - 2))
} else {
context.resources.getQuantityString(R.plurals.fallback_users_read, readReceipts.size)
}
}
} else {
isVisible = false

View file

@ -32,6 +32,7 @@ import android.view.inputmethod.InputMethodManager
import android.widget.TextView
import android.widget.Toast
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityOptionsCompat
import androidx.core.content.ContextCompat
@ -79,8 +80,6 @@ import im.vector.riotx.core.platform.VectorBaseFragment
import im.vector.riotx.core.ui.views.JumpToReadMarkerView
import im.vector.riotx.core.ui.views.NotificationAreaView
import im.vector.riotx.core.utils.*
import im.vector.riotx.core.utils.Debouncer
import im.vector.riotx.core.utils.createUIHandler
import im.vector.riotx.features.autocomplete.command.AutocompleteCommandPresenter
import im.vector.riotx.features.autocomplete.command.CommandAutocompletePolicy
import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter
@ -362,7 +361,7 @@ class RoomDetailFragment :
private fun renderSpecialMode(event: TimelineEvent,
@DrawableRes iconRes: Int,
descriptionRes: Int,
@StringRes descriptionRes: Int,
defaultContent: String) {
commandAutocompletePolicy.enabled = false
//switch to expanded bar
@ -376,25 +375,17 @@ class RoomDetailFragment :
var formattedBody: CharSequence? = null
if (messageContent is MessageTextContent && messageContent.format == MessageType.FORMAT_MATRIX_HTML) {
val parser = Parser.builder().build()
val document = parser.parse(messageContent.formattedBody
?: messageContent.body)
val document = parser.parse(messageContent.formattedBody ?: messageContent.body)
formattedBody = eventHtmlRenderer.render(document)
}
composerLayout.composerRelatedMessageContent.text = formattedBody
?: nonFormattedBody
composerLayout.composerRelatedMessageContent.text = formattedBody ?: nonFormattedBody
updateComposerText(defaultContent)
composerLayout.composerRelatedMessageActionIcon.setImageDrawable(ContextCompat.getDrawable(requireContext(), iconRes))
composerLayout.sendButton.setContentDescription(getString(descriptionRes))
avatarRenderer.render(event.senderAvatar, event.root.senderId
?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)
avatarRenderer.render(event.senderAvatar,
event.root.senderId ?: "",
event.senderName,
composerLayout.composerRelatedMessageAvatar)
avatarRenderer.render(event.senderAvatar, event.root.senderId ?: "", event.senderName, composerLayout.composerRelatedMessageAvatar)
composerLayout.expand {
//need to do it here also when not using quick reply
focusComposerAndShowKeyboard()
@ -431,10 +422,8 @@ class RoomDetailFragment :
when (requestCode) {
REQUEST_FILES_REQUEST_CODE, TAKE_IMAGE_REQUEST_CODE -> handleMediaIntent(data)
REACTION_SELECT_REQUEST_CODE -> {
val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID)
?: return
val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT)
?: return
val eventId = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_EVENT_ID) ?: return
val reaction = data.getStringExtra(EmojiReactionPickerActivity.EXTRA_REACTION_RESULT) ?: return
//TODO check if already reacted with that?
roomDetailViewModel.process(RoomDetailActions.SendReaction(reaction, eventId))
}
@ -596,7 +585,6 @@ class RoomDetailFragment :
Timber.w("Send button is locked")
return@setOnClickListener
}
composerLayout.sendButton.setContentDescription(getString(R.string.send))
val textMessage = composerLayout.composerEditText.text.toString()
if (textMessage.isNotBlank()) {
lockSendButton = true

View file

@ -142,8 +142,13 @@ class LoginFragment : VectorBaseFragment() {
private fun renderPasswordField() {
passwordField.showPassword(passwordShown)
passwordReveal.setImageResource(if (passwordShown) R.drawable.ic_eye_closed_black else R.drawable.ic_eye_black)
passwordReveal.setContentDescription(if (passwordShown) getString(R.string.a11y_hide_password) else getString(R.string.a11y_show_password))
if (passwordShown) {
passwordReveal.setImageResource(R.drawable.ic_eye_closed_black)
passwordReveal.contentDescription = getString(R.string.a11y_hide_password)
} else {
passwordReveal.setImageResource(R.drawable.ic_eye_black)
passwordReveal.contentDescription = getString(R.string.a11y_show_password)
}
}
override fun invalidate() = withState(viewModel) { state ->

View file

@ -92,10 +92,14 @@ class EmojiReactionPickerActivity : VectorBaseActivity(), EmojiCompatFontProvide
it.rawData?.categories?.let { categories ->
for (category in categories) {
val s = category.emojis[0]
val newTab =tabLayout.newTab()
newTab.setText(it.rawData!!.emojis[s]!!.emojiString())
newTab.setContentDescription(category.name)
tabLayout.addTab(newTab)
tabLayout.newTab()
.also { tab ->
tab.text = it.rawData!!.emojis[s]!!.emojiString()
tab.contentDescription = category.name
}
.also { tab ->
tabLayout.addTab(tab)
}
}
tabLayout.addOnTabSelectedListener(tabLayoutSelectionListener)
}

View file

@ -43,28 +43,29 @@ import kotlin.math.abs
* TODO: Performances
* TODO: Scroll to section - Find a way to snap section to the top
*/
class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reactionClickListener: ReactionClickListener?) :
class EmojiRecyclerAdapter(private val dataSource: EmojiDataSource? = null,
private var reactionClickListener: ReactionClickListener?) :
RecyclerView.Adapter<EmojiRecyclerAdapter.ViewHolder>() {
var interactionListener: InteractionListener? = null
var mRecyclerView: RecyclerView? = null
private var mRecyclerView: RecyclerView? = null
var currentFirstVisibleSection = 0
private var currentFirstVisibleSection = 0
enum class ScrollState {
private enum class ScrollState {
IDLE,
DRAGGING,
SETTLING,
UNKNWON
UNKNOWN
}
private var scrollState = ScrollState.UNKNWON
private var scrollState = ScrollState.UNKNOWN
private var isFastScroll = false
val toUpdateWhenNotBusy = ArrayList<Pair<String, EmojiViewHolder>>()
private val toUpdateWhenNotBusy = ArrayList<Pair<String, EmojiViewHolder>>()
val itemClickListener = View.OnClickListener { view ->
private val itemClickListener = View.OnClickListener { view ->
mRecyclerView?.getChildLayoutPosition(view)?.let { itemPosition ->
if (itemPosition != RecyclerView.NO_POSITION) {
val categories = dataSource?.rawData?.categories ?: return@OnClickListener
@ -132,7 +133,7 @@ class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reaction
itemView.setOnClickListener(itemClickListener)
val viewHolder = when (viewType) {
R.layout.grid_section_header -> SectionViewHolder(itemView)
else -> EmojiViewHolder(itemView)
else -> EmojiViewHolder(itemView)
}
endTraceSession()
return viewHolder
@ -200,7 +201,7 @@ class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reaction
val sectionMojis = categories[sectionNumber].emojis
val sectionOffset = getSectionOffset(sectionNumber)
val emoji = sectionMojis[position - sectionOffset]
val item = dataSource!!.rawData!!.emojis[emoji]!!.emojiString()
val item = dataSource.rawData!!.emojis[emoji]!!.emojiString()
(holder as EmojiViewHolder).data = item
if (scrollState != ScrollState.SETTLING || !isFastScroll) {
// Log.i("PERF","Bind with draw at position:$position")
@ -232,13 +233,13 @@ class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reaction
override fun getItemCount(): Int {
dataSource?.rawData?.categories?.let {
return dataSource?.rawData?.categories?.let {
var count = /*number of sections*/ it.size
for (ad in it) {
count += ad.emojis.size
}
return count
} ?: kotlin.run { return 0 }
count
} ?: 0
}
@ -247,10 +248,10 @@ class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reaction
}
class EmojiViewHolder(itemView: View) : ViewHolder(itemView) {
private class EmojiViewHolder(itemView: View) : ViewHolder(itemView) {
var emojiView: EmojiDrawView = itemView.findViewById(R.id.grid_item_emoji_text)
val placeHolder: View = itemView.findViewById(R.id.grid_item_place_holder)
private var emojiView: EmojiDrawView = itemView.findViewById(R.id.grid_item_emoji_text)
private val placeHolder: View = itemView.findViewById(R.id.grid_item_place_holder)
var data: String? = null
@ -258,7 +259,7 @@ class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reaction
emojiView.emoji = s
if (s != null) {
emojiView.mLayout = getStaticLayoutForEmoji(s)
emojiView.setContentDescription(s)
emojiView.contentDescription = s
placeHolder.visibility = View.GONE
// emojiView.visibility = View.VISIBLE
} else {
@ -269,9 +270,9 @@ class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reaction
}
}
class SectionViewHolder(itemView: View) : ViewHolder(itemView) {
private class SectionViewHolder(itemView: View) : ViewHolder(itemView) {
var textView: TextView = itemView.findViewById(R.id.section_header_textview)
private var textView: TextView = itemView.findViewById(R.id.section_header_textview)
override fun bind(s: String?) {
textView.text = s
@ -292,17 +293,16 @@ class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reaction
}
}
val staticLayoutCache = HashMap<String, StaticLayout>()
private val staticLayoutCache = HashMap<String, StaticLayout>()
fun getStaticLayoutForEmoji(emoji: String): StaticLayout {
private fun getStaticLayoutForEmoji(emoji: String): StaticLayout {
var cachedLayout = staticLayoutCache[emoji]
if (cachedLayout == null) {
cachedLayout = StaticLayout(emoji, EmojiDrawView.tPaint, EmojiDrawView.emojiSize, Layout.Alignment.ALIGN_CENTER, 1f, 0f, true)
staticLayoutCache[emoji] = cachedLayout!!
staticLayoutCache[emoji] = cachedLayout
}
return cachedLayout!!
return cachedLayout
}
}
interface InteractionListener {
@ -315,10 +315,10 @@ class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reaction
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
scrollState = when (newState) {
RecyclerView.SCROLL_STATE_IDLE -> ScrollState.IDLE
RecyclerView.SCROLL_STATE_IDLE -> ScrollState.IDLE
RecyclerView.SCROLL_STATE_SETTLING -> ScrollState.SETTLING
RecyclerView.SCROLL_STATE_DRAGGING -> ScrollState.DRAGGING
else -> ScrollState.UNKNWON
else -> ScrollState.UNKNOWN
}
//TODO better

View file

@ -87,9 +87,9 @@
android:layout_marginTop="8dp"
android:background="?attr/selectableItemBackground"
android:scaleType="center"
android:contentDescription="@string/a11y_show_password"
android:src="@drawable/ic_eye_black"
android:tint="?attr/colorAccent" />
android:tint="?attr/colorAccent"
tools:contentDescription="@string/a11y_show_password" />
</FrameLayout>
@ -104,8 +104,8 @@
android:id="@+id/homeServerField"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri"
android:imeOptions="actionDone"
android:inputType="textUri"
android:maxLines="1" />
</com.google.android.material.textfield.TextInputLayout>

View file

@ -6,29 +6,6 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/syncProgressBarWrap"
android:layout_width="match_parent"
android:layout_height="3dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/roomToolbar"
tools:visibility="visible">
<ProgressBar
android:id="@+id/syncProgressBar"
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
android:layout_width="match_parent"
android:layout_height="14dp"
android:layout_gravity="center"
android:background="?riotx_header_panel_background"
android:indeterminate="true"
android:visibility="gone"
tools:visibility="visible" />
</FrameLayout>
<!-- Trick to remove surrounding padding (clip frome wrapping frame) -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/roomToolbar"
style="@style/VectorToolbarStyle"
@ -127,7 +104,8 @@
android:visibility="invisible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/syncStateView" />
app:layout_constraintTop_toBottomOf="@id/syncStateView"
tools:visibility="visible" />
<im.vector.riotx.core.ui.views.NotificationAreaView
android:id="@+id/notificationAreaView"

View file

@ -71,8 +71,8 @@
android:layout_width="22dp"
android:layout_height="22dp"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_close_round"
android:contentDescription="@string/cancel"
android:src="@drawable/ic_close_round"
android:tint="@color/riotx_notice"
tools:ignore="MissingConstraints" />
@ -89,8 +89,8 @@
android:layout_width="0dp"
android:layout_height="0dp"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_attachment"
android:contentDescription="@string/option_send_files"
android:src="@drawable/ic_attachment"
android:tint="?attr/colorAccent"
tools:ignore="MissingConstraints" />

View file

@ -11,13 +11,13 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toStartOf="@+id/closeJumpToReadMarkerView"
android:background="?attr/selectableItemBackground"
android:drawableStart="@drawable/arrow_up_circle"
android:drawablePadding="10dp"
android:background="?attr/selectableItemBackground"
android:gravity="center_vertical"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingTop="12dp"
android:paddingEnd="16dp"
android:paddingBottom="12dp"
android:text="@string/room_jump_to_first_unread"
android:textColor="@color/white" />
@ -25,12 +25,12 @@
<ImageView
android:id="@+id/closeJumpToReadMarkerView"
android:layout_width="wrap_content"
android:background="?attr/selectableItemBackground"
android:layout_height="match_parent"
android:layout_alignTop="@+id/jumpToReadMarkerLabelView"
android:layout_alignBottom="@+id/jumpToReadMarkerLabelView"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/action_close"
android:paddingStart="16dp"
android:paddingLeft="16dp"

View file

@ -10,12 +10,12 @@
android:id="@+id/receiptMore"
android:layout_width="wrap_content"
android:layout_height="18dp"
android:gravity="center"
android:textSize="12sp"
android:background="?vctr_pill_receipt"
android:gravity="center"
android:importantForAccessibility="no"
android:paddingStart="4dp"
android:paddingEnd="4dp"
android:importantForAccessibility = "no"
android:textSize="12sp"
tools:text="999+" />
<ImageView
@ -24,6 +24,7 @@
android:layout_height="18dp"
android:layout_marginStart="2dp"
android:adjustViewBounds="true"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
tools:src="@tools:sample/avatars" />
@ -33,6 +34,7 @@
android:layout_height="18dp"
android:layout_marginStart="2dp"
android:adjustViewBounds="true"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
tools:src="@tools:sample/avatars" />
@ -42,6 +44,7 @@
android:layout_height="18dp"
android:layout_marginStart="2dp"
android:adjustViewBounds="true"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
tools:src="@tools:sample/avatars" />
@ -51,6 +54,7 @@
android:layout_height="18dp"
android:layout_marginStart="2dp"
android:adjustViewBounds="true"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
tools:src="@tools:sample/avatars" />
@ -60,6 +64,7 @@
android:layout_height="18dp"
android:layout_marginStart="2dp"
android:adjustViewBounds="true"
android:importantForAccessibility="no"
android:scaleType="centerCrop"
tools:src="@tools:sample/avatars" />

View file

@ -26,9 +26,9 @@
<string name="a11y_jump_to_bottom">Jump to bottom</string>
<!-- Read receipts list a11y -->
<string name="two_and_some_others_read">%s, %s and %d others read</string>
<string name="three_users_read">%s, %s and %s read</string>
<string name="two_users_read">%s and %s read</string>
<string name="two_and_some_others_read">%1$s, %2$s and %3$d others read</string>
<string name="three_users_read">%1$s, %2$s and %3$s read</string>
<string name="two_users_read">%1$s and %2$s read</string>
<string name="one_user_read">%s read</string>
<plurals name="fallback_users_read">
<item quantity="one">1 user read</item>