From a611de6da4a7b6d71ebd4484eb12c20c2df173b6 Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Fri, 17 Feb 2023 15:19:00 +0100 Subject: [PATCH 01/11] Add instrumented test to test repository together with room database. Improve data handling on first boot. --- .../tichucounter/ExampleInstrumentedTest.kt | 22 -- .../RepositoryInstrumentedTest.kt | 280 ++++++++++++++++++ .../me/zobrist/tichucounter/data/DaoBase.kt | 5 +- .../me/zobrist/tichucounter/data/GameDao.kt | 9 +- .../tichucounter/data/GameWithScores.kt | 5 +- .../zobrist/tichucounter/data/entity/Game.kt | 10 +- .../tichucounter/repository/GameRepository.kt | 58 ++-- .../zobrist/tichucounter/ui/MainViewModel.kt | 14 +- .../ui/counter/CounterViewModel.kt | 8 +- 9 files changed, 331 insertions(+), 80 deletions(-) delete mode 100644 app/src/androidTest/java/me/zobrist/tichucounter/ExampleInstrumentedTest.kt create mode 100644 app/src/androidTest/java/me/zobrist/tichucounter/RepositoryInstrumentedTest.kt diff --git a/app/src/androidTest/java/me/zobrist/tichucounter/ExampleInstrumentedTest.kt b/app/src/androidTest/java/me/zobrist/tichucounter/ExampleInstrumentedTest.kt deleted file mode 100644 index ef6f3a2..0000000 --- a/app/src/androidTest/java/me/zobrist/tichucounter/ExampleInstrumentedTest.kt +++ /dev/null @@ -1,22 +0,0 @@ -package me.zobrist.tichucounter - -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.platform.app.InstrumentationRegistry -import org.junit.Assert.* -import org.junit.Test -import org.junit.runner.RunWith - -/** - * Instrumented test, which will execute on an Android device. - * - * See [testing documentation](http://d.android.com/tools/testing). - */ -@RunWith(AndroidJUnit4::class) -class ExampleInstrumentedTest { - @Test - fun useAppContext() { - // Context of the app under test. - val appContext = InstrumentationRegistry.getInstrumentation().targetContext - assertEquals("me.zobrist.tichucounter", appContext.packageName) - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/me/zobrist/tichucounter/RepositoryInstrumentedTest.kt b/app/src/androidTest/java/me/zobrist/tichucounter/RepositoryInstrumentedTest.kt new file mode 100644 index 0000000..7620a3b --- /dev/null +++ b/app/src/androidTest/java/me/zobrist/tichucounter/RepositoryInstrumentedTest.kt @@ -0,0 +1,280 @@ +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 +import org.junit.Assert.* +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException +import java.util.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class RepositoryInstrumentedTest { + private lateinit var gameDao: GameDao + private lateinit var roundDao: RoundDao + private lateinit var repository: GameRepository + private lateinit var db: AppDatabase + + @Before + fun createDb() { + val context = ApplicationProvider.getApplicationContext() + db = Room.inMemoryDatabaseBuilder( + context, AppDatabase::class.java).build() + roundDao = db.roundDao() + gameDao = db.gameDao() + + repository = GameRepository(gameDao, roundDao) + } + + @After + @Throws(IOException::class) + fun closeDb() { + db.close() + } + + @Test + @Throws(Exception::class) + fun gameInitialisation() = runTest { + repository.getActiveGameFlow().take(1).collect { + assertEquals("TeamA", it.game.nameA) + assertEquals("TeamB", it.game.nameB) + assertTrue(it.game.active) + assertEquals(0, it.rounds.count()) + } + } + + @Test + @Throws(Exception::class) + fun modifyNames() = runTest { + + repository.getActiveGameFlow().take(1).collect { + } + + repository.updateActiveTeamName(nameA ="aaa") + + repository.getActiveGameFlow().take(1).collect { + assertEquals("aaa", it.game.nameA) + assertEquals("TeamB", it.game.nameB) + } + + repository.updateActiveTeamName(nameB ="bbb") + + repository.getActiveGameFlow().take(1).collect { + assertEquals("aaa", it.game.nameA) + assertEquals("bbb", it.game.nameB) + } + + + } + + + + @Test + @Throws(Exception::class) + fun newGame() = runTest { + + repository.getActiveGameFlow().take(1).collect { + } + + repository.newGame() + repository.newGame() + repository.newGame() + repository.newGame() + repository.newGame() + + + repository.getAllWithRoundFlow().take(1).collect() { it -> + assertEquals(6, it.count()) + + var uid: Long = 1 + it.forEach {game -> + assertEquals(uid++, game.game.uid) + assertEquals(0, game.rounds.count()) + } + } + } + + @Test + @Throws(Exception::class) + fun setActive() = runTest { + + repository.getActiveGameFlow().take(1).collect { + } + + repository.newGame() + repository.newGame() + repository.newGame() + repository.newGame() + repository.newGame() + + + repository.getAllWithRoundFlow().take(1).collect() { it -> + val filtered = it.filter { it.game.active } + assertEquals(1, filtered.count()) + assertEquals(6, filtered.first().game.uid) + } + + repository.setActive(2) + + repository.getAllWithRoundFlow().take(1).collect() { it -> + val filtered = it.filter { it.game.active } + assertEquals(1, filtered.count()) + assertEquals(2, filtered.first().game.uid) + } + } + + @Test + @Throws(Exception::class) + fun addRoundToActiveGame() = runTest { + + repository.getActiveGameFlow().take(1).collect { + } + + repository.newGame() + repository.newGame() + repository.newGame() + repository.newGame() + 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.getAllWithRoundFlow().take(1).collect() { it -> + val filtered = it.filter { it.rounds.isNotEmpty() } + assertEquals(1, filtered.count()) + assertEquals(6, filtered.first().rounds.count()) + } + } + + @Test + @Throws(Exception::class) + fun lastRound() = runTest { + + repository.getActiveGameFlow().take(1).collect { + } + + repository.newGame() + repository.newGame() + repository.newGame() + repository.newGame() + repository.newGame() + + 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) + + var lastRound = repository.getLastRound() + assertEquals(6, lastRound?.scoreA) + assertEquals(6, lastRound?.scoreB) + + repository.deleteLastRound() + + lastRound = repository.getLastRound() + assertEquals(5, lastRound?.scoreA) + assertEquals(5, lastRound?.scoreB) + + repository.deleteLastRound() + repository.deleteLastRound() + repository.deleteLastRound() + repository.deleteLastRound() + repository.deleteLastRound() + + assertNull(repository.getLastRound()) + + // No error thrown + repository.deleteLastRound() + } + + @Test + @Throws(Exception::class) + fun deleteInactive() = runTest { + + repository.getActiveGameFlow().take(1).collect { + } + + 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) + } + assertEquals(6 * 6, roundDao.getAll().count()) + + repository.deleteAllInactive() + + // Consists of two transactions. Delete games then delete rounds. + repository.getAllWithRoundFlow().take(1).collect() { it -> + assertEquals(1, it.count()) + assertEquals(6, it.first().rounds.count()) + } + assertEquals(6, roundDao.getAll().count()) + } + + @Test + @Throws(Exception::class) + fun deleteById() = runTest { + + repository.getActiveGameFlow().take(1).collect { + } + + 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) + } + + // Non existing Id + repository.deleteGame(10) + + repository.getAllWithRoundFlow().take(1).collect() { it -> + assertEquals(7, it.count()) + } + + // Non existing Id + val toDelete: Long = 3 + repository.deleteGame(toDelete) + + repository.getAllWithRoundFlow().take(1).collect() { it -> + assertEquals(6, it.count()) + assertEquals(0, it.count { it.game.uid == toDelete }) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/data/DaoBase.kt b/app/src/main/java/me/zobrist/tichucounter/data/DaoBase.kt index 5f4a9c3..74cad1c 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/DaoBase.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/DaoBase.kt @@ -1,9 +1,6 @@ package me.zobrist.tichucounter.data -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.Update +import androidx.room.* @Dao interface DaoBase { diff --git a/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt b/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt index 00a9855..634b3cb 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt @@ -12,7 +12,7 @@ interface GameDao : DaoBase { fun getAll(): Flow> @Transaction - @Query("SELECT * FROM game where uid ") + @Query("SELECT * FROM game") fun getGamesWithRounds(): Flow> @Transaction @@ -20,10 +20,13 @@ interface GameDao : DaoBase { fun getActiveWithRounds(): Flow @Query("SELECT * FROM game WHERE uid is :gameId") - fun getGameById(gameId: Long): Flow + fun getGameById(gameId: Long): Game @Query("SELECT * FROM game WHERE active is 1") - fun getActive(): Flow + fun getActiveAsFlow(): Flow + + @Query("SELECT * FROM game WHERE active is 1") + fun getActive(): Game? @Query("UPDATE game SET active = 1 WHERE uid is :gameId;") diff --git a/app/src/main/java/me/zobrist/tichucounter/data/GameWithScores.kt b/app/src/main/java/me/zobrist/tichucounter/data/GameWithScores.kt index 44d2d54..463f6ac 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/GameWithScores.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/GameWithScores.kt @@ -5,13 +5,14 @@ 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( - @Embedded val game: Game, + @Embedded val game: Game = Game(), @Relation( parentColumn = "uid", entityColumn = "gameId" ) - val rounds: List + val rounds: List = emptyList() ) \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/data/entity/Game.kt b/app/src/main/java/me/zobrist/tichucounter/data/entity/Game.kt index e70a2de..314c8af 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/entity/Game.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/entity/Game.kt @@ -6,10 +6,10 @@ import java.util.* @Entity data class Game( - var active: Boolean, - var nameA: String, - var nameB: String, - val created: Date, - var modified: Date, + var active: Boolean = true, + var nameA: String = "TeamA", + var nameB: String = "TeamB", + val created: Date = Date(), + var modified: Date = Date(), @PrimaryKey(autoGenerate = true) override val uid: Long = 0 ) : IEntity \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt b/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt index 5716266..47082b6 100644 --- a/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt +++ b/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt @@ -1,11 +1,7 @@ package me.zobrist.tichucounter.repository -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.take -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* +import kotlinx.coroutines.flow.* import me.zobrist.tichucounter.data.GameDao import me.zobrist.tichucounter.data.GameWithScores import me.zobrist.tichucounter.data.RoundDao @@ -19,20 +15,15 @@ class GameRepository @Inject constructor( private val roundDao: RoundDao ) { - private var _activeGame: Game? = null - - val activeGame: Game - get() { - return _activeGame!! - } + private var activeGame: Game = Game(true, "TeamA", "TeamB", Date(), Date()) init { CoroutineScope(Dispatchers.IO).launch { - gameDao.getActive().collect { + gameDao.getActiveAsFlow().collect { if (it == null) { - gameDao.insert(Game(true, "TeamA", "TeamB", Date(), Date())) + newGame() } else { - _activeGame = it + activeGame = it } } } @@ -40,16 +31,25 @@ class GameRepository @Inject constructor( suspend fun newGame() { withContext(Dispatchers.IO) { - val id = - gameDao.insert(Game(true, activeGame.nameA, activeGame.nameB, Date(), Date())) + val id = gameDao.insert(Game(true, activeGame.nameA, activeGame.nameB, Date(), Date())) setActive(id) } } - suspend fun updateGame(game: Game) { - game.modified = Date() + suspend fun updateActiveTeamName(nameA: String? = null, nameB: String? = null) { + + val newA = nameA ?: activeGame.nameA + val newB = nameB ?: activeGame.nameB + + if(newA == activeGame.nameA && newB == activeGame.nameB) { + return + } + + activeGame.modified = Date() + activeGame.nameA = newA + activeGame.nameB = newB withContext(Dispatchers.IO) { - gameDao.update(game) + gameDao.update(activeGame) } } @@ -92,11 +92,10 @@ class GameRepository @Inject constructor( suspend fun deleteGame(uid: Long) { withContext(Dispatchers.IO) { try { - gameDao.getGameById(uid).take(1).collect { - gameDao.delete(it) - val rounds = roundDao.getAllForGame(it.uid) - roundDao.delete(rounds) - } + val game = gameDao.getGameById(uid) + gameDao.delete(game) + val rounds = roundDao.getAllForGame(game.uid) + roundDao.delete(rounds) } catch (_: NullPointerException) { } } @@ -107,9 +106,8 @@ class GameRepository @Inject constructor( try { gameDao.getAll().take(1).collect { games -> - val activeId = games.first { it.active }.uid - val gamesToDelete = games.filter { !it.active } - val roundsToDelete = roundDao.getAll().filter { it.gameId != activeId } + val gamesToDelete = games.filter { it.uid != activeGame.uid } + val roundsToDelete = roundDao.getAll().filter { it.gameId != activeGame.uid } gameDao.delete(gamesToDelete) roundDao.delete(roundsToDelete) @@ -119,8 +117,8 @@ class GameRepository @Inject constructor( } } - fun getActiveGameFlow(): Flow { - return gameDao.getActiveWithRounds() + fun getActiveGameFlow(): Flow { + return gameDao.getActiveWithRounds().filter { it != null }.map { it!! } } fun getAllWithRoundFlow(): Flow> { diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/MainViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/MainViewModel.kt index 53fde44..5905bf0 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/MainViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/MainViewModel.kt @@ -34,17 +34,15 @@ class MainViewModel @Inject constructor( gameRepository.getActiveGameFlow().collect { - activeGameHasRounds = it?.rounds?.isNotEmpty() == true + activeGameHasRounds = it.rounds.isNotEmpty() == true - if (it != null) { - isUndoActionActive = it.rounds.isNotEmpty() + isUndoActionActive = it.rounds.isNotEmpty() - if (expectedRoundCount != it.rounds.count()) { - redoRounds.clear() - } - - expectedRoundCount = it.rounds.count() + if (expectedRoundCount != it.rounds.count()) { + redoRounds.clear() } + + expectedRoundCount = it.rounds.count() } } } diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt index e6ae019..38164e1 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt @@ -245,17 +245,13 @@ class CounterViewModel @Inject constructor( override fun updateNameA(value: String) { viewModelScope.launch { - val game = gameRepository.activeGame - game.nameA = value - gameRepository.updateGame(game) + gameRepository.updateActiveTeamName(nameA = value) } } override fun updateNameB(value: String) { viewModelScope.launch { - val game = gameRepository.activeGame - game.nameB = value - gameRepository.updateGame(game) + gameRepository.updateActiveTeamName(nameB = value) } } -- 2.49.1 From b3bdbfbc059e90718c5f1825096fa4d4bac3b745 Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Fri, 3 Mar 2023 11:27:18 +0100 Subject: [PATCH 02/11] [#23] Limit input to 5 digits. --- .../tichucounter/domain/StringExtensions.kt | 12 ++++++++++ .../ui/counter/CounterViewModel.kt | 10 ++++++--- .../tichucounter/StringExtensionTest.kt | 22 +++++++++++++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/me/zobrist/tichucounter/domain/StringExtensions.kt create mode 100644 app/src/test/java/me/zobrist/tichucounter/StringExtensionTest.kt diff --git a/app/src/main/java/me/zobrist/tichucounter/domain/StringExtensions.kt b/app/src/main/java/me/zobrist/tichucounter/domain/StringExtensions.kt new file mode 100644 index 0000000..3c2328a --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/domain/StringExtensions.kt @@ -0,0 +1,12 @@ +package me.zobrist.tichucounter.domain + +fun String.digitCount(): Int { + var count = 0 + this.forEach { + if (it.isDigit()) { + count++ + } + } + return count +} + diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt index 38164e1..a1810cd 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt @@ -10,6 +10,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.launch import me.zobrist.tichucounter.data.entity.Round import me.zobrist.tichucounter.domain.Tichu +import me.zobrist.tichucounter.domain.digitCount import me.zobrist.tichucounter.domain.getTotalPoints import me.zobrist.tichucounter.repository.GameRepository import javax.inject.Inject @@ -202,9 +203,12 @@ class CounterViewModel @Inject constructor( override fun digitClicked(digit: String) { focusLastInput() - activeValue += digit - updateOtherScore() - updateSubmitButton() + if(activeValue.digitCount() < 5) + { + activeValue += digit + updateOtherScore() + updateSubmitButton() + } } override fun negateClicked() { diff --git a/app/src/test/java/me/zobrist/tichucounter/StringExtensionTest.kt b/app/src/test/java/me/zobrist/tichucounter/StringExtensionTest.kt new file mode 100644 index 0000000..ec3ac64 --- /dev/null +++ b/app/src/test/java/me/zobrist/tichucounter/StringExtensionTest.kt @@ -0,0 +1,22 @@ +package me.zobrist.tichucounter + +import me.zobrist.tichucounter.domain.digitCount +import org.junit.Assert.* +import org.junit.Test + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class StringExtensionTest { + @Test + fun calculation_isCorrect() { + assertEquals(0, "-".digitCount()) + assertEquals(0, "".digitCount()) + assertEquals(2, "-10".digitCount()) + assertEquals(2, "10".digitCount()) + assertEquals(10, "1234567890".digitCount()) + assertEquals(10, "-1234567890".digitCount()) + } +} \ No newline at end of file -- 2.49.1 From a1f344580dec4aba17041e742618ea14e3d200ef Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Fri, 3 Mar 2023 11:45:23 +0100 Subject: [PATCH 03/11] [#23] Prevent trailing zeros stacking up. closes #23 --- .../ui/counter/CounterViewModel.kt | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt index a1810cd..5362fbf 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt @@ -203,12 +203,23 @@ class CounterViewModel @Inject constructor( override fun digitClicked(digit: String) { focusLastInput() - if(activeValue.digitCount() < 5) + + if(activeValue.digitCount() >= 5) { - activeValue += digit - updateOtherScore() - updateSubmitButton() + // 5 digits is enough + return } + + val newValue = activeValue + digit + + try { + activeValue = newValue.toInt().toString() + } catch (_: NumberFormatException) + { + } + + updateOtherScore() + updateSubmitButton() } override fun negateClicked() { -- 2.49.1 From 17d861403edd9ffe65530daddad6205d2063f0f9 Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Fri, 3 Mar 2023 13:20:22 +0100 Subject: [PATCH 04/11] [#24] Add long press to delete functionality. Reformat code. closes #24 --- .../RepositoryInstrumentedTest.kt | 63 +++++++++---------- .../me/zobrist/tichucounter/data/DaoBase.kt | 5 +- .../tichucounter/data/GameWithScores.kt | 3 +- .../tichucounter/domain/NavExtensions.kt | 2 +- .../tichucounter/repository/GameRepository.kt | 12 +++- .../tichucounter/ui/counter/CounterView.kt | 6 +- .../ui/counter/CounterViewModel.kt | 53 ++++++++++++---- .../tichucounter/ui/counter/KeyboardView.kt | 35 ++++++++--- .../tichucounter/StringExtensionTest.kt | 2 +- 9 files changed, 113 insertions(+), 68 deletions(-) diff --git a/app/src/androidTest/java/me/zobrist/tichucounter/RepositoryInstrumentedTest.kt b/app/src/androidTest/java/me/zobrist/tichucounter/RepositoryInstrumentedTest.kt index 7620a3b..0373232 100644 --- a/app/src/androidTest/java/me/zobrist/tichucounter/RepositoryInstrumentedTest.kt +++ b/app/src/androidTest/java/me/zobrist/tichucounter/RepositoryInstrumentedTest.kt @@ -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() 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 diff --git a/app/src/main/java/me/zobrist/tichucounter/data/DaoBase.kt b/app/src/main/java/me/zobrist/tichucounter/data/DaoBase.kt index 74cad1c..5f4a9c3 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/DaoBase.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/DaoBase.kt @@ -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 { diff --git a/app/src/main/java/me/zobrist/tichucounter/data/GameWithScores.kt b/app/src/main/java/me/zobrist/tichucounter/data/GameWithScores.kt index 463f6ac..3b184c3 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/GameWithScores.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/GameWithScores.kt @@ -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 = emptyList() + val rounds: List = emptyList() ) \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/domain/NavExtensions.kt b/app/src/main/java/me/zobrist/tichucounter/domain/NavExtensions.kt index 68771fb..31b0240 100644 --- a/app/src/main/java/me/zobrist/tichucounter/domain/NavExtensions.kt +++ b/app/src/main/java/me/zobrist/tichucounter/domain/NavExtensions.kt @@ -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 diff --git a/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt b/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt index 47082b6..78e5bb3 100644 --- a/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt +++ b/app/src/main/java/me/zobrist/tichucounter/repository/GameRepository.kt @@ -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 } diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt index 1113d76..a4cd96c 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt @@ -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) { + + } } \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt index 5362fbf..6f58c27 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt @@ -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) + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/KeyboardView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/KeyboardView.kt index 273cd8b..2716e99 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/KeyboardView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/KeyboardView.kt @@ -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 = {}) } } } \ No newline at end of file diff --git a/app/src/test/java/me/zobrist/tichucounter/StringExtensionTest.kt b/app/src/test/java/me/zobrist/tichucounter/StringExtensionTest.kt index ec3ac64..70a6650 100644 --- a/app/src/test/java/me/zobrist/tichucounter/StringExtensionTest.kt +++ b/app/src/test/java/me/zobrist/tichucounter/StringExtensionTest.kt @@ -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 /** -- 2.49.1 From bcc3bd38482f525ac683a5e3d4e80aa5ab1a61d3 Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Fri, 3 Mar 2023 14:28:13 +0100 Subject: [PATCH 05/11] Freeze build image version --- .drone.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.drone.yml b/.drone.yml index a2c0734..1e101e3 100644 --- a/.drone.yml +++ b/.drone.yml @@ -26,7 +26,7 @@ steps: - echo "versionCode=$timestamp" >> version.properties - name: build - image: mingc/android-build-box + image: mingc/android-build-box:1.22.0 commands: - ./gradlew test - ./gradlew assembleRelease -- 2.49.1 From 5a229d6c57900947a97797da0775618dfbbfafb2 Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Fri, 3 Mar 2023 15:03:52 +0100 Subject: [PATCH 06/11] [#29] Disable swap button on invalid score. closed [#29] --- .../zobrist/tichucounter/ui/counter/CounterView.kt | 2 +- .../tichucounter/ui/counter/CounterViewModel.kt | 8 ++++---- .../tichucounter/ui/counter/KeyboardView.kt | 14 +++++++------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt index a4cd96c..a9b0497 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterView.kt @@ -103,7 +103,7 @@ internal class PreviewViewModel : ICounterViewModel { override var teamNameB: String = "Team B" override var currentScoreA: String = "" override var currentScoreB: String = "45" - override var enableSubmit: Boolean = false + override var isValidRound: Boolean = false override var isAFocused: Boolean = false override var isBFocused: Boolean = false override var requestFocusA: FocusRequester = FocusRequester() diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt index 6f58c27..81bed74 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/CounterViewModel.kt @@ -23,7 +23,7 @@ interface IKeyBoardViewModel { val currentScoreA: String val currentScoreB: String - val enableSubmit: Boolean + val isValidRound: Boolean val isAFocused: Boolean val isBFocused: Boolean val requestFocusA: FocusRequester @@ -87,7 +87,7 @@ class CounterViewModel @Inject constructor( override var currentScoreB by mutableStateOf("") private set - override var enableSubmit by mutableStateOf(false) + override var isValidRound by mutableStateOf(false) private set override var isAFocused by mutableStateOf(false) @@ -194,7 +194,7 @@ class CounterViewModel @Inject constructor( } override fun updateSubmitButton() { - enableSubmit = isValidTichuRound() + isValidRound = isValidTichuRound() } override fun submitClicked() { @@ -203,7 +203,7 @@ class CounterViewModel @Inject constructor( } currentScoreA = "" currentScoreB = "" - enableSubmit = false + isValidRound = false } override fun digitClicked(digit: String) { diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/KeyboardView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/KeyboardView.kt index 2716e99..a88778c 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/KeyboardView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/KeyboardView.kt @@ -32,7 +32,7 @@ fun KeyBoardView(viewModel: IKeyBoardViewModel) { viewModel.currentScoreB, viewModel.requestFocusA, viewModel.requestFocusB, - viewModel.enableSubmit, + viewModel.isValidRound, viewModel.isAFocused, viewModel.isBFocused, { viewModel.updateFocusStateA(it) }, @@ -53,7 +53,7 @@ fun KeyboardView( scoreB: String, requestFocusA: FocusRequester, requestFocusB: FocusRequester, - enableSubmit: Boolean, + isValidScore: Boolean, focusStateA: Boolean, focusStateB: Boolean, updateFocusStateA: (Boolean) -> Unit, @@ -85,7 +85,7 @@ fun KeyboardView( shape = MaterialTheme.shapes.extraSmall ) { Column { - IconButton(onClick = onSwapClicked) { + IconButton(onClick = onSwapClicked, enabled = isValidScore) { Icon(Icons.Outlined.SwapHoriz, null) } } @@ -197,7 +197,7 @@ fun KeyboardView( } } Column(Modifier.weight(1f)) { - KeyboardIconButton(Icons.Outlined.Check, enableSubmit) { + KeyboardIconButton(Icons.Outlined.Check, isValidScore) { submitClicked() } } @@ -323,11 +323,11 @@ fun KeyboardViewPreview() { AppTheme { Surface { KeyboardView( - "1", - "3511", + "10", + "190", FocusRequester(), FocusRequester(), - enableSubmit = false, + isValidScore = false, focusStateA = true, focusStateB = false, updateFocusStateA = {}, -- 2.49.1 From 9e658853eb17d293f0abe29c8250ba1fd91913d2 Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Fri, 3 Mar 2023 15:31:36 +0100 Subject: [PATCH 07/11] Update Gradle and Java --- app/build.gradle | 6 +++--- build.gradle | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6628138..c74999c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,11 +72,11 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '11' } namespace 'me.zobrist.tichucounter' packagingOptions { diff --git a/build.gradle b/build.gradle index b2dbe00..5f2692d 100644 --- a/build.gradle +++ b/build.gradle @@ -9,7 +9,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.4.0' + classpath 'com.android.tools.build:gradle:7.4.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong -- 2.49.1 From 8e26f6b337511784e33238616b32a8fafd16b28f Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Sat, 4 Mar 2023 08:34:26 +0100 Subject: [PATCH 08/11] fixBuild (#30) Reviewed-on: https://gitea.zobrist.me/fabian/TichuCounter/pulls/30 --- .drone.yml | 7 ++++--- app/build.gradle | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.drone.yml b/.drone.yml index 1e101e3..1042e9f 100644 --- a/.drone.yml +++ b/.drone.yml @@ -26,7 +26,7 @@ steps: - echo "versionCode=$timestamp" >> version.properties - name: build - image: mingc/android-build-box:1.22.0 + image: mingc/android-build-box commands: - ./gradlew test - ./gradlew assembleRelease @@ -39,9 +39,10 @@ steps: from_secret: SeafileApiKey APK_FILE: app/build/outputs/apk/release/app-release.apk BUNDLE_FILE: app/build/outputs/bundle/release/app-release.aab - SEAFILE_REPO: daffda8b-5840-4a65-b6d0-73b991facfb6 + SEAFILE_REPO: 6debeef9-121e-46ba-acc7-81e109fdcbdd commands: - 'UPLOAD_URL=$(curl -H "Authorization: Token $SEAFILE_API_KEY" https://seafile.zobrist.me/api2/repos/$SEAFILE_REPO/upload-link/ | tr -d "\"")' + - echo $UPLOAD_URL - 'curl -H "Authorization: Token $SEAFILE_API_KEY" -F file=@$APK_FILE -F parent_dir=/ -F relative_path=latest/ -F replace=1 "$UPLOAD_URL"' - 'curl -H "Authorization: Token $SEAFILE_API_KEY" -F file=@$BUNDLE_FILE -F parent_dir=/ -F relative_path=latest/ -F replace=1 "$UPLOAD_URL"' @@ -52,7 +53,7 @@ steps: from_secret: SeafileApiKey APK_FILE: app/build/outputs/apk/release/app-release.apk BUNDLE_FILE: app/build/outputs/bundle/release/app-release.aab - SEAFILE_REPO: daffda8b-5840-4a65-b6d0-73b991facfb6 + SEAFILE_REPO: 6debeef9-121e-46ba-acc7-81e109fdcbdd commands: - 'UPLOAD_URL=$(curl -H "Authorization: Token $SEAFILE_API_KEY" https://seafile.zobrist.me/api2/repos/$SEAFILE_REPO/upload-link/ | tr -d "\"")' - 'curl -H "Authorization: Token $SEAFILE_API_KEY" -F file=@$APK_FILE -F parent_dir=/ -F relative_path=tagged/$DRONE_TAG/ -F replace=1 "$UPLOAD_URL"' diff --git a/app/build.gradle b/app/build.gradle index c74999c..55e0f8f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -72,11 +72,11 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_11 - targetCompatibility JavaVersion.VERSION_11 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '11' + jvmTarget = '17' } namespace 'me.zobrist.tichucounter' packagingOptions { -- 2.49.1 From 801a17d759d002f94707204b13f91c6fa76d2dfc Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Tue, 7 Mar 2023 20:53:57 +0100 Subject: [PATCH 09/11] Increase version to 2.1 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 55e0f8f..f1eb6f9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,7 +16,7 @@ def keystoreProperties = new Properties() def versionProperties = new Properties() def versionMajor = 2 -def versionMinor = 0 +def versionMinor = 1 // Load your keystore.properties file into the keystoreProperties object. keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) -- 2.49.1 From 343d1d8e754f20c125570fc018e268f382df7ba8 Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Tue, 7 Mar 2023 22:41:49 +0100 Subject: [PATCH 10/11] [#20] Add missing translation key. closes #20 --- app/src/main/res/values-de/strings.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index be60463..5a3a559 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -22,5 +22,6 @@ Aktives Spiel Vergangene Spiele Counter + About \ No newline at end of file -- 2.49.1 From 895264de2aa8563d72c8dee1335d390af97cd89e Mon Sep 17 00:00:00 2001 From: Fabian Zobrist Date: Fri, 10 Mar 2023 15:08:38 +0100 Subject: [PATCH 11/11] [#34] Add contact button and play store button to about page. closes [#34] --- .../tichucounter/ui/about/AboutView.kt | 81 ++++++++++++++----- app/src/main/res/values-de/strings.xml | 1 + app/src/main/res/values/strings.xml | 2 + 3 files changed, 62 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/about/AboutView.kt b/app/src/main/java/me/zobrist/tichucounter/ui/about/AboutView.kt index 9202b49..eb9085c 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/about/AboutView.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/about/AboutView.kt @@ -2,17 +2,16 @@ package me.zobrist.tichucounter.ui.about import android.content.res.Configuration import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Mail +import androidx.compose.material.icons.outlined.Shop +import androidx.compose.material3.* import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment.Companion.Top import androidx.compose.ui.Modifier import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalUriHandler import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -24,24 +23,62 @@ import me.zobrist.tichucounter.ui.AppTheme @Composable fun AboutView() { - Row(Modifier.padding(20.dp)) { - Image( - modifier = Modifier - .height(80.dp) - .padding(end = 10.dp) - .align(Top), - painter = painterResource(R.drawable.app_logo), - contentDescription = null, - contentScale = ContentScale.Fit - ) - Column { - Text( - text = stringResource(id = R.string.app_name), - style = MaterialTheme.typography.headlineMedium + val uriHandler = LocalUriHandler.current + + Column( + modifier = Modifier + .padding( + top = 20.dp, + start = 20.dp, + end = 20.dp, + bottom = 40.dp + ), + + ) { + + Row() { + Image( + modifier = Modifier + .height(80.dp) + .padding(end = 10.dp) + .align(Top), + painter = painterResource(R.drawable.app_logo), + contentDescription = null, + contentScale = ContentScale.Fit ) - Text(text = "V" + BuildConfig.VERSION_NAME) + + Column { + Text( + text = stringResource(id = R.string.app_name), + style = MaterialTheme.typography.headlineMedium + ) + Text(text = "V" + BuildConfig.VERSION_NAME) + } } + + + Button( + modifier = Modifier + .fillMaxWidth() + .padding(top = 30.dp), + onClick = { uriHandler.openUri("market://details?id=me.zobrist.tichucounter") } + ) { + Icon(imageVector = Icons.Outlined.Shop, contentDescription = null) + Text(stringResource(id = R.string.play_store)) + } + + Button( + modifier = Modifier + .fillMaxWidth() + .padding(top = 30.dp), + onClick = { uriHandler.openUri("mailto:app@zobrist.me") } + ) { + Icon(imageVector = Icons.Outlined.Mail, contentDescription = null) + Text(stringResource(id = R.string.contact_us)) + } + + } } diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 5a3a559..ae3d8f6 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -23,5 +23,6 @@ Vergangene Spiele Counter About + Schreib uns \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e0d8623..30a2071 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -26,4 +26,6 @@ Old Games Counter About + Contact us + Play Store \ No newline at end of file -- 2.49.1