[#24] Add long press to delete functionality.
Some checks are pending
continuous-integration/drone/push Build is running

Reformat code.
closes #24
This commit is contained in:
2023-03-03 13:20:22 +01:00
parent a1f344580d
commit 17d861403e
9 changed files with 113 additions and 68 deletions

View File

@@ -1,18 +1,13 @@
package me.zobrist.tichucounter
import android.content.Context
import androidx.compose.runtime.collectAsState
import androidx.lifecycle.asLiveData
import androidx.room.Room
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.runTest
import kotlinx.coroutines.withContext
import me.zobrist.tichucounter.data.AppDatabase
import me.zobrist.tichucounter.data.GameDao
import me.zobrist.tichucounter.data.GameWithScores
import me.zobrist.tichucounter.data.RoundDao
import me.zobrist.tichucounter.repository.GameRepository
import org.junit.After
@@ -39,7 +34,8 @@ class RepositoryInstrumentedTest {
fun createDb() {
val context = ApplicationProvider.getApplicationContext<Context>()
db = Room.inMemoryDatabaseBuilder(
context, AppDatabase::class.java).build()
context, AppDatabase::class.java
).build()
roundDao = db.roundDao()
gameDao = db.gameDao()
@@ -70,14 +66,14 @@ class RepositoryInstrumentedTest {
repository.getActiveGameFlow().take(1).collect {
}
repository.updateActiveTeamName(nameA ="aaa")
repository.updateActiveTeamName(nameA = "aaa")
repository.getActiveGameFlow().take(1).collect {
assertEquals("aaa", it.game.nameA)
assertEquals("TeamB", it.game.nameB)
}
repository.updateActiveTeamName(nameB ="bbb")
repository.updateActiveTeamName(nameB = "bbb")
repository.getActiveGameFlow().take(1).collect {
assertEquals("aaa", it.game.nameA)
@@ -88,7 +84,6 @@ class RepositoryInstrumentedTest {
}
@Test
@Throws(Exception::class)
fun newGame() = runTest {
@@ -107,7 +102,7 @@ class RepositoryInstrumentedTest {
assertEquals(6, it.count())
var uid: Long = 1
it.forEach {game ->
it.forEach { game ->
assertEquals(uid++, game.game.uid)
assertEquals(0, game.rounds.count())
}
@@ -157,12 +152,12 @@ class RepositoryInstrumentedTest {
repository.newGame()
repository.addRoundToActiveGame(1,1)
repository.addRoundToActiveGame(2,2)
repository.addRoundToActiveGame(3,3)
repository.addRoundToActiveGame(4,4)
repository.addRoundToActiveGame(5,5)
repository.addRoundToActiveGame(6,6)
repository.addRoundToActiveGame(1, 1)
repository.addRoundToActiveGame(2, 2)
repository.addRoundToActiveGame(3, 3)
repository.addRoundToActiveGame(4, 4)
repository.addRoundToActiveGame(5, 5)
repository.addRoundToActiveGame(6, 6)
repository.getAllWithRoundFlow().take(1).collect() { it ->
@@ -187,12 +182,12 @@ class RepositoryInstrumentedTest {
assertNull(repository.getLastRound())
repository.addRoundToActiveGame(1,1)
repository.addRoundToActiveGame(2,2)
repository.addRoundToActiveGame(3,3)
repository.addRoundToActiveGame(4,4)
repository.addRoundToActiveGame(5,5)
repository.addRoundToActiveGame(6,6)
repository.addRoundToActiveGame(1, 1)
repository.addRoundToActiveGame(2, 2)
repository.addRoundToActiveGame(3, 3)
repository.addRoundToActiveGame(4, 4)
repository.addRoundToActiveGame(5, 5)
repository.addRoundToActiveGame(6, 6)
var lastRound = repository.getLastRound()
assertEquals(6, lastRound?.scoreA)
@@ -225,12 +220,12 @@ class RepositoryInstrumentedTest {
for (i in 1..6) {
repository.newGame()
repository.addRoundToActiveGame(1,1)
repository.addRoundToActiveGame(2,2)
repository.addRoundToActiveGame(3,3)
repository.addRoundToActiveGame(4,4)
repository.addRoundToActiveGame(5,5)
repository.addRoundToActiveGame(6,6)
repository.addRoundToActiveGame(1, 1)
repository.addRoundToActiveGame(2, 2)
repository.addRoundToActiveGame(3, 3)
repository.addRoundToActiveGame(4, 4)
repository.addRoundToActiveGame(5, 5)
repository.addRoundToActiveGame(6, 6)
}
assertEquals(6 * 6, roundDao.getAll().count())
@@ -253,12 +248,12 @@ class RepositoryInstrumentedTest {
for (i in 1..6) {
repository.newGame()
repository.addRoundToActiveGame(1,1)
repository.addRoundToActiveGame(2,2)
repository.addRoundToActiveGame(3,3)
repository.addRoundToActiveGame(4,4)
repository.addRoundToActiveGame(5,5)
repository.addRoundToActiveGame(6,6)
repository.addRoundToActiveGame(1, 1)
repository.addRoundToActiveGame(2, 2)
repository.addRoundToActiveGame(3, 3)
repository.addRoundToActiveGame(4, 4)
repository.addRoundToActiveGame(5, 5)
repository.addRoundToActiveGame(6, 6)
}
// Non existing Id

View File

@@ -1,6 +1,9 @@
package me.zobrist.tichucounter.data
import androidx.room.*
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Update
@Dao
interface DaoBase<T> {

View File

@@ -5,7 +5,6 @@ import androidx.room.Entity
import androidx.room.Relation
import me.zobrist.tichucounter.data.entity.Game
import me.zobrist.tichucounter.data.entity.Round
import java.util.*
@Entity
data class GameWithScores(
@@ -14,5 +13,5 @@ data class GameWithScores(
parentColumn = "uid",
entityColumn = "gameId"
)
val rounds: List<Round> = emptyList()
val rounds: List<Round> = emptyList()
)

View File

@@ -5,7 +5,7 @@ import androidx.navigation.*
import androidx.navigation.compose.composable
fun NavController.navigate(route: Route) {
this.navigate(route.name){
this.navigate(route.name) {
// Pop up to the start destination of the graph to
// avoid building up a large stack of destinations
// on the back stack as users select items

View File

@@ -1,7 +1,13 @@
package me.zobrist.tichucounter.repository
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import me.zobrist.tichucounter.data.GameDao
import me.zobrist.tichucounter.data.GameWithScores
import me.zobrist.tichucounter.data.RoundDao
@@ -41,7 +47,7 @@ class GameRepository @Inject constructor(
val newA = nameA ?: activeGame.nameA
val newB = nameB ?: activeGame.nameB
if(newA == activeGame.nameA && newB == activeGame.nameB) {
if (newA == activeGame.nameA && newB == activeGame.nameB) {
return
}

View File

@@ -137,9 +137,6 @@ internal class PreviewViewModel : ICounterViewModel {
override fun addSub100Clicked(toAdd: Int) {
}
override fun deleteClicked() {
}
override fun updateNameA(value: String) {
}
@@ -161,4 +158,7 @@ internal class PreviewViewModel : ICounterViewModel {
override fun showKeyboard() {
}
override fun deleteState(pressed: Boolean) {
}
}

View File

@@ -7,6 +7,8 @@ import androidx.compose.ui.focus.FocusRequester
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import me.zobrist.tichucounter.data.entity.Round
import me.zobrist.tichucounter.domain.Tichu
@@ -38,12 +40,12 @@ interface IKeyBoardViewModel {
fun digitClicked(digit: String)
fun negateClicked()
fun addSub100Clicked(toAdd: Int)
fun deleteClicked()
fun updateFocusStateA(state: Boolean)
fun updateFocusStateB(state: Boolean)
fun swapInputScores()
fun hideKeyboard()
fun showKeyboard()
fun deleteState(pressed: Boolean)
}
@@ -138,6 +140,10 @@ class CounterViewModel @Inject constructor(
private var lastFocused = Focused.TEAM_A
private var deletePressed = false
private var deleteJob: Job? = null
init {
viewModelScope.launch {
gameRepository.getActiveGameFlow().collect {
@@ -204,8 +210,7 @@ class CounterViewModel @Inject constructor(
focusLastInput()
if(activeValue.digitCount() >= 5)
{
if (activeValue.digitCount() >= 5) {
// 5 digits is enough
return
}
@@ -214,8 +219,7 @@ class CounterViewModel @Inject constructor(
try {
activeValue = newValue.toInt().toString()
} catch (_: NumberFormatException)
{
} catch (_: NumberFormatException) {
}
updateOtherScore()
@@ -250,14 +254,6 @@ class CounterViewModel @Inject constructor(
updateSubmitButton()
}
override fun deleteClicked() {
if (activeValue != "") {
activeValue = activeValue.dropLast(1)
}
updateOtherScore()
updateSubmitButton()
}
override fun updateNameA(value: String) {
viewModelScope.launch {
gameRepository.updateActiveTeamName(nameA = value)
@@ -297,4 +293,35 @@ class CounterViewModel @Inject constructor(
override fun showKeyboard() {
keyboardHidden = false
}
override fun deleteState(pressed: Boolean) {
deletePressed = pressed
if (deletePressed) {
if (deleteJob?.isActive != true) {
deleteJob = deleteRepeatedlyUntilRelease()
}
} else {
deleteJob?.cancel()
}
}
private fun deleteLastDigitActive() {
if (activeValue != "") {
activeValue = activeValue.dropLast(1)
}
updateOtherScore()
updateSubmitButton()
}
private fun deleteRepeatedlyUntilRelease(): Job {
return viewModelScope.launch {
deleteLastDigitActive()
delay(500)
while (deletePressed) {
deleteLastDigitActive()
delay(100)
}
}
}
}

View File

@@ -2,6 +2,8 @@ package me.zobrist.tichucounter.ui.counter
import android.content.res.Configuration
import androidx.compose.animation.core.*
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Backspace
@@ -37,11 +39,11 @@ fun KeyBoardView(viewModel: IKeyBoardViewModel) {
{ viewModel.updateFocusStateB(it) },
{ viewModel.digitClicked(it) },
{ viewModel.addSub100Clicked(it) },
{ viewModel.deleteClicked() },
{ viewModel.negateClicked() },
{ viewModel.submitClicked() },
{ viewModel.hideKeyboard() },
{ viewModel.swapInputScores() }
{ viewModel.swapInputScores() },
{ viewModel.deleteState(it) }
)
}
@@ -58,11 +60,11 @@ fun KeyboardView(
updateFocusStateB: (Boolean) -> Unit,
digitClicked: (String) -> Unit,
addSub100Clicked: (Int) -> Unit,
deleteClicked: () -> Unit,
negateClicked: () -> Unit,
submitClicked: () -> Unit,
hideKeyboardClicked: () -> Unit,
onSwapClicked: () -> Unit
onSwapClicked: () -> Unit,
deleteButtonPressedState: (Boolean) -> Unit
) {
Column {
Row(Modifier.height(IntrinsicSize.Max)) {
@@ -164,9 +166,16 @@ fun KeyboardView(
}
}
Column(Modifier.weight(1f)) {
KeyboardIconButton(Icons.Outlined.Backspace) {
deleteClicked()
}
val interactionSource = remember { MutableInteractionSource() }
val deletePressed by interactionSource.collectIsPressedAsState()
deleteButtonPressedState(deletePressed)
KeyboardIconButton(
icon = Icons.Outlined.Backspace,
interactionSource = interactionSource
) {}
}
}
@@ -219,7 +228,12 @@ fun KeyboardTextButton(text: String, onClicked: () -> Unit) {
}
@Composable
fun KeyboardIconButton(icon: ImageVector, enabled: Boolean = true, onClicked: () -> Unit) {
fun KeyboardIconButton(
icon: ImageVector,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
onClicked: () -> Unit
) {
ElevatedButton(
onClick = { onClicked() },
@@ -228,6 +242,7 @@ fun KeyboardIconButton(icon: ImageVector, enabled: Boolean = true, onClicked: ()
.height(50.dp)
.padding(2.dp),
enabled = enabled,
interactionSource = interactionSource
) {
Icon(
icon,
@@ -319,11 +334,11 @@ fun KeyboardViewPreview() {
updateFocusStateB = {},
digitClicked = {},
addSub100Clicked = {},
deleteClicked = {},
negateClicked = {},
submitClicked = {},
hideKeyboardClicked = {},
onSwapClicked = {})
onSwapClicked = {},
deleteButtonPressedState = {})
}
}
}

View File

@@ -1,7 +1,7 @@
package me.zobrist.tichucounter
import me.zobrist.tichucounter.domain.digitCount
import org.junit.Assert.*
import org.junit.Assert.assertEquals
import org.junit.Test
/**