Skip to content
This repository was archived by the owner on Sep 1, 2024. It is now read-only.
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
43 changes: 33 additions & 10 deletions README.md
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,25 +1,48 @@
Unscramble App
===================================

Single player game app that displays scrambled words. To play the game, player has to make a
word using all the letters in the displayed scrambled word.
This code demonstrates the Android Architecture component- ViewModel and LiveData.
This code sample also demonstrates how to implement Data Binding with LiveData.
Starter code for Android Basics codelab - Store the data in a ViewModel

Used in the [Android Basics with Kotlin](https://developer.android.com/courses/android-basics-kotlin/course) course.
Unscramble is a single player game app that displays scrambled words. To play the game, player has
to make a word using all the letters from the displayed scrambled word.

Used in the [Android Basics with Kotlin](https://developer.android
.com/courses/android-basics-kotlin/course) course.


Pre-requisites
--------------

You need to know:
- How to use Fragments
- How to design a layout in ConstraintLayout
- How to write control flow statements (if / else, when statements)
- How to update the UI of the app based on user input
- How to add a click listener to a Button
- Knowledge about Fragments.
- How to design a layout in ConstraintLayout.
- Able to write control flow statements (if / else, when statements).
- Able to update the UI of the app based on user input.
- Able to add a click listener to a Button.


Getting Started
---------------

1. Download and run the app.

License
-------

Copyright (C) 2020 The Android Open Source Project.

Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for
additional information regarding copyright ownership. The ASF licenses this
file to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.

22 changes: 10 additions & 12 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,12 @@ plugins {
}

android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
compileSdkVersion 31

defaultConfig {
applicationId "com.example.android.unscramble"
minSdkVersion 23
targetSdkVersion 30
targetSdkVersion 31
versionCode 1
versionName "1.0"

Expand All @@ -48,20 +47,19 @@ android {
jvmTarget = '1.8'
}
buildFeatures {
dataBinding = true
viewBinding true
}
}

dependencies {

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
// LiveData
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
// ViewModel
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.fragment:fragment-ktx:1.2.5'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
implementation 'androidx.fragment:fragment-ktx:1.3.6'
}
3 changes: 2 additions & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Unscramble">
<activity android:name=".MainActivity">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,106 +20,91 @@ 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 com.example.android.unscramble.R
import com.example.android.unscramble.databinding.GameFragmentBinding
import com.google.android.material.dialog.MaterialAlertDialogBuilder

/**
* Fragment where the game is played, contains the game logic.
*/
class GameFragment : Fragment() {

private var score = 0
private var currentWordCount = 0
private var currentScrambledWord = "test"


// Binding object instance with access to the views in the game_fragment.xml layout
private lateinit var binding: GameFragmentBinding

// Create a ViewModel the first time the fragment is created.
// If the fragment is re-created, it receives the same GameViewModel instance created by the
// first fragment.
private val viewModel: GameViewModel by viewModels()
// first fragment

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout XML file and return a binding object instance
binding = DataBindingUtil.inflate(inflater, R.layout.game_fragment, container, false)
binding = GameFragmentBinding.inflate(inflater, container, false)
return binding.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// Set the viewModel for data binding - this allows the bound layout access
// to all the data in the VieWModel
binding.gameViewModel = viewModel
binding.maxNoOfWords = MAX_NO_OF_WORDS
// Specify the fragment view as the lifecycle owner of the binding.
// This is used so that the binding can observe LiveData updates
binding.lifecycleOwner = viewLifecycleOwner

// Setup a click listener for the Submit and Skip buttons.
binding.submit.setOnClickListener { onSubmitWord() }
binding.skip.setOnClickListener { onSkipWord() }
// Update the UI
updateNextWordOnScreen()
binding.score.text = getString(R.string.score, 0)
binding.wordCount.text = getString(
R.string.word_count, 0, MAX_NO_OF_WORDS)
}

/*
* Checks the user's word, and updates the score accordingly.
* Displays the next scrambled word.
* After the last word, the user is shown a Dialog with the final score.
*/
private fun onSubmitWord() {
val playerWord = binding.textInputEditText.text.toString()

if (viewModel.isUserWordCorrect(playerWord)) {
setErrorTextField(false)
if (!viewModel.nextWord()) {
showFinalScoreDialog()
}
} else {
setErrorTextField(true)
}
currentScrambledWord = getNextScrambledWord()
currentWordCount++
score += SCORE_INCREASE
binding.wordCount.text = getString(R.string.word_count, currentWordCount, MAX_NO_OF_WORDS)
binding.score.text = getString(R.string.score, score)
setErrorTextField(false)
updateNextWordOnScreen()
}

/*
* Skips the current word without changing the score.
* Increases the word count.
* After the last word, the user is shown a Dialog with the final score.
*/
private fun onSkipWord() {
if (viewModel.nextWord()) {
setErrorTextField(false)
} else {
showFinalScoreDialog()
}
currentScrambledWord = getNextScrambledWord()
currentWordCount++
binding.wordCount.text = getString(R.string.word_count, currentWordCount, MAX_NO_OF_WORDS)
setErrorTextField(false)
updateNextWordOnScreen()
}

/*
* Creates and shows an AlertDialog with final score.
* Gets a random word for the list of words and shuffles the letters in it.
*/
private fun showFinalScoreDialog() {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.congratulations))
.setMessage(getString(R.string.you_scored, viewModel.score.value))
.setCancelable(false)
.setNegativeButton(getString(R.string.exit)) { _, _ ->
exitGame()
}
.setPositiveButton(getString(R.string.play_again)) { _, _ ->
restartGame()
}
.show()
private fun getNextScrambledWord(): String {
val tempWord = allWordsList.random().toCharArray()
tempWord.shuffle()
return String(tempWord)
}

/*
* Re-initializes the data in the ViewModel and updates the views with the new data, to
* restart the game.
*/
private fun restartGame() {
viewModel.reinitializeData()
setErrorTextField(false)
updateNextWordOnScreen()
}

/*
Expand All @@ -141,4 +126,11 @@ class GameFragment : Fragment() {
binding.textInputEditText.text = null
}
}

/*
* Displays the next scrambled word on screen.
*/
private fun updateNextWordOnScreen() {
binding.textViewUnscrambledWord.text = currentScrambledWord
}
}

This file was deleted.

Loading