長押し中はずっとイベントを発火する みたいな処理をやる

# コード

```kotlin
suspend fun PointerInputScope.detectTapOrLongPressingGestures(
    onTap: () -> Unit,
    onLongPressing: () -> Unit,
    longPressInterval: Long = 100L
) = coroutineScope {
    awaitEachGesture {
        val down = awaitFirstDown()
        val longPressChange = awaitLongPressOrCancellation(down.id)
        if (longPressChange == null) {
            onTap()
            return@awaitEachGesture
        }
        launch {
            do {
                delay(longPressInterval)
                onLongPressing()
            } while (!longPressChange.isConsumed)
        }
        waitForUpOrCancellation()
        longPressChange.consume()
    }
}
```

連続タップは onTap の連続にしている。
２本指は、両方の指が完全にリリースされればLongPress終了。２本指でもLongPressに満たない間隔で両方の指をリリースすればタップにしている。


# 使ったもの

### PointerInputScope
```kotlin
fun Modifier.pointerInput(key1: Any?, block: suspend PointerInputScope.() -> Unit): Modifier
```
ポインタイベントが発生中はそのイベントに対する処理を行い、イベントが発生してない時は 次のイベントが発生するまで中断されるスコープ。これをループすることで イベントの待ち受けをやっている。

https://developer.android.com/reference/kotlin/androidx/compose/ui/input/pointer/PointerInputScope


### PointerInputScope.*()

PointerInputScope には gesture を検出するための拡張関数がいくつかある。
<br>

```kotlin
PointerInputScope.detectTapGestures(
    onDoubleTap: ((Offset) -> Unit)?,
    onLongPress: ((Offset) -> Unit)?,
    onPress: suspend PressGestureScope.(Offset) -> Unit,
    onTap: ((Offset) -> Unit)?
)
```
これのonLongPress は 、一度長押しを消費すると 指を離すまで次のポインタイベントは 処理されないので 、今回は使えない。

<br>

```kotlin
PointerInputScope.awaitEachGesture(block: suspend AwaitPointerEventScope.() -> Unit)
```

Gestureを細かく制御するならこっち

### AwaitPointerEventScope

https://developer.android.com/reference/kotlin/androidx/compose/ui/input/pointer/AwaitPointerEventScope

そのままだが、ポインタイベントを待ち受けるためのスコープ。
そのための拡張関数が用意されている。

```kotlin
AwaitPointerEventScope.awaitFirstDown(
    requireUnconsumed: Boolean,
    pass: PointerEventPass
)
```
最初に、指が Downされるまで 待ち受け

<br>

```kotlin
AwaitPointerEventScope.awaitLongPressOrCancellation(
    pointerId: PointerId
)
```

ダブルタップを検出するまで待ち受け


<br>

```kotlin
AwaitPointerEventScope.waitForUpOrCancellation(
    pass: PointerEventPass
)
```
指が Upされるまで待ち受け。2本以上の指によるGestureは全てがUpされるまで待ち受け。

### PointerInputChange

`await*` 関数は `PointerInputChange` を返す。
これはその時点での指の位置や押されてるかどうか といった Pointerイベントの状態。

https://developer.android.com/reference/kotlin/androidx/compose/ui/input/pointer/PointerInputChange

```kotlin
val longPressChange = awaitLongPressOrCancellation(down.id)
```

> 長押しが検出される前にすべてのポインタが上げられた場合、または他のジェスチャが変更を消費した場合はNULL

キャンセル と ほかGesture による消費 を見分けたい時はどうすればいいんだろう.. 
今回はnull であればシングルタップとみなしている。

### 処理をコメントしていく

```kotlin
suspend fun PointerInputScope.detectTapOrLongPressingGestures(
    onTap: () -> Unit,
    onLongPressing: () -> Unit,
    longPressInterval: Long = 100L
) = coroutineScope {
    awaitEachGesture {
        // 最初のDownイベントを待ち受け
        val down = awaitFirstDown()
        // 上のdownイベントの長押しを待ち受け
        val longPressChange = awaitLongPressOrCancellation(down.id)
        // 長押しでなければシングルタップ
        if (longPressChange == null) {
            onTap()
            return@awaitEachGesture
        }
        launch {
            // 長押し が消費される（指が離される）まで 長押しイベントを上げる
            do {
                delay(longPressInterval)
                onLongPressing()
            } while (!longPressChange.isConsumed)
        }
        // 指が離れるのを待ち受け
        waitForUpOrCancellation()
        // 指が離れたら、長押しを消費して 上のループを抜ける
        longPressChange.consume()
    }
}

```