diff --git a/app/build.gradle b/app/build.gradle index e085085..e35b03f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -82,6 +82,7 @@ dependencies { implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' implementation 'androidx.fragment:fragment-ktx:1.5.5' implementation 'androidx.preference:preference-ktx:1.2.0' + implementation 'androidx.recyclerview:recyclerview:1.2.1' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/app/src/main/java/me/zobrist/tichucounter/data/Game.kt b/app/src/main/java/me/zobrist/tichucounter/data/Game.kt index d6fc7b4..1c49317 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/Game.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/Game.kt @@ -6,10 +6,10 @@ import java.util.* @Entity data class Game( - val active: Boolean, - var nameA: String, - var nameB: String, - val created: Date, - var modified: Date, - @PrimaryKey(autoGenerate = true) val uid: Long? = null -) \ No newline at end of file + override var active: Boolean, + override var nameA: String, + override var nameB: String, + override val created: Date, + override var modified: Date, + @PrimaryKey(autoGenerate = true) override val uid: Long? = null +) : IGame, IEntity \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/data/GameAndScore.kt b/app/src/main/java/me/zobrist/tichucounter/data/GameAndScore.kt new file mode 100644 index 0000000..d2d3bd9 --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/data/GameAndScore.kt @@ -0,0 +1,17 @@ +package me.zobrist.tichucounter.data + +import androidx.room.Entity +import java.util.* + +@Entity +data class GameAndScore( + override var active: Boolean, + override var nameA: String, + override var nameB: String, + override val created: Date, + override var modified: Date, + override var gameId: Long, + override var scoreA: Int?, + override var scoreB: Int?, +) : IGame, IRound { +} \ No newline at end of file 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 7f46de7..2701d1c 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/GameDao.kt @@ -7,6 +7,14 @@ import kotlinx.coroutines.flow.Flow @Dao interface GameDao : DaoBase { + @Query("SELECT * FROM game") + fun getAll(): Flow> + + @Query("SELECT *, SUM(round.scoreA) as scoreA, SUM(round.scoreB) as scoreB, SUM(round.scoreB) as scoreB " + + "FROM game " + + "LEFT JOIN round ON round.gameId = game.uid GROUP BY gameId ORDER BY modified DESC") + fun getAllWithPoints(): Flow> + @Query("SELECT * FROM game WHERE uid is :gameId") fun getGameById(gameId: Long): Flow diff --git a/app/src/main/java/me/zobrist/tichucounter/data/IEntity.kt b/app/src/main/java/me/zobrist/tichucounter/data/IEntity.kt new file mode 100644 index 0000000..c01aff7 --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/data/IEntity.kt @@ -0,0 +1,5 @@ +package me.zobrist.tichucounter.data + +interface IEntity { + val uid: Long? +} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/data/IGame.kt b/app/src/main/java/me/zobrist/tichucounter/data/IGame.kt new file mode 100644 index 0000000..3afbdff --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/data/IGame.kt @@ -0,0 +1,11 @@ +package me.zobrist.tichucounter.data + +import java.util.* + +interface IGame { + var active: Boolean + var nameA: String + var nameB: String + val created: Date + var modified: Date +} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/data/IRound.kt b/app/src/main/java/me/zobrist/tichucounter/data/IRound.kt new file mode 100644 index 0000000..e05a33d --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/data/IRound.kt @@ -0,0 +1,7 @@ +package me.zobrist.tichucounter.data + +interface IRound { + var gameId: Long + var scoreA: Int? + var scoreB: Int? +} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/data/Round.kt b/app/src/main/java/me/zobrist/tichucounter/data/Round.kt index 81c9fe5..de27554 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/Round.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/Round.kt @@ -5,8 +5,8 @@ import androidx.room.PrimaryKey @Entity data class Round( - var gameId: Long, - var scoreA: Int?, - var scoreB: Int?, - @PrimaryKey(autoGenerate = true) val uid: Long? = null -) \ No newline at end of file + override var gameId: Long, + override var scoreA: Int?, + override var scoreB: Int?, + @PrimaryKey(autoGenerate = true) override val uid: Long? = null +) : IRound, IEntity \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/data/RoundDao.kt b/app/src/main/java/me/zobrist/tichucounter/data/RoundDao.kt index 600dde1..4c1a69d 100644 --- a/app/src/main/java/me/zobrist/tichucounter/data/RoundDao.kt +++ b/app/src/main/java/me/zobrist/tichucounter/data/RoundDao.kt @@ -9,6 +9,14 @@ interface RoundDao : DaoBase { @Query("SELECT * FROM round WHERE gameId is :gameId") fun getAllForGame(gameId: Long?): List + @Query( + "SELECT gameId, SUM(scoreA) as scoreA, SUM(scoreB) as scoreB " + + "FROM round " + + "LEFT JOIN game ON game.uid = round.gameId " + + "WHERE game.active == 1" + ) + fun getRoundSumForActiveGame() : Flow + @Query( "SELECT gameId, scoreA, scoreB, round.uid " + "FROM round " + diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/HistoryList.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundList.kt similarity index 74% rename from app/src/main/java/me/zobrist/tichucounter/ui/counter/HistoryList.kt rename to app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundList.kt index f8dba9a..9951064 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/HistoryList.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundList.kt @@ -6,16 +6,16 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.activityViewModels import dagger.hilt.android.AndroidEntryPoint -import me.zobrist.tichucounter.databinding.FragmentHistoryListBinding +import me.zobrist.tichucounter.databinding.FragmentRoundListBinding import me.zobrist.tichucounter.ui.FragmentBase @AndroidEntryPoint -class HistoryList : FragmentBase() { +class RoundList : FragmentBase() { - override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentHistoryListBinding - get() = FragmentHistoryListBinding::inflate + override val bindingInflater: (LayoutInflater, ViewGroup?, Boolean) -> FragmentRoundListBinding + get() = FragmentRoundListBinding::inflate - private val viewModel: HistoryListViewModel by activityViewModels() + private val viewModel: RoundListViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/HistoryListViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundListViewModel.kt similarity index 94% rename from app/src/main/java/me/zobrist/tichucounter/ui/counter/HistoryListViewModel.kt rename to app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundListViewModel.kt index 2816c24..df5882b 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/HistoryListViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/RoundListViewModel.kt @@ -12,7 +12,7 @@ import me.zobrist.tichucounter.framework.SingleLiveEvent import javax.inject.Inject @HiltViewModel -class HistoryListViewModel @Inject constructor(private val roundDao: RoundDao) : ViewModel() { +class RoundListViewModel @Inject constructor(private val roundDao: RoundDao) : ViewModel() { private val _historyA: MutableLiveData = MutableLiveData() private val _historyB: MutableLiveData = MutableLiveData() diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/counter/TeamScoresViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/counter/TeamScoresViewModel.kt index dfd10d5..4bd604c 100644 --- a/app/src/main/java/me/zobrist/tichucounter/ui/counter/TeamScoresViewModel.kt +++ b/app/src/main/java/me/zobrist/tichucounter/ui/counter/TeamScoresViewModel.kt @@ -5,6 +5,7 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import me.zobrist.tichucounter.data.Round import me.zobrist.tichucounter.data.RoundDao @@ -18,26 +19,14 @@ class TeamScoresViewModel @Inject constructor(private val roundDao: RoundDao) : init { viewModelScope.launch { - roundDao.getForActiveGame().collect { scores -> - update(scores) + + roundDao.getRoundSumForActiveGame().collect { score -> + _scoreA.value = if(score?.scoreA != null) score.scoreA else 0 + _scoreB.value = if(score?.scoreB != null) score.scoreB else 0 } } } - private fun update(scores: List) { - - var scoreA = 0 - var scoreB = 0 - - scores.forEach { - it.scoreA?.let { a -> scoreA += a } - it.scoreB?.let { b -> scoreB += b } - } - - _scoreA.value = scoreA - _scoreB.value = scoreB - } - val scoreA: LiveData get() { return _scoreA diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/history/HistoryFragment.kt b/app/src/main/java/me/zobrist/tichucounter/ui/history/HistoryFragment.kt new file mode 100644 index 0000000..d9f2eb1 --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/ui/history/HistoryFragment.kt @@ -0,0 +1,68 @@ +package me.zobrist.tichucounter.ui.history + +import android.os.Bundle +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.activityViewModels +import dagger.hilt.android.AndroidEntryPoint +import me.zobrist.tichucounter.R + +/** + * A fragment representing a list of Items. + */ +@AndroidEntryPoint +class HistoryFragment : Fragment() { + + private val viewModel: HistoryFragmentViewModel by activityViewModels() + + private var columnCount = 1 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + arguments?.let { + columnCount = it.getInt(ARG_COLUMN_COUNT) + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + val view = inflater.inflate(R.layout.fragment_history_list, container, false) + + // Set the adapter + if (view is RecyclerView) { + with(view) { + layoutManager = when { + columnCount <= 1 -> LinearLayoutManager(context) + else -> GridLayoutManager(context, columnCount) + } + + viewModel.gameAndHistory.observe(viewLifecycleOwner) { + adapter = MyGameRecyclerViewAdapter(it) + } + } + } + return view + } + + + companion object { + + const val ARG_COLUMN_COUNT = "1" + + @JvmStatic + fun newInstance(columnCount: Int) = + HistoryFragment().apply { + arguments = Bundle().apply { + putInt(ARG_COLUMN_COUNT, columnCount) + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/history/HistoryFragmentViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/history/HistoryFragmentViewModel.kt new file mode 100644 index 0000000..a3d964a --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/ui/history/HistoryFragmentViewModel.kt @@ -0,0 +1,32 @@ +package me.zobrist.tichucounter.ui.history + +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.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import me.zobrist.tichucounter.data.GameAndScore +import me.zobrist.tichucounter.data.GameDao +import me.zobrist.tichucounter.data.RoundDao +import javax.inject.Inject + +@HiltViewModel +class HistoryFragmentViewModel @Inject constructor(private val gameDao: GameDao, private val roundDao: RoundDao) : ViewModel() { + + private val _gameAndHistory: MutableLiveData> = MutableLiveData() + + val gameAndHistory: LiveData> + get() { return _gameAndHistory} + + init { + viewModelScope.launch { + gameDao.getAllWithPoints().collect { games -> + _gameAndHistory.value = games + } + } + + } +} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/history/MyGameRecyclerViewAdapter.kt b/app/src/main/java/me/zobrist/tichucounter/ui/history/MyGameRecyclerViewAdapter.kt new file mode 100644 index 0000000..277b899 --- /dev/null +++ b/app/src/main/java/me/zobrist/tichucounter/ui/history/MyGameRecyclerViewAdapter.kt @@ -0,0 +1,49 @@ +package me.zobrist.tichucounter.ui.history + +import android.provider.Settings.Global.getString +import androidx.recyclerview.widget.RecyclerView +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.core.content.res.TypedArrayUtils +import me.zobrist.tichucounter.R +import me.zobrist.tichucounter.data.GameAndScore +import me.zobrist.tichucounter.databinding.FragmentHistoryBinding +import java.text.DateFormat + +/** + * [RecyclerView.Adapter] that can display a [GameAndScore]. + * TODO: Replace the implementation with code for your data type. + */ +class MyGameRecyclerViewAdapter( + private val values: List +) : RecyclerView.Adapter() { + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + + return ViewHolder( + FragmentHistoryBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val item = values[position] + holder.title.text = "${item.nameA} - ${item.nameB}" + holder.secondary.text = item.scoreA.toString() + ":" + item.scoreB + holder.support.text = DateFormat.getDateInstance().format(item.modified) + } + + override fun getItemCount(): Int = values.size + + inner class ViewHolder(binding: FragmentHistoryBinding) : + RecyclerView.ViewHolder(binding.root) { + val title: TextView = binding.cardTitle + val secondary: TextView = binding.cardSecondary + val support: TextView = binding.cardSupporting + } +} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/slideshow/SlideshowFragment.kt b/app/src/main/java/me/zobrist/tichucounter/ui/slideshow/SlideshowFragment.kt deleted file mode 100644 index 471c95e..0000000 --- a/app/src/main/java/me/zobrist/tichucounter/ui/slideshow/SlideshowFragment.kt +++ /dev/null @@ -1,36 +0,0 @@ -package me.zobrist.tichucounter.ui.slideshow - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import androidx.lifecycle.ViewModelProvider -import me.zobrist.tichucounter.databinding.FragmentSlideshowBinding - -class SlideshowFragment : Fragment() { - - private var _binding: FragmentSlideshowBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { - val slideshowViewModel = - ViewModelProvider(this)[SlideshowViewModel::class.java] - - _binding = FragmentSlideshowBinding.inflate(inflater, container, false) - - return binding.root - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } -} \ No newline at end of file diff --git a/app/src/main/java/me/zobrist/tichucounter/ui/slideshow/SlideshowViewModel.kt b/app/src/main/java/me/zobrist/tichucounter/ui/slideshow/SlideshowViewModel.kt deleted file mode 100644 index 0f89ee3..0000000 --- a/app/src/main/java/me/zobrist/tichucounter/ui/slideshow/SlideshowViewModel.kt +++ /dev/null @@ -1,13 +0,0 @@ -package me.zobrist.tichucounter.ui.slideshow - -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.ViewModel - -class SlideshowViewModel : ViewModel() { - - private val _text = MutableLiveData().apply { - value = "This is slideshow Fragment" - } - val text: LiveData = _text -} \ No newline at end of file diff --git a/app/src/main/res/layout-land/fragment_counter.xml b/app/src/main/res/layout-land/fragment_counter.xml index d376cd8..c435645 100644 --- a/app/src/main/res/layout-land/fragment_counter.xml +++ b/app/src/main/res/layout-land/fragment_counter.xml @@ -39,12 +39,12 @@ + tools:layout="@layout/fragment_round_list" /> + tools:layout="@layout/fragment_round_list" /> + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_history_list.xml b/app/src/main/res/layout/fragment_history_list.xml index 1c28414..e0f42cb 100644 --- a/app/src/main/res/layout/fragment_history_list.xml +++ b/app/src/main/res/layout/fragment_history_list.xml @@ -1,45 +1,13 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file + android:layout_marginLeft="16dp" + android:layout_marginRight="16dp" + app:layoutManager="LinearLayoutManager" + tools:context=".ui.history.HistoryFragment" + tools:listitem="@layout/fragment_history" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_keyboard.xml b/app/src/main/res/layout/fragment_keyboard.xml index ad6dd19..f75967f 100644 --- a/app/src/main/res/layout/fragment_keyboard.xml +++ b/app/src/main/res/layout/fragment_keyboard.xml @@ -47,14 +47,13 @@