Use composable scaffold.
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -96,25 +96,28 @@ dependencies {
|
||||
implementation 'androidx.fragment:fragment-ktx:1.5.5'
|
||||
implementation 'androidx.preference:preference-ktx:1.2.0'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1'
|
||||
implementation 'androidx.compose.material:material-icons-extended:1.3.1'
|
||||
implementation 'androidx.activity:activity-compose:1.3.1'
|
||||
implementation "androidx.compose.ui:ui:$compose_version"
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
|
||||
implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
|
||||
implementation 'androidx.activity:activity-compose:1.6.1'
|
||||
implementation "androidx.compose.ui:ui:1.3.3"
|
||||
implementation "androidx.compose.ui:ui-tooling-preview:1.3.3"
|
||||
implementation "androidx.compose.runtime:runtime-livedata:1.3.3"
|
||||
implementation "androidx.navigation:navigation-compose:2.5.3"
|
||||
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1"
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
|
||||
implementation "com.google.dagger:hilt-android:2.44"
|
||||
androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
|
||||
debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
|
||||
debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version"
|
||||
androidTestImplementation "androidx.compose.ui:ui-test-junit4:1.3.3"
|
||||
debugImplementation "androidx.compose.ui:ui-tooling:1.3.3"
|
||||
debugImplementation "androidx.compose.ui:ui-test-manifest:1.3.3"
|
||||
kapt "com.google.dagger:hilt-compiler:2.44"
|
||||
implementation("androidx.room:room-runtime:2.4.3")
|
||||
annotationProcessor("androidx.room:room-compiler:2.4.3")
|
||||
kapt("androidx.room:room-compiler:2.4.3")
|
||||
implementation("androidx.room:room-ktx:2.4.3")
|
||||
implementation("androidx.multidex:multidex:2.0.1")
|
||||
implementation "androidx.room:room-runtime:2.5.0"
|
||||
annotationProcessor "androidx.room:room-compiler:2.5.0"
|
||||
kapt "androidx.room:room-compiler:2.5.0"
|
||||
implementation "androidx.room:room-ktx:2.5.0"
|
||||
implementation "androidx.multidex:multidex:2.0.1"
|
||||
api "androidx.navigation:navigation-fragment-ktx:2.5.3"
|
||||
}
|
||||
|
||||
// Allow references to generated code
|
||||
|
||||
@@ -1,50 +1,163 @@
|
||||
package me.zobrist.tichucounter
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.navigation.ui.AppBarConfiguration
|
||||
import androidx.navigation.ui.navigateUp
|
||||
import androidx.navigation.ui.setupActionBarWithNavController
|
||||
import androidx.navigation.ui.setupWithNavController
|
||||
import com.google.android.material.navigation.NavigationView
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.material.icons.outlined.Calculate
|
||||
import androidx.compose.material.icons.outlined.List
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavDestination.Companion.hierarchy
|
||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import me.zobrist.tichucounter.databinding.ActivityDrawerBinding
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.ui.counter.*
|
||||
|
||||
@AndroidEntryPoint
|
||||
class MainActivity : BaseActivity() {
|
||||
|
||||
private lateinit var appBarConfiguration: AppBarConfiguration
|
||||
private lateinit var binding: ActivityDrawerBinding
|
||||
private val counterViewModel: CounterViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityDrawerBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
val drawerLayout: DrawerLayout = binding.drawerLayout
|
||||
val navView: NavigationView = binding.navView
|
||||
val navController = findNavController(R.id.nav_host_fragment_content_drawer)
|
||||
|
||||
setSupportActionBar(binding.appBarDrawer.toolbar)
|
||||
|
||||
// Passing each menu ID as a set of Ids because each
|
||||
// menu should be considered as top level destinations.
|
||||
appBarConfiguration = AppBarConfiguration(
|
||||
setOf(
|
||||
R.id.nav_counter, R.id.nav_history, R.id.nav_settings
|
||||
), drawerLayout
|
||||
)
|
||||
|
||||
setupActionBarWithNavController(navController, appBarConfiguration)
|
||||
navView.setupWithNavController(navController)
|
||||
|
||||
|
||||
setContent {
|
||||
NavigationDrawer()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSupportNavigateUp(): Boolean {
|
||||
val navController = findNavController(R.id.nav_host_fragment_content_drawer)
|
||||
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MyScaffoldLayout(
|
||||
drawerState: DrawerState,
|
||||
scope: CoroutineScope,
|
||||
navController: NavHostController
|
||||
) {
|
||||
|
||||
MaterialTheme() {
|
||||
Scaffold(
|
||||
topBar = { TopBar(drawerState, scope) }) {
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = "counter",
|
||||
modifier = Modifier.padding(it)
|
||||
) {
|
||||
composable("counter") {
|
||||
Counter(counterViewModel)
|
||||
}
|
||||
composable("history") {
|
||||
Column() {
|
||||
Text("History")
|
||||
}
|
||||
}
|
||||
composable("settings") {
|
||||
Column() {
|
||||
Text("Settings")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TopBar(drawerState: DrawerState, scope: CoroutineScope) {
|
||||
CenterAlignedTopAppBar(
|
||||
title = {
|
||||
Text(
|
||||
"Centered TopAppBar",
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { scope.launch { drawerState.open() } }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Menu,
|
||||
contentDescription = "Localized description"
|
||||
)
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
IconButton(onClick = { /* doSomething() */ }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Favorite,
|
||||
contentDescription = "Localized description"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun NavigationDrawer() {
|
||||
val drawerState = rememberDrawerState(DrawerValue.Closed)
|
||||
val scope = rememberCoroutineScope()
|
||||
val navController = rememberNavController()
|
||||
|
||||
val items = listOf(Screen.Calculator, Screen.History, Screen.Settings)
|
||||
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentDestination = navBackStackEntry?.destination
|
||||
|
||||
ModalNavigationDrawer(
|
||||
drawerState = drawerState,
|
||||
drawerContent = {
|
||||
ModalDrawerSheet {
|
||||
Spacer(Modifier.height(12.dp))
|
||||
items.forEach { screen ->
|
||||
NavigationDrawerItem(
|
||||
icon = { Icon(screen.icon, contentDescription = null) },
|
||||
label = { Text(stringResource(screen.resourceId)) },
|
||||
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
|
||||
onClick = {
|
||||
scope.launch { drawerState.close() }
|
||||
navController.navigate(screen.route) {
|
||||
// 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
|
||||
popUpTo(navController.graph.findStartDestination().id) {
|
||||
saveState = true
|
||||
}
|
||||
// Avoid multiple copies of the same destination when
|
||||
// reselecting the same item
|
||||
launchSingleTop = true
|
||||
// Restore state when reselecting a previously selected item
|
||||
restoreState = true
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
content = { MyScaffoldLayout(drawerState, scope, navController) }
|
||||
)
|
||||
}
|
||||
|
||||
sealed class Screen(val route: String, val icon: ImageVector, @StringRes val resourceId: Int) {
|
||||
object Calculator : Screen("counter", Icons.Outlined.Calculate, R.string.menu_counter)
|
||||
object History : Screen("history", Icons.Outlined.List, R.string.menu_history)
|
||||
object Settings : Screen("settings", Icons.Outlined.Settings, R.string.menu_settings)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.view.MenuHost
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.R
|
||||
import me.zobrist.tichucounter.databinding.FragmentCounterBinding
|
||||
import me.zobrist.tichucounter.repository.GameRepository
|
||||
import me.zobrist.tichucounter.ui.FragmentBase
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class CounterFragment : FragmentBase<FragmentCounterBinding>(), MenuProvider {
|
||||
|
||||
@Inject
|
||||
lateinit var gameRepository: GameRepository
|
||||
|
||||
override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentCounterBinding
|
||||
get() = FragmentCounterBinding::inflate
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val menuHost: MenuHost = requireActivity()
|
||||
menuHost.addMenuProvider(
|
||||
this, viewLifecycleOwner, Lifecycle.State.RESUMED
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.menu_counter, menu)
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
return when (menuItem.itemId) {
|
||||
R.id.action_new -> {
|
||||
val builder = context?.let { AlertDialog.Builder(it) }
|
||||
if (builder != null) {
|
||||
builder.setMessage(getString(R.string.confirmNew)).setTitle(R.string.clear)
|
||||
.setCancelable(false)
|
||||
.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
gameRepository.newGame()
|
||||
}
|
||||
}.setNegativeButton(getString(R.string.no)) { dialog, _ ->
|
||||
dialog.cancel()
|
||||
}
|
||||
builder.create().show()
|
||||
}
|
||||
true
|
||||
}
|
||||
R.id.action_undo -> {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
gameRepository.revertLastRound()
|
||||
}
|
||||
true
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
|
||||
@Composable
|
||||
fun Counter(
|
||||
viewModel: CounterViewModel
|
||||
) {
|
||||
Column() {
|
||||
TeamNamesView(
|
||||
viewModel.teamNameA,
|
||||
viewModel.teamNameB,
|
||||
{ viewModel.updateNameA(it) },
|
||||
{ viewModel.updateNameB(it) })
|
||||
|
||||
TeamScoresView(
|
||||
viewModel.totalScoreA,
|
||||
viewModel.totalScoreB
|
||||
)
|
||||
|
||||
RoundListView(
|
||||
viewModel.roundScoreList,
|
||||
Modifier.weight(1f)
|
||||
)
|
||||
|
||||
KeyboardView(
|
||||
viewModel.currentScoreA,
|
||||
viewModel.currentScoreB,
|
||||
viewModel.requestFocusA,
|
||||
viewModel.enableSubmit,
|
||||
{ viewModel.updateFocusStateA(it) },
|
||||
{ viewModel.updateFocusStateB(it) },
|
||||
{ viewModel.digitClicked(it) },
|
||||
{ viewModel.addSub100Clicked(it) },
|
||||
{ viewModel.deleteClicked() },
|
||||
{ viewModel.negateClicked() },
|
||||
{ viewModel.submitClicked() }
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -8,17 +8,39 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.GameDao
|
||||
import me.zobrist.tichucounter.data.Round
|
||||
import me.zobrist.tichucounter.data.RoundDao
|
||||
import me.zobrist.tichucounter.domain.Tichu
|
||||
import me.zobrist.tichucounter.repository.GameRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class KeyboardViewModel @Inject constructor(private val gameRepository: GameRepository) :
|
||||
class CounterViewModel @Inject constructor(
|
||||
private val gameRepository: GameRepository,
|
||||
private val roundDao: RoundDao,
|
||||
private val gameDao: GameDao
|
||||
) :
|
||||
ViewModel() {
|
||||
var scoreA by mutableStateOf("")
|
||||
|
||||
var roundScoreList by mutableStateOf(emptyList<Round>())
|
||||
|
||||
var totalScoreA by mutableStateOf(0)
|
||||
private set
|
||||
|
||||
var scoreB by mutableStateOf("")
|
||||
var totalScoreB by mutableStateOf(0)
|
||||
private set
|
||||
|
||||
var teamNameA by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var teamNameB by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var currentScoreA by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var currentScoreB by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var enableSubmit by mutableStateOf(false)
|
||||
@@ -34,89 +56,58 @@ class KeyboardViewModel @Inject constructor(private val gameRepository: GameRepo
|
||||
private var activeValue: String
|
||||
get() {
|
||||
return if (isBFocused) {
|
||||
scoreB
|
||||
currentScoreB
|
||||
} else {
|
||||
scoreA
|
||||
currentScoreA
|
||||
}
|
||||
}
|
||||
set(value) {
|
||||
if (isBFocused) {
|
||||
scoreB = value
|
||||
currentScoreB = value
|
||||
} else {
|
||||
scoreA = value
|
||||
currentScoreA = value
|
||||
}
|
||||
}
|
||||
|
||||
private var inactiveValue: String
|
||||
get() {
|
||||
return if (isAFocused) {
|
||||
scoreB
|
||||
currentScoreB
|
||||
} else {
|
||||
scoreA
|
||||
currentScoreA
|
||||
}
|
||||
}
|
||||
set(value) {
|
||||
if (isAFocused) {
|
||||
scoreB = value
|
||||
currentScoreB = value
|
||||
} else {
|
||||
scoreA = value
|
||||
currentScoreA = value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun submitScore() {
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
gameRepository.addRoundToActiveGame(scoreA.toInt(), scoreB.toInt())
|
||||
roundDao.getForActiveGame().collect {
|
||||
roundScoreList = it
|
||||
}
|
||||
scoreA = ""
|
||||
scoreB = ""
|
||||
enableSubmit = false
|
||||
}
|
||||
|
||||
fun appendToFocusedScore(toAppend: String) {
|
||||
giveFocusToAIfNone()
|
||||
|
||||
activeValue += toAppend
|
||||
updateOtherScore()
|
||||
updateSubmitButton()
|
||||
viewModelScope.launch {
|
||||
gameDao.getActive().collect {
|
||||
if (it != null) {
|
||||
teamNameA = it.nameA
|
||||
teamNameB = it.nameB
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun negateActiveInput() {
|
||||
giveFocusToAIfNone()
|
||||
|
||||
activeValue = if (activeValue.contains("-")) {
|
||||
activeValue.replace("-", "")
|
||||
} else {
|
||||
"-$activeValue"
|
||||
viewModelScope.launch {
|
||||
roundDao.getRoundSumForActiveGame().collect { score ->
|
||||
totalScoreA = score.scoreA
|
||||
totalScoreB = score.scoreB
|
||||
}
|
||||
updateOtherScore()
|
||||
updateSubmitButton()
|
||||
}
|
||||
|
||||
fun addToActiveInput(toAdd: Int) {
|
||||
giveFocusToAIfNone()
|
||||
|
||||
activeValue = try {
|
||||
val temp = activeValue.toInt() + toAdd
|
||||
temp.toString()
|
||||
} catch (e: Exception) {
|
||||
toAdd.toString()
|
||||
}
|
||||
if(inactiveValue == "")
|
||||
{
|
||||
updateOtherScore()
|
||||
}
|
||||
|
||||
updateSubmitButton()
|
||||
}
|
||||
|
||||
fun removeLastCharFromActive() {
|
||||
if (activeValue != "") {
|
||||
activeValue = activeValue.dropLast(1)
|
||||
}
|
||||
updateOtherScore()
|
||||
}
|
||||
|
||||
|
||||
private fun giveFocusToAIfNone() {
|
||||
if (!isAFocused && !isBFocused) {
|
||||
@@ -142,7 +133,7 @@ class KeyboardViewModel @Inject constructor(private val gameRepository: GameRepo
|
||||
private fun isValidTichuRound(): Boolean {
|
||||
return try {
|
||||
val tichu = Tichu()
|
||||
tichu.isValidRound(scoreA.toInt(), scoreB.toInt())
|
||||
tichu.isValidRound(currentScoreA.toInt(), currentScoreB.toInt())
|
||||
} catch (_: java.lang.NumberFormatException) {
|
||||
false
|
||||
}
|
||||
@@ -151,4 +142,80 @@ class KeyboardViewModel @Inject constructor(private val gameRepository: GameRepo
|
||||
private fun updateSubmitButton() {
|
||||
enableSubmit = isValidTichuRound()
|
||||
}
|
||||
|
||||
fun submitClicked() {
|
||||
viewModelScope.launch {
|
||||
gameRepository.addRoundToActiveGame(currentScoreA.toInt(), currentScoreB.toInt())
|
||||
}
|
||||
currentScoreA = ""
|
||||
currentScoreB = ""
|
||||
enableSubmit = false
|
||||
}
|
||||
|
||||
fun digitClicked(digit: String) {
|
||||
giveFocusToAIfNone()
|
||||
|
||||
activeValue += digit
|
||||
updateOtherScore()
|
||||
updateSubmitButton()
|
||||
}
|
||||
|
||||
fun negateClicked() {
|
||||
giveFocusToAIfNone()
|
||||
|
||||
activeValue = if (activeValue.contains("-")) {
|
||||
activeValue.replace("-", "")
|
||||
} else {
|
||||
"-$activeValue"
|
||||
}
|
||||
updateOtherScore()
|
||||
updateSubmitButton()
|
||||
}
|
||||
|
||||
fun addSub100Clicked(toAdd: Int) {
|
||||
giveFocusToAIfNone()
|
||||
|
||||
activeValue = try {
|
||||
val temp = activeValue.toInt() + toAdd
|
||||
temp.toString()
|
||||
} catch (e: Exception) {
|
||||
toAdd.toString()
|
||||
}
|
||||
if (inactiveValue == "") {
|
||||
updateOtherScore()
|
||||
}
|
||||
|
||||
updateSubmitButton()
|
||||
}
|
||||
|
||||
fun deleteClicked() {
|
||||
if (activeValue != "") {
|
||||
activeValue = activeValue.dropLast(1)
|
||||
}
|
||||
updateOtherScore()
|
||||
}
|
||||
|
||||
fun updateNameA(value: String) {
|
||||
viewModelScope.launch {
|
||||
val game = gameRepository.activeGame
|
||||
game.nameA = value
|
||||
gameRepository.updateGame(game)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateNameB(value: String) {
|
||||
viewModelScope.launch {
|
||||
val game = gameRepository.activeGame
|
||||
game.nameB = value
|
||||
gameRepository.updateGame(game)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFocusStateA(state: Boolean) {
|
||||
isAFocused = state
|
||||
}
|
||||
|
||||
fun updateFocusStateB(state: Boolean) {
|
||||
isBFocused = state
|
||||
}
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Backspace
|
||||
import androidx.compose.material.icons.outlined.Check
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.platform.*
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import me.zobrist.tichucounter.R
|
||||
|
||||
@AndroidEntryPoint
|
||||
class Keyboard : Fragment() {
|
||||
|
||||
|
||||
private val viewModel: KeyboardViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
|
||||
return ComposeView(requireContext()).apply {
|
||||
// Dispose of the Composition when the view's LifecycleOwner
|
||||
// is destroyed
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
KeyboardView(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun KeyboardView(viewModel: KeyboardViewModel) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
Column {
|
||||
Row {
|
||||
TextField(
|
||||
value = viewModel.scoreA,
|
||||
onValueChange = { },
|
||||
placeholder = { Text("0") },
|
||||
singleLine = true,
|
||||
readOnly = true,
|
||||
modifier = Modifier
|
||||
.onFocusChanged {
|
||||
keyboardController?.hide()
|
||||
viewModel.isAFocused = it.isFocused
|
||||
}
|
||||
.focusRequester(viewModel.requestFocusA)
|
||||
.weight(1f),
|
||||
)
|
||||
TextField(
|
||||
value = viewModel.scoreB,
|
||||
onValueChange = { },
|
||||
placeholder = { Text("0") },
|
||||
singleLine = true,
|
||||
readOnly = true,
|
||||
modifier = Modifier
|
||||
.onFocusChanged {
|
||||
keyboardController?.hide()
|
||||
viewModel.isBFocused = it.isFocused
|
||||
}
|
||||
.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
Row {
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("1") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("1") }
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("2") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("2") }
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("3") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("3") }
|
||||
TextButton(
|
||||
onClick = { viewModel.addToActiveInput(100) },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("+100") }
|
||||
|
||||
}
|
||||
Row {
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("4") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("4") }
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("5") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("5") }
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("6") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("6") }
|
||||
TextButton(
|
||||
onClick = { viewModel.addToActiveInput(-100) },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("-100") }
|
||||
}
|
||||
Row {
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("7") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("7") }
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("8") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("8") }
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("9") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("9") }
|
||||
IconButton(
|
||||
onClick = { viewModel.removeLastCharFromActive() },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Icon(Icons.Outlined.Backspace, stringResource(R.string.submit)) }
|
||||
}
|
||||
Row {
|
||||
TextButton(
|
||||
onClick = { viewModel.negateActiveInput() },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("+/-") }
|
||||
TextButton(
|
||||
onClick = { viewModel.appendToFocusedScore("0") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("0") }
|
||||
Spacer(modifier = Modifier.weight(1F))
|
||||
IconButton(
|
||||
onClick = { viewModel.submitScore() },
|
||||
modifier = Modifier.weight(1F),
|
||||
enabled = viewModel.enableSubmit
|
||||
) { Icon(Icons.Outlined.Check, stringResource(R.string.submit)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Backspace
|
||||
import androidx.compose.material.icons.outlined.Check
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.ExperimentalComposeUiApi
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import me.zobrist.tichucounter.R
|
||||
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalComposeUiApi::class)
|
||||
@Composable
|
||||
fun KeyboardView(
|
||||
scoreA: String,
|
||||
scoreB: String,
|
||||
requestFocusA: FocusRequester,
|
||||
enableSubmit: Boolean,
|
||||
updateFocusStateA: (Boolean) -> Unit,
|
||||
updateFocusStateB: (Boolean) -> Unit,
|
||||
digitClicked: (String) -> Unit,
|
||||
addSub100Clicked: (Int) -> Unit,
|
||||
deleteClicked: () -> Unit,
|
||||
negateClicked: () -> Unit,
|
||||
submitClicked: () -> Unit
|
||||
) {
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
Column {
|
||||
Row {
|
||||
TextField(
|
||||
value = scoreA,
|
||||
onValueChange = { },
|
||||
placeholder = { Text("0") },
|
||||
singleLine = true,
|
||||
readOnly = true,
|
||||
modifier = Modifier
|
||||
.onFocusChanged {
|
||||
keyboardController?.hide()
|
||||
updateFocusStateA(it.isFocused)
|
||||
}
|
||||
.focusRequester(requestFocusA)
|
||||
.weight(1f),
|
||||
)
|
||||
TextField(
|
||||
value = scoreB,
|
||||
onValueChange = { },
|
||||
placeholder = { Text("0") },
|
||||
singleLine = true,
|
||||
readOnly = true,
|
||||
modifier = Modifier
|
||||
.onFocusChanged {
|
||||
keyboardController?.hide()
|
||||
updateFocusStateB(it.isFocused)
|
||||
}
|
||||
.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
Row {
|
||||
TextButton(
|
||||
onClick = { digitClicked("1") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("1") }
|
||||
TextButton(
|
||||
onClick = { digitClicked("2") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("2") }
|
||||
TextButton(
|
||||
onClick = { digitClicked("3") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("3") }
|
||||
TextButton(
|
||||
onClick = { addSub100Clicked(100) },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("+100") }
|
||||
|
||||
}
|
||||
Row {
|
||||
TextButton(
|
||||
onClick = { digitClicked("4") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("4") }
|
||||
TextButton(
|
||||
onClick = { digitClicked("5") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("5") }
|
||||
TextButton(
|
||||
onClick = { digitClicked("6") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("6") }
|
||||
TextButton(
|
||||
onClick = { addSub100Clicked(-100) },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("-100") }
|
||||
}
|
||||
Row {
|
||||
TextButton(
|
||||
onClick = { digitClicked("7") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("7") }
|
||||
TextButton(
|
||||
onClick = { digitClicked("8") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("8") }
|
||||
TextButton(
|
||||
onClick = { digitClicked("9") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("9") }
|
||||
IconButton(
|
||||
onClick = { deleteClicked() },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Icon(Icons.Outlined.Backspace, stringResource(R.string.submit)) }
|
||||
}
|
||||
Row {
|
||||
TextButton(
|
||||
onClick = { negateClicked() },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("+/-") }
|
||||
TextButton(
|
||||
onClick = { digitClicked("0") },
|
||||
modifier = Modifier.weight(1F)
|
||||
) { Text("0") }
|
||||
Spacer(modifier = Modifier.weight(1F))
|
||||
IconButton(
|
||||
onClick = { submitClicked() },
|
||||
modifier = Modifier.weight(1F),
|
||||
enabled = enableSubmit
|
||||
) { Icon(Icons.Outlined.Check, stringResource(R.string.submit)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.Round
|
||||
|
||||
@AndroidEntryPoint
|
||||
class RoundList : Fragment() {
|
||||
|
||||
private val viewModel: RoundListViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
|
||||
return ComposeView(requireContext()).apply {
|
||||
// Dispose of the Composition when the view's LifecycleOwner
|
||||
// is destroyed
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
RoundListView(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RoundListView(viewModel: RoundListViewModel) {
|
||||
RoundListView(viewModel.scores)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RoundListView(rounds: List<Round>) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LazyColumn(state = lazyListState) {
|
||||
itemsIndexed(rounds) { index, item ->
|
||||
RoundListItem(item, index, lazyListState)
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
lazyListState.animateScrollToItem(viewModel.scores.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RoundListItem(round: Round, index: Int, lazyListState: LazyListState) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = round.scoreA.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(5f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Text(
|
||||
text = index.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Text(
|
||||
text = round.scoreB.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(5f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun RoundListViewPreview() {
|
||||
val rounds = listOf<Round>(
|
||||
Round(1, 10, 90),
|
||||
Round(1, 5, 95),
|
||||
Round(1, 100, 0),
|
||||
Round(1, 125, -25),
|
||||
Round(1, 50, 50)
|
||||
)
|
||||
RoundListView(rounds)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.Round
|
||||
|
||||
@Composable
|
||||
fun RoundListView(rounds: List<Round>, modifier: Modifier) {
|
||||
val lazyListState = rememberLazyListState()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
LazyColumn(state = lazyListState, modifier = modifier) {
|
||||
itemsIndexed(rounds) { index, item ->
|
||||
RoundListItem(item, index, lazyListState)
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
lazyListState.animateScrollToItem(rounds.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RoundListItem(round: Round, index: Int, lazyListState: LazyListState) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(all = 4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = round.scoreA.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(5f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Text(
|
||||
text = index.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Text(
|
||||
text = round.scoreB.toString(),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier.weight(5f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun RoundListViewPreview() {
|
||||
val rounds = listOf<Round>(
|
||||
Round(1, 10, 90),
|
||||
Round(1, 5, 95),
|
||||
Round(1, 100, 0),
|
||||
Round(1, 125, -25),
|
||||
Round(1, 50, 50)
|
||||
)
|
||||
RoundListView(rounds, Modifier)
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.Round
|
||||
import me.zobrist.tichucounter.data.RoundDao
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class RoundListViewModel @Inject constructor(private val roundDao: RoundDao) : ViewModel(){
|
||||
|
||||
var scores by mutableStateOf(emptyList<Round>())
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
roundDao.getForActiveGame().collect {
|
||||
scores = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TeamNames : Fragment() {
|
||||
|
||||
private val viewModel: TeamNamesViewModel by viewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
|
||||
return ComposeView(requireContext()).apply {
|
||||
// Dispose of the Composition when the view's LifecycleOwner
|
||||
// is destroyed
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
TeamNamesView(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TeamNamesView(viewModel: TeamNamesViewModel) {
|
||||
|
||||
TeamNamesView(
|
||||
viewModel.nameA,
|
||||
viewModel.nameB,
|
||||
{ viewModel.updateNameA(it)},
|
||||
{ viewModel.updateNameB(it)})
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun TeamNamesView(
|
||||
nameA: String,
|
||||
nameB: String,
|
||||
updateA: (String) -> Unit,
|
||||
updateB: (String) -> Unit
|
||||
){
|
||||
Row() {
|
||||
TextField(
|
||||
value = nameA,
|
||||
onValueChange = { updateA(it) },
|
||||
singleLine = true,
|
||||
modifier = Modifier.weight(1f))
|
||||
|
||||
TextField(
|
||||
value = nameB,
|
||||
onValueChange = { updateB(it) },
|
||||
singleLine = true,
|
||||
modifier = Modifier.weight(1f))
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TeamNamesView() {
|
||||
TeamNamesView("TeamA", "TeamB", {}, {})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun TeamNamesView(
|
||||
nameA: String,
|
||||
nameB: String,
|
||||
updateA: (String) -> Unit,
|
||||
updateB: (String) -> Unit
|
||||
) {
|
||||
Row() {
|
||||
TextField(
|
||||
value = nameA,
|
||||
onValueChange = { updateA(it) },
|
||||
singleLine = true,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
TextField(
|
||||
value = nameB,
|
||||
onValueChange = { updateB(it) },
|
||||
singleLine = true,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TeamNamesViewPreview() {
|
||||
TeamNamesView("TeamA", "TeamB", {}, {})
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.Game
|
||||
import me.zobrist.tichucounter.data.GameDao
|
||||
import me.zobrist.tichucounter.repository.GameRepository
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class TeamNamesViewModel @Inject constructor(
|
||||
private val gameDao: GameDao,
|
||||
private val gameRepository: GameRepository
|
||||
) :
|
||||
ViewModel() {
|
||||
|
||||
var nameA by mutableStateOf("")
|
||||
private set
|
||||
|
||||
var nameB by mutableStateOf("")
|
||||
private set
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
gameDao.getActive().collect {
|
||||
if (it != null) {
|
||||
nameA = it.nameA
|
||||
nameB = it.nameB
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateNameA(value: String) {
|
||||
viewModelScope.launch {
|
||||
val game = gameRepository.activeGame
|
||||
game.nameA = value
|
||||
gameRepository.updateGame(game)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateNameB(value: String) {
|
||||
viewModelScope.launch {
|
||||
val game = gameRepository.activeGame
|
||||
game.nameB = value
|
||||
gameRepository.updateGame(game)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import me.zobrist.tichucounter.ui.FragmentBase
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TeamScores : Fragment() {
|
||||
|
||||
private val viewModel: TeamScoresViewModel by activityViewModels()
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
|
||||
return ComposeView(requireContext()).apply {
|
||||
// Dispose of the Composition when the view's LifecycleOwner
|
||||
// is destroyed
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
MaterialTheme {
|
||||
TeamScoresView(viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TeamScoresView(viewModel: TeamScoresViewModel) {
|
||||
|
||||
TeamScoresView(viewModel.scoreA, viewModel.scoreB)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TeamScoresView(scoreA: Int, scoreB: Int,){
|
||||
Row() {
|
||||
Text(
|
||||
text = scoreA.toString(),
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center)
|
||||
|
||||
Text(
|
||||
text = scoreB.toString(),
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TeamNamesView() {
|
||||
TeamScoresView(10, 90)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
|
||||
|
||||
@Composable
|
||||
fun TeamScoresView(scoreA: Int, scoreB: Int) {
|
||||
Row() {
|
||||
Text(
|
||||
text = scoreA.toString(),
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
Text(
|
||||
text = scoreB.toString(),
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun TeamNamesView() {
|
||||
TeamScoresView(10, 90)
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
package me.zobrist.tichucounter.ui.counter
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import me.zobrist.tichucounter.data.RoundDao
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class TeamScoresViewModel @Inject constructor(private val roundDao: RoundDao) : ViewModel() {
|
||||
|
||||
var scoreA by mutableStateOf(0)
|
||||
private set
|
||||
|
||||
var scoreB by mutableStateOf(0)
|
||||
private set
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
|
||||
roundDao.getRoundSumForActiveGame().collect { score ->
|
||||
scoreA = score.scoreA
|
||||
scoreB = score.scoreB
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="visible"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/left"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:weightSum="2"
|
||||
app:layout_constraintEnd_toStartOf="@+id/right"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/teamNames"
|
||||
android:name="me.zobrist.tichucounter.ui.counter.TeamNames"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:layout="@layout/fragment_team_names" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/teamScores"
|
||||
android:name="me.zobrist.tichucounter.ui.counter.TeamScores"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/teamNames"
|
||||
tools:layout="@layout/fragment_team_scores" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/scrollHistory"
|
||||
android:name="me.zobrist.tichucounter.ui.counter.RoundList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="16dp"
|
||||
android:clickable="false"
|
||||
tools:layout="@layout/fragment_round_list" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/right"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="bottom"
|
||||
android:orientation="vertical"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/left">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/keyboard"
|
||||
android:name="me.zobrist.tichucounter.ui.counter.Keyboard"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:layout="@layout/fragment_keyboard" />
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/drawer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:openDrawer="start">
|
||||
|
||||
<include
|
||||
android:id="@+id/app_bar_drawer"
|
||||
layout="@layout/app_bar_drawer"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/nav_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_gravity="start"
|
||||
android:fitsSystemWindows="true"
|
||||
app:headerLayout="@layout/nav_header_drawer"
|
||||
app:menu="@menu/activity_main_drawer" />
|
||||
</androidx.drawerlayout.widget.DrawerLayout>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".MainActivity">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:theme="@style/AppTheme.AppBarOverlay">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="?attr/colorPrimary"
|
||||
app:popupTheme="@style/AppTheme.PopupOverlay" />
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<include layout="@layout/content_drawer" />
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
@@ -1,20 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||
tools:showIn="@layout/app_bar_drawer">
|
||||
|
||||
<fragment
|
||||
android:id="@+id/nav_host_fragment_content_drawer"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/mobile_navigation" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,74 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/teamNames"
|
||||
android:name="me.zobrist.tichucounter.ui.counter.TeamNames"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:layout="@layout/fragment_team_names" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/teamScores"
|
||||
android:name="me.zobrist.tichucounter.ui.counter.TeamScores"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/teamNames"
|
||||
tools:layout="@layout/fragment_team_scores" />
|
||||
|
||||
|
||||
<View
|
||||
android:id="@+id/dividerTop"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_weight="1"
|
||||
android:background="?android:attr/listDivider"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/teamScores"
|
||||
tools:layout_editor_absoluteY="50dp" />
|
||||
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/scrollHistory"
|
||||
android:name="me.zobrist.tichucounter.ui.counter.RoundList"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="16dp"
|
||||
app:layout_constraintBottom_toTopOf="@id/dividerBottom"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/dividerTop"
|
||||
tools:layout="@layout/fragment_round_list" />
|
||||
|
||||
<View
|
||||
android:id="@+id/dividerBottom"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?android:attr/listDivider"
|
||||
app:layout_constraintBottom_toTopOf="@id/keyboard"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<androidx.fragment.app.FragmentContainerView
|
||||
android:id="@+id/keyboard"
|
||||
android:name="me.zobrist.tichucounter.ui.counter.Keyboard"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:layout="@layout/fragment_keyboard" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -1,35 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/nav_header_height"
|
||||
android:background="@drawable/side_nav_bar"
|
||||
android:gravity="bottom"
|
||||
android:orientation="vertical"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:theme="@style/ThemeOverlay.AppCompat.Dark">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:contentDescription="@string/nav_header_desc"
|
||||
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
||||
app:srcCompat="@mipmap/ic_launcher_round" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="@dimen/nav_header_vertical_spacing"
|
||||
android:text="@string/app_name"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Body1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/nav_header_subtitle" />
|
||||
</LinearLayout>
|
||||
Reference in New Issue
Block a user