Your First Kotlin Android App: Polishing the App

Aug 22 2023 · Kotlin 1.8.20, Android 13, Android Studio Flamingo | 2022.2.1

Part 1: Build Out the App

06. Improve the Alert Title

Episode complete

Play next episode

Next
About this episode
Leave a rating/review
See forum comments
Cinema mode Mark complete Download course materials
Previous episode: 05. Keep Track of Game Rounds Next episode: 07. Challenge: Add Bonus Points

Get immediate access to this and 4,000+ other videos and books.

Take your career further with a Kodeco Personal Plan. With unlimited access to over 40+ books and 4,000+ professional videos in a single subscription, it's simply the best investment you can make in your development career.

Learn more Already a subscriber? Sign in.

Transcript: 06. Improve the Alert Title

Perfect! You almost had it! Not bad. Are you even trying?


Okay, this is not a challenge episode. I didn’t mean what I said earlier.

Now, if you look at the Bullseye app, you can see that it is playable but there’s definitely room for improvement. Currently, the alert dialog still says “Hello There!”. You could give it the name of the game, Bullseye, but I have a better idea.

What if you change the title depending on how well the player did?

If the player put the slider right on the target, the alert could say: “Perfect!” If the slider is close to the target but not quite there, it could say, “You almost had it!” If the player is way off, the alert could say: “Not even close…” And so on. This gives the player a little more feedback on how well they did.

In the previous course, you learned about variables, strings, and if statements, this should be a breeze. Let’s try it out!

You’re going to create a new function that will return an Int, in this case, the alert’s title R constant which is an integer. You’ll find out later on why we don’t just return the string directly in this function.

Go ahead and create this function right below the pointsForCurrentRound() function:

fun alertTitle(): Int {

}

This function is expected to return an Int as seen from its definition and that’s why you have an error since nothing has been returned. Not to worry, you’ll return something shortly.

Next, you’ll get the difference by doing a little calculation like so:

val difference = abs(targetValue - sliderToInt)

At this point, we’ll declare a title variable and set it with an if block like so:

//...
val title: Int = if (difference == 0) {
  R.string.alert_title_1
} else if (difference < 5) {
  R.string.alert_title_2
} else if (difference <= 10) {
  R.string.alert_title_3
} else {
  R.string.alert_title_4
}

Finally, return the title as the last line of the function:

//...
return title

The code is self descriptive. We simply assign the title based on the value of the difference. And we set the conditions using the if statement. Do note that the string resources are already added to the starter project of this episode and you can also find them in the author notes of this episode too.

To see them, hold down the Cmd key and click on one. This takes you its definition in the strings.xml file. And you can see the alert title levels with their corresponding title string. I’ll close this file up.

Now, there’s another conditional statement that can be used when assigning the result of a conditional statement to a variable. And it is called the when statement. Click on the if statement. Then the bulb icon. Then select “Replace if with when.” And your code is updated to accordingly.

val title: Int = when {
  difference == 0 -> {
    R.string.alert_title_1
  }
  difference < 5 -> {
    R.string.alert_title_2
  }
  difference <= 10 -> {
    R.string.alert_title_3
  }
  else -> {
    R.string.alert_title_4
  }
}

Take a look at the code. It’s pretty much doing the same thing but with a different syntax using the when statement. If you don’t like this syntax, just undo the change and move on. Remember, its just an alternative syntax. You’ll learn more about the when statement as you progress in your learning path.

Finally, let’s assign this function we just created as the title of the AlertDialog. Pass it in as the value of a new parameter which will be added to the ResultDialog composable like so:

ResultDialog(
  dialogTitle = alertTitle(),
  //...
)

There’s an error as expected so let’s go add in that new parameter inside ResultDialog’s definition. Navigate to the ResultDialog composable by holding down Cmd and click on it.

Then update your code to the following:

@Composable
fun ResultDialog(
  hideDialog: () -> Unit,
  dialogTitle: Int, // New Code
  //...
) {
  //...
    title = { Text(stringResource(id = dialogTitle)) }, // Updated Code
}

Now before you run the app, I want to tell why we didn’t make the alertTitle() function return a string directly. If you hover over the stringResource() function, you can see it has a @Composable annotation which makes it a composable function. And remember from the previous course, one of the rules of a composable function is that a composable function can only be called inside another composable function.

The alertTitle() function is not a composable function so you’ll get an error if you try calling stringResource() inside it. And that is why we just return the R constant which is the resource id.

Alright, go ahead and run the app.

Then play multiple rounds to see different variations of the alert title.

This looks great - but something doesn’t really smell right. Not an actual smell, but something known as a “Code smell.” What is a code smell? You might ask.

Whenever you find repeated lines of code in your app, that’s a code smell. In our code, you can see we have the same code to calculate the difference repeated in multiple places.

Programmers prefer to write each line of code only once and then reuse it in multiple places.

This is an approach that programmers call “Don’t Repeat Yourself,” which is often shortened to DRY.

The rationale behind DRY is to make code easier to read and change and to make it less prone to errors when updating your code because you have the code in one place which is then reused in multiple places.

For example, if you decide later that you want to change the way that you converted the slider’s value, you’d have to change it in one spot if you were using DRY code. If you didn’t use the DRY approach then you’ll have to remember to fix the code in different spots.

Code smell here simply means repeating code becomes a problem when you have to update the same code in multiple places. Thats a deeper problem if you think about it carefully.

Now, determining what is and what’s not a code smell is subjective and can be very opinionated.

For instance, I personally use the WET approach which means “Write Everything Twice.” With this approach, I only refactor my code when I need to reuse the same code the third time. With this, I save a lot of time during development and avoid premature optimization because in many cases, I only repeat code twice.

And with this, I have successfully confused you on which approach to use. Okay let’s use the DRY approach to get rid of the code smell.

You can see that we have repeated code for the difference calculation. Let’s refactor this uisng the DRY principle. The first thing you’ll do is create a new function to calculate the difference. We’ll call this differenceAmount().

Enter the following code above the pointsForCurrentRound() function:

fun differenceAmount() = abs(targetValue - sliderToInt)

The code above shows the use of something called: “Single Expression Function.” It acts like a variable and if you notice we did not include the return type of Int in the function definition. This is simply because the function is short and easy to read so the compiler can easily infer its type as Int based on the value on the other side of the operator.

Now go ahead and replace it where the difference is needed inside the pointsForCurrentRound() and alertTitle() functions.

fun pointsForCurrentRound(): Int {
  //...
  val difference = differenceAmount() // Updated Code
  //...
}

fun alertTitle(): Int {
  val difference = differenceAmount() // Updated Code
  //...
}

Cool!!! And with that, our code smell is gone and we have a reusable function for the difference calculation.