improve MediaPreviewAdapter / fix IndexOutOfBoundsException (#4514)

Found this crash in the Google Play reports:

```
Exception java.lang.IndexOutOfBoundsException: Index -1 out of bounds for length 0
  at jdk.internal.util.Preconditions.outOfBounds (Preconditions.java:64)
  at jdk.internal.util.Preconditions.outOfBoundsCheckIndex (Preconditions.java:70)
  at jdk.internal.util.Preconditions.checkIndex (Preconditions.java:266)
  at java.util.Objects.checkIndex (Objects.java:359)
  at java.util.ArrayList.get (ArrayList.java:434)
  at java.util.Collections$UnmodifiableList.get (Collections.java:1394)
  at com.keylesspalace.tusky.components.compose.MediaPreviewAdapter.onMediaClick (MediaPreviewAdapter.java:45)
  at com.keylesspalace.tusky.components.compose.MediaPreviewAdapter.access$onMediaClick (MediaPreviewAdapter.java:32)
  at com.keylesspalace.tusky.components.compose.MediaPreviewAdapter$PreviewViewHolder._init_$lambda$0 (MediaPreviewAdapter.java:144)
  at android.view.View.performClick (View.java:7535)
  at android.view.View.performClickInternal (View.java:7512)
  at android.view.View.-$$Nest$mperformClickInternal
  at android.view.View$PerformClick.run (View.java:29488)
  at android.os.Handler.handleCallback (Handler.java:984)
  at android.os.Handler.dispatchMessage (Handler.java:104)
  at android.os.Looper.loopOnce (Looper.java:238)
  at android.os.Looper.loop (Looper.java:357)
  at android.app.ActivityThread.main (ActivityThread.java:8118)
  at java.lang.reflect.Method.invoke
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:548)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:957)
```

Can't reproduce, but seems to be some kind of race condition where the
view is clicked at the same time as it is being removed from the
`RecyclerView`. Not using the index in the click listener should resolve
the problem. Also refactored to `ListAdapter` to not deal with the
`AsyncListDiffer` manually.
This commit is contained in:
Konrad Pozniak 2024-06-19 16:51:12 +02:00 committed by GitHub
parent 5251287b53
commit b895ce936e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 18 additions and 31 deletions

View File

@ -21,8 +21,8 @@ import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.PopupMenu import android.widget.PopupMenu
import androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
@ -35,14 +35,21 @@ class MediaPreviewAdapter(
private val onAddFocus: (ComposeActivity.QueuedMedia) -> Unit, private val onAddFocus: (ComposeActivity.QueuedMedia) -> Unit,
private val onEditImage: (ComposeActivity.QueuedMedia) -> Unit, private val onEditImage: (ComposeActivity.QueuedMedia) -> Unit,
private val onRemove: (ComposeActivity.QueuedMedia) -> Unit private val onRemove: (ComposeActivity.QueuedMedia) -> Unit
) : RecyclerView.Adapter<MediaPreviewAdapter.PreviewViewHolder>() { ) : ListAdapter<ComposeActivity.QueuedMedia, MediaPreviewAdapter.PreviewViewHolder>(
object : DiffUtil.ItemCallback<ComposeActivity.QueuedMedia>() {
override fun areItemsTheSame(
oldItem: ComposeActivity.QueuedMedia,
newItem: ComposeActivity.QueuedMedia
) = oldItem.localId == newItem.localId
fun submitList(list: List<ComposeActivity.QueuedMedia>) { override fun areContentsTheSame(
this.differ.submitList(list) oldItem: ComposeActivity.QueuedMedia,
newItem: ComposeActivity.QueuedMedia
) = oldItem == newItem
} }
) {
private fun onMediaClick(position: Int, view: View) { private fun onMediaClick(item: ComposeActivity.QueuedMedia, view: View) {
val item = differ.currentList[position]
val popup = PopupMenu(view.context, view) val popup = PopupMenu(view.context, view)
val addCaptionId = 1 val addCaptionId = 1
val addFocusId = 2 val addFocusId = 2
@ -73,14 +80,12 @@ class MediaPreviewAdapter(
private val thumbnailViewSize = private val thumbnailViewSize =
context.resources.getDimensionPixelSize(R.dimen.compose_media_preview_size) context.resources.getDimensionPixelSize(R.dimen.compose_media_preview_size)
override fun getItemCount(): Int = differ.currentList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PreviewViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PreviewViewHolder {
return PreviewViewHolder(ProgressImageView(parent.context)) return PreviewViewHolder(ProgressImageView(parent.context))
} }
override fun onBindViewHolder(holder: PreviewViewHolder, position: Int) { override fun onBindViewHolder(holder: PreviewViewHolder, position: Int) {
val item = differ.currentList[position] val item = getItem(position)
holder.progressImageView.setChecked(!item.description.isNullOrEmpty()) holder.progressImageView.setChecked(!item.description.isNullOrEmpty())
holder.progressImageView.setProgress(item.uploadPercent) holder.progressImageView.setProgress(item.uploadPercent)
if (item.type == ComposeActivity.QueuedMedia.Type.AUDIO) { if (item.type == ComposeActivity.QueuedMedia.Type.AUDIO) {
@ -107,28 +112,13 @@ class MediaPreviewAdapter(
} }
glide.into(imageView) glide.into(imageView)
holder.progressImageView.setOnClickListener {
onMediaClick(item, holder.progressImageView)
}
} }
} }
private val differ = AsyncListDiffer(
this,
object : DiffUtil.ItemCallback<ComposeActivity.QueuedMedia>() {
override fun areItemsTheSame(
oldItem: ComposeActivity.QueuedMedia,
newItem: ComposeActivity.QueuedMedia
): Boolean {
return oldItem.localId == newItem.localId
}
override fun areContentsTheSame(
oldItem: ComposeActivity.QueuedMedia,
newItem: ComposeActivity.QueuedMedia
): Boolean {
return oldItem == newItem
}
}
)
inner class PreviewViewHolder(val progressImageView: ProgressImageView) : inner class PreviewViewHolder(val progressImageView: ProgressImageView) :
RecyclerView.ViewHolder(progressImageView) { RecyclerView.ViewHolder(progressImageView) {
init { init {
@ -140,9 +130,6 @@ class MediaPreviewAdapter(
layoutParams.setMargins(margin, 0, margin, marginBottom) layoutParams.setMargins(margin, 0, margin, marginBottom)
progressImageView.layoutParams = layoutParams progressImageView.layoutParams = layoutParams
progressImageView.scaleType = ImageView.ScaleType.CENTER_CROP progressImageView.scaleType = ImageView.ScaleType.CENTER_CROP
progressImageView.setOnClickListener {
onMediaClick(bindingAdapterPosition, progressImageView)
}
} }
} }
} }