Browse Source

Add assertions and prevent out of bounds crashes in the choice list adapters. Resolves #1906.

Aidan Follestad 5 years ago
parent
commit
6069c88c1d

+ 18 - 4
core/src/main/java/com/afollestad/materialdialogs/internal/list/MultiChoiceDialogAdapter.kt

@@ -57,7 +57,10 @@ internal class MultiChoiceViewHolder(
       titleView.isEnabled = value
     }
 
-  override fun onClick(view: View) = adapter.itemClicked(adapterPosition)
+  override fun onClick(view: View) {
+    if (adapterPosition < 0) return
+    adapter.itemClicked(adapterPosition)
+  }
 }
 
 /**
@@ -73,7 +76,8 @@ internal class MultiChoiceDialogAdapter(
   private val waitForActionButton: Boolean,
   private val allowEmptySelection: Boolean,
   internal var selection: MultiChoiceListener
-) : RecyclerView.Adapter<MultiChoiceViewHolder>(), DialogAdapter<CharSequence, MultiChoiceListener> {
+) : RecyclerView.Adapter<MultiChoiceViewHolder>(),
+    DialogAdapter<CharSequence, MultiChoiceListener> {
 
   private var currentSelection: IntArray = initialSelection
     set(value) {
@@ -201,7 +205,12 @@ internal class MultiChoiceDialogAdapter(
 
   override fun checkItems(indices: IntArray) {
     val existingSelection = this.currentSelection
-    val indicesToAdd = indices.filter { !existingSelection.contains(it) }
+    val indicesToAdd = indices.filter {
+      check(it >= 0 && it < items.size) {
+        "Index $it is out of range for this adapter of ${items.size} items."
+      }
+      !existingSelection.contains(it)
+    }
     this.currentSelection = this.currentSelection.appendAll(indicesToAdd)
     if (existingSelection.isEmpty()) {
       dialog.setActionButtonEnabled(POSITIVE, true)
@@ -210,7 +219,12 @@ internal class MultiChoiceDialogAdapter(
 
   override fun uncheckItems(indices: IntArray) {
     val existingSelection = this.currentSelection
-    val indicesToAdd = indices.filter { existingSelection.contains(it) }
+    val indicesToAdd = indices.filter {
+      check(it >= 0 && it < items.size) {
+        "Index $it is out of range for this adapter of ${items.size} items."
+      }
+      existingSelection.contains(it)
+    }
     this.currentSelection = this.currentSelection.removeAll(indicesToAdd)
         .also {
           if (it.isEmpty()) {

+ 12 - 2
core/src/main/java/com/afollestad/materialdialogs/internal/list/SingleChoiceDialogAdapter.kt

@@ -55,7 +55,10 @@ internal class SingleChoiceViewHolder(
       titleView.isEnabled = value
     }
 
-  override fun onClick(view: View) = adapter.itemClicked(adapterPosition)
+  override fun onClick(view: View) {
+    if (adapterPosition < 0) return
+    adapter.itemClicked(adapterPosition)
+  }
 }
 
 /**
@@ -70,7 +73,8 @@ internal class SingleChoiceDialogAdapter(
   initialSelection: Int,
   private val waitForActionButton: Boolean,
   internal var selection: SingleChoiceListener
-) : RecyclerView.Adapter<SingleChoiceViewHolder>(), DialogAdapter<CharSequence, SingleChoiceListener> {
+) : RecyclerView.Adapter<SingleChoiceViewHolder>(),
+    DialogAdapter<CharSequence, SingleChoiceListener> {
 
   private var currentSelection: Int = initialSelection
     set(value) {
@@ -179,12 +183,18 @@ internal class SingleChoiceDialogAdapter(
 
   override fun checkItems(indices: IntArray) {
     val targetIndex = if (indices.isNotEmpty()) indices[0] else -1
+    check(targetIndex >= 0 && targetIndex < items.size) {
+      "Index $targetIndex is out of range for this adapter of ${items.size} items."
+    }
     if (this.disabledIndices.contains(targetIndex)) return
     this.currentSelection = targetIndex
   }
 
   override fun uncheckItems(indices: IntArray) {
     val targetIndex = if (indices.isNotEmpty()) indices[0] else -1
+    check(targetIndex >= 0 && targetIndex < items.size) {
+      "Index $targetIndex is out of range for this adapter of ${items.size} items."
+    }
     if (this.disabledIndices.contains(targetIndex)) return
     this.currentSelection = -1
   }