mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Cleanup code after a11y PR (#596) and fix some merging issues
This commit is contained in:
parent
8bd1fb08f7
commit
d387c310c8
12 changed files with 85 additions and 97 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ->
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -132,7 +132,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 +200,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 +232,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
|
||||
}
|
||||
|
||||
|
||||
|
@ -258,7 +258,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 {
|
||||
|
@ -298,11 +298,10 @@ class EmojiRecyclerAdapter(val dataSource: EmojiDataSource? = null, var reaction
|
|||
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 +314,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.UNKNWON
|
||||
}
|
||||
|
||||
//TODO better
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" />
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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" />
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue