From 493d16fc7cf6248efc6d88b67e766d9baab67c90 Mon Sep 17 00:00:00 2001 From: SudKul Date: Sun, 20 Sep 2020 14:52:39 +0530 Subject: [PATCH 01/10] Update Gradle scripts --- app/build.gradle | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index 6166c369e..07d343f50 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -39,6 +39,15 @@ android { buildFeatures { dataBinding true } + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_1_8 + } } dependencies { From a362e8b22f30b6794d155e4feafd219e91e83063 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Mon, 21 Dec 2020 15:58:50 -0300 Subject: [PATCH 02/10] project: Update project dependencies --- app/build.gradle | 26 ++++++++++++------------ build.gradle | 15 ++++++++------ gradle/wrapper/gradle-wrapper.properties | 4 ++-- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 07d343f50..d63d8e5c3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -17,7 +17,6 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply plugin: 'kotlin-android-extensions' apply plugin: "androidx.navigation.safeargs.kotlin" android { @@ -51,22 +50,23 @@ android { } dependencies { - implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation 'androidx.appcompat:appcompat:1.2.0' - implementation 'androidx.constraintlayout:constraintlayout:1.1.3' + implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.legacy:legacy-support-v4:1.0.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.1.1' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' + testImplementation 'junit:junit:4.13.1' + androidTestImplementation 'androidx.test:runner:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' - // KTX - implementation 'androidx.core:core-ktx:1.3.1' + // https://developer.android.com/jetpack/androidx/releases/core + implementation "androidx.core:core-ktx:$core_version" - // Navigation - implementation "android.arch.navigation:navigation-fragment-ktx:1.0.0-rc02" - implementation "android.arch.navigation:navigation-ui-ktx:1.0.0-rc02" + // https://developer.android.com/jetpack/androidx/releases/navigation + implementation "androidx.navigation:navigation-fragment-ktx:$rootProject.nav_version" + implementation "androidx.navigation:navigation-ui-ktx:$rootProject.nav_version" - // Lifecycles - implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' + // https://developer.android.com/jetpack/androidx/releases/lifecycle + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" + implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version" } diff --git a/build.gradle b/build.gradle index 23285629f..3cae14440 100644 --- a/build.gradle +++ b/build.gradle @@ -17,18 +17,16 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.3.72' + ext.kotlin_version = "1.4.21" + ext.nav_version = "2.3.2" repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:4.0.1' + classpath 'com.android.tools.build:gradle:4.1.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0-rc02" - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files + classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } } @@ -41,4 +39,9 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir +} + +ext { + lifecycle_version = "2.2.0" + core_version = "1.3.2" } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 5e277db02..959ff6be2 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Thurs Aug 11 17:10:25 PDT 2020 +#Mon Dec 21 14:43:50 BRT 2020 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip From 61c3e2b9efab79becc2f452e8a472f1a6633a79e Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Mon, 21 Dec 2020 15:59:21 -0300 Subject: [PATCH 03/10] refactor: Move business logic related code to ViewModel --- .../guesstheword/screens/game/GameFragment.kt | 104 ++++-------------- .../screens/game/GameViewModel.kt | 74 +++++++++++++ 2 files changed, 95 insertions(+), 83 deletions(-) create mode 100644 app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt diff --git a/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt b/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt index 0dbe12118..e35b27e07 100644 --- a/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt +++ b/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt @@ -20,10 +20,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.navigation.fragment.NavHostFragment.findNavController -import com.example.android.guesstheword.R import com.example.android.guesstheword.databinding.GameFragmentBinding /** @@ -31,111 +30,50 @@ import com.example.android.guesstheword.databinding.GameFragmentBinding */ class GameFragment : Fragment() { - // The current word - private var word = "" - - // The current score - private var score = 0 - - // The list of words - the front of the list is the next word to guess - private lateinit var wordList: MutableList + private val viewModel: GameViewModel by viewModels() private lateinit var binding: GameFragmentBinding - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle?): View? { - - // Inflate view and obtain an instance of the binding class - binding = DataBindingUtil.inflate( + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = GameFragmentBinding.inflate( inflater, - R.layout.game_fragment, container, false ) - resetList() - nextWord() - - binding.correctButton.setOnClickListener { onCorrect() } - binding.skipButton.setOnClickListener { onSkip() } + binding.correctButton.setOnClickListener { + viewModel.onCorrect() + updateWordText() + updateScoreText() + } + binding.skipButton.setOnClickListener { + viewModel.onSkip() + updateWordText() + updateScoreText() + } updateScoreText() updateWordText() return binding.root - - } - - /** - * Resets the list of words and randomizes the order - */ - private fun resetList() { - wordList = mutableListOf( - "queen", - "hospital", - "basketball", - "cat", - "change", - "snail", - "soup", - "calendar", - "sad", - "desk", - "guitar", - "home", - "railway", - "zebra", - "jelly", - "car", - "crow", - "trade", - "bag", - "roll", - "bubble" - ) - wordList.shuffle() } /** * Called when the game is finished */ private fun gameFinished() { - val action = GameFragmentDirections.actionGameToScore(score) + val action = GameFragmentDirections.actionGameToScore(viewModel.score) findNavController(this).navigate(action) } - /** - * Moves to the next word in the list - */ - private fun nextWord() { - //Select and remove a word from the list - if (wordList.isEmpty()) { - gameFinished() - } else { - word = wordList.removeAt(0) - } - updateWordText() - updateScoreText() - } - - /** Methods for buttons presses **/ - - private fun onSkip() { - score-- - nextWord() - } - - private fun onCorrect() { - score++ - nextWord() - } - /** Methods for updating the UI **/ - private fun updateWordText() { - binding.wordText.text = word - + binding.wordText.text = viewModel.word } private fun updateScoreText() { - binding.scoreText.text = score.toString() + binding.scoreText.text = viewModel.score.toString() } } diff --git a/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt b/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt new file mode 100644 index 000000000..2c78d0baa --- /dev/null +++ b/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt @@ -0,0 +1,74 @@ +package com.example.android.guesstheword.screens.game + +import androidx.lifecycle.ViewModel + +class GameViewModel : ViewModel() { + + // The current word + var word = "" + + // The current score + var score = 0 + + // The list of words - the front of the list is the next word to guess + private lateinit var wordList: MutableList + + init { + resetList() + nextWord() + } + + /** + * Resets the list of words and randomizes the order + */ + private fun resetList() { + wordList = mutableListOf( + "queen", + "hospital", + "basketball", + "cat", + "change", + "snail", + "soup", + "calendar", + "sad", + "desk", + "guitar", + "home", + "railway", + "zebra", + "jelly", + "car", + "crow", + "trade", + "bag", + "roll", + "bubble" + ) + wordList.shuffle() + } + + /** + * Moves to the next word in the list + */ + private fun nextWord() { + //Select and remove a word from the list + if (wordList.isEmpty()) { + // TODO gameFinished() + } else { + word = wordList.removeAt(0) + } + } + + /** Methods for buttons presses **/ + + fun onSkip() { + score-- + nextWord() + } + + fun onCorrect() { + score++ + nextWord() + } +} \ No newline at end of file From 7bf22fc33869794442c48bfeb53944819734c21d Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Wed, 23 Dec 2020 16:26:53 -0300 Subject: [PATCH 04/10] refactor: Introduce Livedata to observe for data changes --- .../guesstheword/screens/game/GameFragment.kt | 33 +++++-------------- .../screens/game/GameViewModel.kt | 11 ++++--- 2 files changed, 15 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt b/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt index e35b27e07..f6ab9e6eb 100644 --- a/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt +++ b/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt @@ -32,48 +32,33 @@ class GameFragment : Fragment() { private val viewModel: GameViewModel by viewModels() - private lateinit var binding: GameFragmentBinding + private lateinit var viewBinding: GameFragmentBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = GameFragmentBinding.inflate( + viewBinding = GameFragmentBinding.inflate( inflater, container, false ) - binding.correctButton.setOnClickListener { - viewModel.onCorrect() - updateWordText() - updateScoreText() + viewBinding.correctButton.setOnClickListener { viewModel.onCorrect() } + viewBinding.skipButton.setOnClickListener { viewModel.onSkip() } + viewModel.score.observe(viewLifecycleOwner) { score -> + viewBinding.scoreText.text = score.toString() } - binding.skipButton.setOnClickListener { - viewModel.onSkip() - updateWordText() - updateScoreText() - } - updateScoreText() - updateWordText() - return binding.root + viewModel.word.observe(viewLifecycleOwner) { word -> viewBinding.wordText.text = word } + return viewBinding.root } /** * Called when the game is finished */ private fun gameFinished() { - val action = GameFragmentDirections.actionGameToScore(viewModel.score) + val action = GameFragmentDirections.actionGameToScore(viewModel.score.value ?: 0) findNavController(this).navigate(action) } - - /** Methods for updating the UI **/ - private fun updateWordText() { - binding.wordText.text = viewModel.word - } - - private fun updateScoreText() { - binding.scoreText.text = viewModel.score.toString() - } } diff --git a/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt b/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt index 2c78d0baa..5de1d0bed 100644 --- a/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt +++ b/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt @@ -1,14 +1,15 @@ package com.example.android.guesstheword.screens.game +import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class GameViewModel : ViewModel() { // The current word - var word = "" + var word = MutableLiveData().apply { value = "" } // The current score - var score = 0 + var score = MutableLiveData().apply { value = 0 } // The list of words - the front of the list is the next word to guess private lateinit var wordList: MutableList @@ -56,19 +57,19 @@ class GameViewModel : ViewModel() { if (wordList.isEmpty()) { // TODO gameFinished() } else { - word = wordList.removeAt(0) + word.value = wordList.removeAt(0) } } /** Methods for buttons presses **/ fun onSkip() { - score-- + score.value = score.value?.minus(1) nextWord() } fun onCorrect() { - score++ + score.value = score.value?.plus(1) nextWord() } } \ No newline at end of file From 218a9467cffce45e0747a167fbd56e320a5eb6e7 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Thu, 24 Dec 2020 11:14:03 -0300 Subject: [PATCH 05/10] feat: Use Livedata as a Event --- .../guesstheword/screens/game/GameFragment.kt | 19 ++++++++++---- .../screens/game/GameViewModel.kt | 25 ++++++++++++++----- settings.gradle | 1 + 3 files changed, 34 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt b/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt index f6ab9e6eb..094d87f6c 100644 --- a/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt +++ b/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt @@ -45,12 +45,20 @@ class GameFragment : Fragment() { false ) - viewBinding.correctButton.setOnClickListener { viewModel.onCorrect() } - viewBinding.skipButton.setOnClickListener { viewModel.onSkip() } - viewModel.score.observe(viewLifecycleOwner) { score -> - viewBinding.scoreText.text = score.toString() + with(viewBinding) { + correctButton.setOnClickListener { viewModel.onCorrect() } + skipButton.setOnClickListener { viewModel.onSkip() } + } + with(viewModel) { + score.observe(viewLifecycleOwner) { score -> + viewBinding.scoreText.text = score.toString() + } + word.observe(viewLifecycleOwner) { word -> viewBinding.wordText.text = word } + eventGameFinished.observe(viewLifecycleOwner) { hasFinished -> + if (hasFinished) + gameFinished() + } } - viewModel.word.observe(viewLifecycleOwner) { word -> viewBinding.wordText.text = word } return viewBinding.root } @@ -60,5 +68,6 @@ class GameFragment : Fragment() { private fun gameFinished() { val action = GameFragmentDirections.actionGameToScore(viewModel.score.value ?: 0) findNavController(this).navigate(action) + viewModel.onGameFinishedNavigated() } } diff --git a/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt b/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt index 5de1d0bed..670387c74 100644 --- a/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt +++ b/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt @@ -1,19 +1,28 @@ package com.example.android.guesstheword.screens.game +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel class GameViewModel : ViewModel() { // The current word - var word = MutableLiveData().apply { value = "" } + private val _word = MutableLiveData("") + val word: LiveData + get() = _word // The current score - var score = MutableLiveData().apply { value = 0 } + private val _score = MutableLiveData(0) + val score: LiveData + get() = _score // The list of words - the front of the list is the next word to guess private lateinit var wordList: MutableList + private val _eventGameFinished = MutableLiveData(false) + val eventGameFinished: LiveData + get() = _eventGameFinished + init { resetList() nextWord() @@ -55,21 +64,25 @@ class GameViewModel : ViewModel() { private fun nextWord() { //Select and remove a word from the list if (wordList.isEmpty()) { - // TODO gameFinished() + _eventGameFinished.value = true } else { - word.value = wordList.removeAt(0) + _word.value = wordList.removeAt(0) } } /** Methods for buttons presses **/ fun onSkip() { - score.value = score.value?.minus(1) + _score.value = score.value?.minus(1) nextWord() } fun onCorrect() { - score.value = score.value?.plus(1) + _score.value = score.value?.plus(1) nextWord() } + + fun onGameFinishedNavigated() { + _eventGameFinished.value = false + } } \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 99e067489..83859e207 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,3 +15,4 @@ */ include ':app' +rootProject.name = "Guess the Word" \ No newline at end of file From 734a5345c5e10ededbdc9455e322881ee9c95eab Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Thu, 24 Dec 2020 12:13:29 -0300 Subject: [PATCH 06/10] feat: Game now has a timer --- .../guesstheword/screens/game/GameFragment.kt | 1 + .../screens/game/GameViewModel.kt | 41 ++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt b/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt index 094d87f6c..87f9d8233 100644 --- a/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt +++ b/app/src/main/java/com/example/android/guesstheword/screens/game/GameFragment.kt @@ -58,6 +58,7 @@ class GameFragment : Fragment() { if (hasFinished) gameFinished() } + timerText.observe(viewLifecycleOwner) { viewBinding.timerText.text = it } } return viewBinding.root } diff --git a/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt b/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt index 670387c74..1db383ee0 100644 --- a/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt +++ b/app/src/main/java/com/example/android/guesstheword/screens/game/GameViewModel.kt @@ -1,5 +1,7 @@ package com.example.android.guesstheword.screens.game +import android.os.CountDownTimer +import android.text.format.DateUtils import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel @@ -23,9 +25,28 @@ class GameViewModel : ViewModel() { val eventGameFinished: LiveData get() = _eventGameFinished + private lateinit var timer: CountDownTimer + + private val _timerText = MutableLiveData("0:00") + val timerText: LiveData + get() = _timerText + init { resetList() nextWord() + configureTimer() + } + + private fun configureTimer() { + timer = object : CountDownTimer(COUNTDOWN_TIME, ONE_SECOND) { + override fun onTick(millisUntilFinished: Long) { + _timerText.value = DateUtils.formatElapsedTime(millisUntilFinished / ONE_SECOND) + } + + override fun onFinish() { + finishGame() + } + }.run { start() } } /** @@ -64,12 +85,16 @@ class GameViewModel : ViewModel() { private fun nextWord() { //Select and remove a word from the list if (wordList.isEmpty()) { - _eventGameFinished.value = true + finishGame() } else { _word.value = wordList.removeAt(0) } } + private fun finishGame() { + _eventGameFinished.value = true + } + /** Methods for buttons presses **/ fun onSkip() { @@ -85,4 +110,18 @@ class GameViewModel : ViewModel() { fun onGameFinishedNavigated() { _eventGameFinished.value = false } + + override fun onCleared() { + super.onCleared() + timer.cancel() + } + + companion object { + // This is when the game is over + const val DONE = 0L + // This is the number of milliseconds in a second + const val ONE_SECOND = 1000L + // This is the total time of the game + const val COUNTDOWN_TIME = 60000L + } } \ No newline at end of file From 1cd86905d7a3b298a1da867c96d28f4de901ce68 Mon Sep 17 00:00:00 2001 From: Filipe Bezerra Date: Fri, 25 Dec 2020 12:13:19 -0300 Subject: [PATCH 07/10] feat: Adapt ScoreFragment to work with ViewModel --- .../screens/score/ScoreFragment.kt | 40 ++++++++++--------- .../screens/score/ScoreViewModel.kt | 20 ++++++++++ .../screens/score/ScoreViewModelFactory.kt | 14 +++++++ app/src/main/res/layout/score_fragment.xml | 24 ++++++++--- 4 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/com/example/android/guesstheword/screens/score/ScoreViewModel.kt create mode 100644 app/src/main/java/com/example/android/guesstheword/screens/score/ScoreViewModelFactory.kt diff --git a/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreFragment.kt b/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreFragment.kt index 63bcb6191..74e024b7e 100644 --- a/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreFragment.kt +++ b/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreFragment.kt @@ -20,11 +20,10 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.databinding.DataBindingUtil import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs -import com.example.android.guesstheword.R import com.example.android.guesstheword.databinding.ScoreFragmentBinding /** @@ -32,29 +31,34 @@ import com.example.android.guesstheword.databinding.ScoreFragmentBinding */ class ScoreFragment : Fragment() { + private val arguments: ScoreFragmentArgs by navArgs() + + private val viewModel: ScoreViewModel by viewModels { ScoreViewModelFactory(arguments.score) } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - - // Inflate view and obtain an instance of the binding class. - val binding: ScoreFragmentBinding = DataBindingUtil.inflate( - inflater, - R.layout.score_fragment, - container, - false - ) - - // Get args using by navArgs property delegate - val scoreFragmentArgs by navArgs() - binding.scoreText.text = scoreFragmentArgs.score.toString() - binding.playAgainButton.setOnClickListener { onPlayAgain() } - - return binding.root + ): View = ScoreFragmentBinding.inflate(inflater, container, false) + .apply { + viewModel = this@ScoreFragment.viewModel + lifecycleOwner = viewLifecycleOwner + } + .root + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + with(viewModel) { + playAgainGameEvent.observe(viewLifecycleOwner) { hasClicked -> + if (hasClicked) onPlayAgain() + } + } } private fun onPlayAgain() { findNavController().navigate(ScoreFragmentDirections.actionRestart()) + .also { + viewModel.navigatedToPlayAgain() + } } } diff --git a/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreViewModel.kt b/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreViewModel.kt new file mode 100644 index 000000000..b8eaba46c --- /dev/null +++ b/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreViewModel.kt @@ -0,0 +1,20 @@ +package com.example.android.guesstheword.screens.score + +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +class ScoreViewModel(score: Int) : ViewModel() { + + private val _scoreText = MutableLiveData(score.toString()) + val scoreText: LiveData + get() = _scoreText + + private val _playAgainGameEvent = MutableLiveData(false) + val playAgainGameEvent: LiveData + get() = _playAgainGameEvent + + fun playAgain() = _playAgainGameEvent.postValue(true) + + fun navigatedToPlayAgain() = _playAgainGameEvent.postValue(false) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreViewModelFactory.kt b/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreViewModelFactory.kt new file mode 100644 index 000000000..bc20f2298 --- /dev/null +++ b/app/src/main/java/com/example/android/guesstheword/screens/score/ScoreViewModelFactory.kt @@ -0,0 +1,14 @@ +package com.example.android.guesstheword.screens.score + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider + +class ScoreViewModelFactory(private val score: Int) : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T { + if (modelClass.isAssignableFrom(ScoreViewModel::class.java)) { + return ScoreViewModel(score) as T + } + throw IllegalArgumentException("Unknown ViewModel class ${modelClass::class.java}") + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/score_fragment.xml b/app/src/main/res/layout/score_fragment.xml index 7a1999319..15e574ffb 100644 --- a/app/src/main/res/layout/score_fragment.xml +++ b/app/src/main/res/layout/score_fragment.xml @@ -16,13 +16,22 @@ + xmlns:tools="http://schemas.android.com/tools" + > + + + + + tools:context=".screens.score.ScoreFragment" + > + app:layout_constraintVertical_chainStyle="packed" + /> + tools:text="40" + />