1 /* 2 * Copyright 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 @file:Suppress("NOTHING_TO_INLINE", "KotlinRedundantDiagnosticSuppress") 18 19 package androidx.compose.ui.input.pointer.util 20 21 import androidx.compose.ui.input.pointer.PointerId 22 23 /** 24 * This collection is specifically for dealing with [PointerId] values. We know that they contain 25 * [Long] values, so we store them in an underlying LongArray. We want to be able to resize the 26 * array if there are many ids to be stored, so we recreate the internal LongArray as necessary 27 * (since LongArray is not itself resizable). 28 */ 29 internal class PointerIdArray { 30 /** 31 * The size of this [PointerIdArray], which is equal to the number of ids stored in the array. 32 */ 33 // Note that this is different than the size of the backing LongArray, which may allocate more 34 // entries to avoid resizing for every additional id that is added. 35 var size = 0 36 private set 37 38 /** Returns index of last item in array */ 39 inline val lastIndex: Int 40 get() = size - 1 41 42 /** 43 * The ids are stored as Long values in a LongArray. LongArray is not resizable, and we may need 44 * to expand this array if there are many pointer ids in use at any given time, so we keep the 45 * LongArray private and resize the PointerIdArray by allocating a larger LongArray (and copying 46 * existing values to it) as necessary. 47 * 48 * By default, we allocate the underlying array with 2 elements, since it is uncommon (though 49 * possible) to have more than two ids at a time. 50 */ 51 private var internalArray = LongArray(2) 52 53 /** 54 * Returns the PointerId at the given index. This getter allows use of [] syntax to retrieve 55 * values. 56 */ getnull57 operator fun get(index: Int): PointerId { 58 return PointerId(internalArray[index]) 59 } 60 61 /** 62 * Removes the given [PointerId] from this array, if it exists. 63 * 64 * @return true if [pointerId] was in the array, false otherwise 65 */ removenull66 inline fun remove(pointerId: PointerId): Boolean { 67 return remove(pointerId.value) 68 } 69 70 /** 71 * Removes a [PointerId] with the given value from this array, if it exists. 72 * 73 * @return true if a [PointerId] with the value [pointerIdValue] was in the array, false 74 * otherwise 75 */ removenull76 fun remove(pointerIdValue: Long): Boolean { 77 for (i in 0 until size) { 78 if (pointerIdValue == internalArray[i]) { 79 for (j in i until size - 1) { 80 internalArray[j] = internalArray[j + 1] 81 } 82 size-- 83 return true 84 } 85 } 86 return false 87 } 88 89 /** 90 * Removes the [PointerId] at the given index value, if the index is less than the size of the 91 * array. 92 * 93 * @return true if a [PointerId] at that index was removed, false otherwise 94 */ removeAtnull95 fun removeAt(index: Int): Boolean { 96 if (index < size) { 97 for (i in index until size - 1) { 98 internalArray[i] = internalArray[i + 1] 99 } 100 size-- 101 return true 102 } 103 return false 104 } 105 106 /** Returns the current size of the array */ isEmptynull107 fun isEmpty() = size == 0 108 109 /** 110 * Adds the given pointerId value to this array unless it is already there. 111 * 112 * @return true if id was added, false otherwise 113 */ 114 fun add(value: Long): Boolean { 115 if (!contains(value)) { 116 set(size, value) 117 return true 118 } 119 return false 120 } 121 122 /** 123 * Adds the given pointerId value to this array unless it is already there. 124 * 125 * @return true if id was added, false otherwise 126 */ addnull127 inline fun add(pointerId: PointerId): Boolean { 128 return add(pointerId.value) 129 } 130 131 /** 132 * Sets the value at the given index to a [PointerId] with the value [value]. The index must be 133 * less than or equal to the current size of the array. If it is equal to the size of the array, 134 * the storage in the array will be expanded to ensure that the item can be added to the end of 135 * it. 136 */ setnull137 operator fun set(index: Int, value: Long) { 138 var internalArray = internalArray 139 if (index >= internalArray.size) { 140 // Increase the size of the backing array 141 internalArray = resizeStorage(index + 1) 142 } 143 internalArray[index] = value 144 if (index >= size) size = index + 1 145 } 146 resizeStoragenull147 private fun resizeStorage(minSize: Int): LongArray { 148 return internalArray.copyOf(maxOf(minSize, internalArray.size * 2)).apply { 149 internalArray = this 150 } 151 } 152 153 /** 154 * Sets the value at the given index to [pointerId]. The index must be less than or equal to the 155 * current size of the array. If it is equal to the size of the array, the storage in the array 156 * will be expanded to ensure that the item can be added to the end of it. 157 */ setnull158 inline operator fun set(index: Int, pointerId: PointerId) { 159 set(index, pointerId.value) 160 } 161 162 /** Clears the array. The new [size] of the array will be 0. */ clearnull163 fun clear() { 164 // No need to clear, just reset the size. Elements beyond the current size are ignored. 165 size = 0 166 } 167 168 /** Returns true if [pointerId] is in the array, false otherwise */ containsnull169 inline fun contains(pointerId: PointerId): Boolean { 170 return contains(pointerId.value) 171 } 172 173 /** Returns true if a [PointerId] with the given value is in the array, false otherwise */ containsnull174 fun contains(pointerIdValue: Long): Boolean { 175 for (i in 0 until size) { 176 if (internalArray[i] == pointerIdValue) return true 177 } 178 return false 179 } 180 } 181