Extract and unit test MultipleTapToUnlock
This commit is contained in:
@@ -15,5 +15,7 @@ android {
|
||||
dependencies {
|
||||
testImplementation(libs.test.junit)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.test.truth)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
package io.element.android.libraries.ui.utils
|
||||
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
/**
|
||||
* Returns true if the user has tapped [numberOfTapToUnlock] times in a short amount of time.
|
||||
* The counter is reset after 2 seconds of inactivity.
|
||||
*
|
||||
* @param numberOfTapToUnlock The number of taps required to unlock.
|
||||
*/
|
||||
class MultipleTapToUnlock(
|
||||
private val numberOfTapToUnlock: Int = 7,
|
||||
) {
|
||||
private var counter = numberOfTapToUnlock
|
||||
private var currentJob: Job? = null
|
||||
|
||||
fun unlock(scope: CoroutineScope): Boolean {
|
||||
counter--
|
||||
currentJob?.cancel()
|
||||
return if (counter > 0) {
|
||||
currentJob = scope.launch {
|
||||
delay(2.seconds)
|
||||
// Reset counter if user is not fast enough
|
||||
counter = numberOfTapToUnlock
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2025 New Vector Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||
|
||||
package io.element.android.libraries.ui.utils
|
||||
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.test.advanceTimeBy
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class MultipleTapToUnlockTest {
|
||||
@Test
|
||||
fun `test multiple tap should unlock`() = runTest {
|
||||
val sut = MultipleTapToUnlock(3)
|
||||
assertThat(sut.unlock(backgroundScope)).isFalse()
|
||||
assertThat(sut.unlock(backgroundScope)).isFalse()
|
||||
assertThat(sut.unlock(backgroundScope)).isTrue()
|
||||
assertThat(sut.unlock(backgroundScope)).isTrue()
|
||||
// All next call returns true
|
||||
advanceTimeBy(3.seconds)
|
||||
assertThat(sut.unlock(backgroundScope)).isTrue()
|
||||
}
|
||||
@Test
|
||||
fun `test waiting should reset counter`() = runTest {
|
||||
val sut = MultipleTapToUnlock(3)
|
||||
assertThat(sut.unlock(backgroundScope)).isFalse()
|
||||
assertThat(sut.unlock(backgroundScope)).isFalse()
|
||||
advanceTimeBy(3.seconds)
|
||||
assertThat(sut.unlock(backgroundScope)).isFalse()
|
||||
assertThat(sut.unlock(backgroundScope)).isFalse()
|
||||
assertThat(sut.unlock(backgroundScope)).isTrue()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user