Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@ android {
buildFeatures {
dataBinding true
}

// Configure only for each module that uses Java 8
// language features (either in its source code or
// through dependencies).
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
// For Kotlin projects
kotlinOptions {
jvmTarget = "1.8"
}
}

dependencies {
Expand All @@ -53,6 +65,7 @@ dependencies {

// KTX
implementation 'androidx.core:core-ktx:1.3.1'
implementation "androidx.fragment:fragment-ktx:1.2.5"

// Navigation
implementation "android.arch.navigation:navigation-fragment-ktx:1.0.0-rc02"
Expand Down
9 changes: 7 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.example.android.guesstheword">

<uses-permission android:name="android.permission.VIBRATE" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_guess_it"
Expand All @@ -30,8 +32,11 @@
<!-- Screen locked to landscape for easier play -->
<!-- configChanges attribute makes the following actions NOT cause a config change -->
<!-- screenOrientation attribute sets the default animation-->
<activity android:name=".MainActivity">

<activity
android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="landscape"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,125 +17,76 @@
package com.example.android.guesstheword.screens.game

import android.os.Bundle
import android.os.VibrationEffect
import android.os.Vibrator
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.core.content.getSystemService
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.navigation.fragment.NavHostFragment.findNavController
import com.example.android.guesstheword.R
import com.example.android.guesstheword.databinding.GameFragmentBinding

/**
* Fragment where the game is played
*/
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<String>

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(
inflater,
R.layout.game_fragment,
container,
false
)

resetList()
nextWord()

binding.correctButton.setOnClickListener { onCorrect() }
binding.skipButton.setOnClickListener { onSkip() }
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()
}
private val viewModel by viewModels<GameViewModel>()

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? = GameFragmentBinding.inflate(inflater)
.apply {
this.viewmodel = viewModel
this.lifecycleOwner = viewLifecycleOwner
}
.also {
viewModel.run {
eventGameFinish.observe(viewLifecycleOwner, Observer { hasFinished ->
handleEventGameFinish(hasFinished)
})
eventBuzz.observe(viewLifecycleOwner, Observer { buzzType ->
handleEventBuzz(buzzType)
})
}
}
.root

/**
* Called when the game is finished
*/
private fun gameFinished() {
val action = GameFragmentDirections.actionGameToScore(score)
findNavController(this).navigate(action)
private fun handleEventGameFinish(hasFinished: Boolean) {
if (hasFinished) {
val action = GameFragmentDirections.actionGameToScore(viewModel.score.value ?: 0)
findNavController(this).navigate(action)
viewModel.onGameFinishComplete()
}
}

/**
* 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)
private fun handleEventBuzz(type: GameViewModel.BuzzType) {
if (type != GameViewModel.BuzzType.NO_BUZZ) {
buzz(type.pattern)
viewModel.onBuzzComplete()
}
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

}

private fun updateScoreText() {
binding.scoreText.text = score.toString()
@Suppress("DEPRECATION")
private fun buzz(pattern: LongArray) {
activity?.getSystemService<Vibrator>()
?.let { vibrator ->
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1))
} else {
vibrator.vibrate(pattern, -1)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
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.Transformations
import androidx.lifecycle.ViewModel

private val CORRECT_BUZZ_PATTERN = longArrayOf(100, 100, 100, 100, 100, 100)
private val PANIC_BUZZ_PATTERN = longArrayOf(0, 200)
private val GAME_OVER_BUZZ_PATTERN = longArrayOf(0, 2000)
private val NO_BUZZ_PATTERN = longArrayOf(0)

class GameViewModel : ViewModel() {

companion object {
// These represent different important times
// This is when the game is over
private const val DONE = 0L

// This is the number of milliseconds in a second
private const val ONE_SECOND = 1000L

// This is the total time of the game
private const val COUNTDOWN_TIME = 60000L

// This is when we must start to buzz a panic pattern
private const val COUNTDOWN_PANIC_SECONDS = 10L
}

// These are the three different types of buzzing in the game. Buzz pattern is the number of
// milliseconds each interval of buzzing and non-buzzing takes.
enum class BuzzType(val pattern: LongArray) {
CORRECT(CORRECT_BUZZ_PATTERN),
COUNTDOWN_PANIC(PANIC_BUZZ_PATTERN),
GAME_OVER(GAME_OVER_BUZZ_PATTERN),
NO_BUZZ(NO_BUZZ_PATTERN)
}

// The current word
private val _word = MutableLiveData("")
val word: LiveData<String>
get() = _word

// The current score
private val _score = MutableLiveData(0)
val score: LiveData<Int>
get() = _score

private val _eventGameFinish = MutableLiveData(false)
val eventGameFinish: LiveData<Boolean>
get() = _eventGameFinish

private val _eventBuzz = MutableLiveData(BuzzType.NO_BUZZ)
val eventBuzz: LiveData<BuzzType>
get() = _eventBuzz

// The list of words - the front of the list is the next word to guess
private lateinit var wordList: MutableList<String>

private val timer: CountDownTimer

private val _currentTime = MutableLiveData<Long>(0)
val currentTime: LiveData<Long>
get() = _currentTime

val elapsedTime: LiveData<String>
get() = Transformations.map(currentTime) { time -> DateUtils.formatElapsedTime(time) }

init {
resetList()
nextWord()
timer = object : CountDownTimer(COUNTDOWN_TIME, ONE_SECOND) {
override fun onTick(millisUntilFinished: Long) = onTimerTick(millisUntilFinished)
override fun onFinish() = finishGame()
}
timer.start()
}

private fun onTimerTick(millisUntilFinished: Long) {
val secondsToFinished = millisUntilFinished / ONE_SECOND
_currentTime.value = secondsToFinished
if (secondsToFinished <= COUNTDOWN_PANIC_SECONDS) {
_eventBuzz.value = BuzzType.COUNTDOWN_PANIC
}
}

private fun finishGame() {
_currentTime.value = DONE
_eventGameFinish.value = true
_eventBuzz.value = BuzzType.GAME_OVER
}

/**
* 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() {
if (wordList.isEmpty()) {
resetList()
}
//Select and remove a word from the list
_word.value = wordList.removeAt(0)
}

/** Methods for buttons presses **/
fun onSkip() {
_score.value = score.value?.minus(1)
nextWord()
}

fun onCorrect() {
_score.value = score.value?.plus(1)
_eventBuzz.value = BuzzType.CORRECT
nextWord()
}

fun onGameFinishComplete() {
_eventGameFinish.value = false
}

fun onBuzzComplete() {
_eventBuzz.value = BuzzType.NO_BUZZ
}

override fun onCleared() {
super.onCleared()
timer.cancel()
}
}
Loading