Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
How to Build Android Apps with Kotlin

You're reading from   How to Build Android Apps with Kotlin A hands-on guide to developing, testing, and publishing your first apps with Android

Arrow left icon
Product type Paperback
Published in Feb 2021
Publisher Packt
ISBN-13 9781838984113
Length 794 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (4):
Arrow left icon
Eran Boudjnah Eran Boudjnah
Author Profile Icon Eran Boudjnah
Eran Boudjnah
Jomar Tigcal Jomar Tigcal
Author Profile Icon Jomar Tigcal
Jomar Tigcal
Alex Forrester Alex Forrester
Author Profile Icon Alex Forrester
Alex Forrester
Alexandru Dumbravan Alexandru Dumbravan
Author Profile Icon Alexandru Dumbravan
Alexandru Dumbravan
Arrow right icon
View More author details
Toc

Table of Contents (17) Chapters Close

Preface
1. Creating Your First App 2. Building User Screen Flows FREE CHAPTER 3. Developing the UI with Fragments 4. Building App Navigation 5. Essential Libraries: Retrofit, Moshi, and Glide 6. RecyclerView 7. Android Permissions and Google Maps 8. Services, WorkManager, and Notifications 9. Unit Tests and Integration Tests with JUnit, Mockito, and Espresso 10. Android Architecture Components 11. Persisting Data 12. Dependency Injection with Dagger and Koin 13. RxJava and Coroutines 14. Architecture Patterns 15. Animations and Transitions with CoordinatorLayout and MotionLayout 16. Launching Your App on Google Play

Activity Interaction with Intents

An intent in Android is a communication mechanism between components. Within your own app, a lot of the time, you will want another specific Activity to start when some action happens in the current activity. Specifying exactly which Activity will start is called an explicit intent. On other occasions, you will want to get access to a system component, such as the camera. As you can't access these components directly, you will have to send an intent, which the system resolves in order to open the camera. These are called implicit intents. An intent filter has to be set up in order to register to respond to these events. Go to the AndroidManifest.xml file and you will see an example of two intent filters set within the <intent-filter> XML element:

<activity android:name=".MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.          LAUNCHER" />
    </intent-filter>
</activity>

The one specified with <action android:name="android.intent.action.MAIN" /> means that this is the main entry point into the app. Depending on which category is set, it governs which Activity first starts when the app is started. The other intent filter that is specified is <category android:name="android.intent.category.LAUNCHER" />, which defines that the app should appear in the launcher. When combined, the two intent filters define that when the app is started from the launcher, MainActivity should be started. Removing any one of these intent filters results in the "Error running 'app': Default Activity not found" message. As the app has not got a main entry point, it can't be launched, which is what also happens when you remove <action android:name="android.intent.action.MAIN". />. If you remove <category android:name="android.intent.category.LAUNCHER" /> and don't specify a category, then there is nowhere that it can be launched from.

For the next exercise, you will see how intents work to navigate around your app.

Exercise 2.04: An Introduction to Intents

The goal of this exercise is to create a simple app that uses intents to display text to the user based on their input. Create a new project in Android Studio and select an empty Activity. Once you have set up the project, go to the toolbar and select File | New | Activity | Empty Activity. Call it WelcomeActivity and leave all the other defaults as they are. It will be added to the AndroidManifest.xml file, ready to use. The issue you have now that you've added WelcomeActivity is how do you do anything with it? MainActivity starts when you launch the app, but you need a way to launch WelcomeActivity and then, optionally, pass data to it, which is when you use intents:

  1. In order to work through this example, add the following code to the strings.xml file. These are the strings you'll be using in the app:
    <resources>
        <string name="app_name">Intents Introduction</string>
        <string name="header_text">Please enter your name and       then we\'ll get started!</string>
        <string name="welcome_text">Hello %s, we hope you enjoy       using the app!</string>
        <string name="full_name_label">Enter your full       name:</string>
        <string name="submit_button_text">SUBMIT</string>
    </resources>
  2. Next, update the styles in the themes.xml file adding the header style.
        <style name="header" parent=      "TextAppearance.AppCompat.Title">
            <item name="android:gravity">center</item>
            <item name="android:layout_marginStart">24dp</item>
            <item name="android:layout_marginEnd">24dp</item>
            <item name="android:layout_marginLeft">24dp</item>
            <item name="android:layout_marginRight">24dp</item>
            <item name="android:textSize">20sp</item>
        </style>
        <!--  continued below -->

    Next, add the fullname, button, and page styles:

        <style name="full_name" parent=      "TextAppearance.AppCompat.Body1">
            <item name="android:layout_marginTop">16dp</item>
            <item name="android:layout_gravity">center</item>
            <item name="android:textSize">20sp</item>
            <item name="android:inputType">text</item>
        </style>
        <style name="button" parent=      "TextAppearance.AppCompat.Button">
            <item name="android:layout_margin">16dp</item>
            <item name="android:gravity">center</item>
            <item name="android:textSize">20sp</item>
        </style>
        <style name="page">
            <item name="android:layout_margin">8dp</item>
            <item name="android:padding">8dp</item>
        </style>

    Normally, you wouldn't specify dimensions directly in the styles themselves. They should be referenced as dimens values so that they can be updated in one place, are more uniform, and can be labeled to represent what the dimension actually is. This is not done here for simplicity.

  3. Next, change the MainActivity layout in activity_main.xml and add a TextView header:
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        style="@style/page"
        tools:context=".MainActivity">
        <TextView
            android:id="@+id/header_text"
            style="@style/header"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/header_text"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>
    </androidx.constraintlayout.widget.ConstraintLayout>

    This should be the first View displayed, and as it's constrained using ConstraintLayout to the top of its parent, it displays at the top of the screen. As it's also constrained to both the start and end of its parent, it will be displayed in the middle when you run the app, as shown in Figure 2.12:

    Figure 2.12: Initial app display after adding the TextView header

    Figure 2.12: Initial app display after adding the TextView header

  4. Now, add an EditText field for the full name and a Button field for the submit button in the activity_main.xml file below the TextView header:
        <EditText
            android:id="@+id/full_name"
            style="@style/full_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="@string/full_name_label"
            app:layout_constraintTop_toBottomOf="@id/header_text"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"/>
        <Button
            android:id="@+id/submit_button"
            style="@style/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/submit_button_text"
            app:layout_constraintTop_toBottomOf="@id/full_name"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>

    The app, when run, looks as in Figure 2.13:

    Figure 2.13: The app display after adding the EditText full name field and submit button

    Figure 2.13: The app display after adding the EditText full name field and submit button

    You now need to configure the button so that when it's clicked, it retrieves the user's full name from the EditText field and then sends it in an intent, which starts WelcomeActivity.

  5. Update the activity_welcome.xml layout file to prepare to do this:
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        style="@style/page"
        tools:context=".WelcomeActivity">
        <TextView
            android:id="@+id/welcome_text"
            style="@style/header"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            tools:text="Welcome John Smith we hope you enjoy           using the app!"/>
    </androidx.constraintlayout.widget.ConstraintLayout>

    You are adding a TextView field to display the full name of the user with a welcome message. The logic to create the full name and welcome message will be shown in the next step.

  6. Now, open MainActivity and add a constant value above the class header and also update the imports:
    package com.example.intentsintroduction
    import android.content.Intent
    import android.os.Bundle
    import android.widget.Button
    import android.widget.EditText
    import android.widget.Toast
    import androidx.appcompat.app.AppCompatActivity
    const val FULL_NAME_KEY = "FULL_NAME_KEY"
    class MainActivity : AppCompatActivity()… 

    You will use the constant to set the key to hold the full name of the user by setting it in the intent.

  7. Then, add the following code to the bottom of onCreate(savedInstanceState: Bundle?):
    findViewById<Button>(R.id.submit_button).setOnClickListener {
        val fullName = findViewById<EditText>(R.id.full_name)      .text.toString().trim()
        if (fullName.isNotEmpty()) {
            //Set the name of the Activity to launch
            Intent(this, WelcomeActivity::class.java)          .also { welcomeIntent ->
                //Add the data
                welcomeIntent.putExtra(FULL_NAME_KEY, fullName)
                //Launch
                startActivity(welcomeIntent)
            }
        } else {
            Toast.makeText(this, getString(          R.string.full_name_label),           Toast.LENGTH_LONG).show()
        }
    }

    There is logic to retrieve the value of the full name and verify that the user has filled this in; otherwise, a pop-up toast message will be shown if it is blank. The main logic, however, takes the fullName value of the EditText field and creates an explicit intent to start WelcomeActivity. The also scope function allows you to carry on using the intent you've just created, Intent(this, WelcomeActivity::class.java), and further operate on it by using something called a lambda expression. The lambda argument here has a default name of it but here for clarity we've called it welcomeIntent. Then, you use the lambda argument in the welcomeIntent.putExtra(FULL_NAME_KEY, fullName) line to add the fullName field, using FULL_NAME_KEY as the key and fullName as the value to the extras that the intent holds.

    Then, you use the intent to start WelcomeActivity.

  8. Now, run the app, enter your name, and press SUBMIT, as shown in Figure 2.14:
    Figure 2.14: The default screen displayed when the intent extras data is not processed

    Figure 2.14: The default screen displayed when the intent extras data is not processed

    Well, that's not very impressive. You've added the logic to send the user's name, but not to display it.

  9. To enable this, please open WelcomeActivity and add the following to the bottom of the onCreate(savedInstanceState: Bundle?) callback:
    //Get the intent which started this activity
    intent?.let {
        //Set the welcome message
        val fullName = it.getStringExtra(FULL_NAME_KEY)
        findViewById<TextView>(R.id.welcome_text).text =
          getString(R.string.welcome_text, fullName)
    }

    We reference the intent that started the Activity with intent?.let{} which specifies that the let block will be run if the intent is not null, and let is a scope function in which you can reference the context object with a default lambda argument of it. This means you don't have to assign a variable before you can use it. You reference the intent with it and then retrieve the string value that was passed from the MainActivity intent by getting the string FULL_NAME_KEY extra key. You then format the <string name="welcome_text">Hello %s, we hope you enjoy using the app!</string> resource string by getting the string from the resources and passing in the fullname value retrieved from the intent. Finally, this is set as the text of TextView.

  10. Run the app again, and a simple greeting will be displayed, as in Figure 2.15:
    Figure 2.15: User welcome message displayed

Figure 2.15: User welcome message displayed

This exercise, although very simple in terms of layouts and user interaction, allows the demonstration of some core principles of intents. You will use them to add navigation and create user flows from one section of your app to another. In the next section, you will see how you can use intents to launch an Activity and receive a result back from it.

Exercise 2.05: Retrieving a Result from an Activity

For some user flows, you will only launch an Activity for the sole purpose of retrieving a result back from it. This pattern is often used to ask permission to use a particular feature, popping up a dialog with a question about whether the user gives their permission to access contacts, the calendar, and so on, and then reporting the result of yes or no back to the calling Activity. In this exercise, you will ask the user to pick their favorite color of the rainbow, and then once that is chosen, display the result in the calling activity:

  1. Create a new project named Activity Results and add the following strings to the strings.xml file:
        <string name="header_text_main">Please click the button       below to choose your favorite color of the rainbow!        </string>
        <string name="header_text_picker">Rainbow Colors</string>
        <string name="footer_text_picker">Click the button       above which is your favorite color of the rainbow.        </string>
        <string name="color_chosen_message">%s is your favorite       color!</string>
        <string name="submit_button_text">CHOOSE COLOR</string>
        <string name="red">RED</string>
        <string name="orange">ORANGE</string>
        <string name="yellow">YELLOW</string>
        <string name="green">GREEN</string>
        <string name="blue">BLUE</string>
        <string name="indigo">INDIGO</string>
        <string name="violet">VIOLET</string>
        <string name="unexpected_color">Unexpected color</string>
  2. Add the following colors to colors.xml
        <!--Colors of the Rainbow -->
        <color name="red">#FF0000</color>
        <color name="orange">#FF7F00</color>
        <color name="yellow">#FFFF00</color>
        <color name="green">#00FF00</color>
        <color name="blue">#0000FF</color>
        <color name="indigo">#4B0082</color>
        <color name="violet">#9400D3</color>
  3. Add the relevant new styles to the themes.xml file. A snippet is shown below, but you'll need to follow the link given to see all the code that you need to add:

    themes.xml

    11    <!-- Style for page header on launch screen -->
    12    <style name="header" parent=        "TextAppearance.AppCompat.Title">
    13        <item name="android:gravity">center</item>
    14        <item name="android:layout_marginStart">24dp</item>
    15        <item name="android:layout_marginEnd">24dp</item>
    16        <item name="android:layout_marginLeft">24dp</item>
    17        <item name="android:layout_marginRight">24dp</item>
    18        <item name="android:textSize">20sp</item>
    19    </style>
    20
    21    <!-- Style for page header on rainbow color         selection screen -->
    22    <style name="header.rainbows" parent="header">
    23        <item name="android:textSize">22sp</item>
    24        <item name="android:textAllCaps">true</item>
    25    </style>

    Note

    Dimensions have not been added to dimens.xml for simplicity.

  4. Now, you have to set up the Activity that will set the result you receive in MainActivity. Go to File | New | Activity | EmptyActivity and create an Activity called RainbowColorPickerActivity.
  5. Update the activity_main.xml layout file to display a header, a button, and then a hidden android:visibility="gone" View, which will be made visible and set with the user's favorite color of the rainbow when the result is reported:
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        style="@style/page"
        tools:context=".MainActivity">
        <TextView
            android:id="@+id/header_text"
            style="@style/header"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/header_text_main"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>
        <Button
            android:id="@+id/submit_button"
            style="@style/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/submit_button_text"
            app:layout_constraintTop_toBottomOf="@id/header_text"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>
        <TextView
            android:id="@+id/rainbow_color"
            style="@style/color_block"
            android:visibility="gone"
            app:layout_constraintTop_toBottomOf="@id/          submit_button"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            tools:text="This is your favorite color of the           rainbow"/>
    </androidx.constraintlayout.widget.ConstraintLayout>
  6. You'll be using the startActivityForResult(Intent intent, int requestCode) function to get a result back from the Activity you launch. In order to ensure that the result you get back is from the operation you expected, you have to set requestCode. Add this constant for the request code, and two others to set keys for the values we want to use in the intent, as well as a default color above the class header in MainActivity so it is displayed as follows with the package name and imports:
    package com.example.activityresults
    import android.content.Intent
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.widget.Button
    import android.widget.TextView
    const val PICK_RAINBOW_COLOR_INTENT = 1  // The request code
    // Key to return rainbow color name in intent
    const val RAINBOW_COLOR_NAME = "RAINBOW_COLOR_NAME" 
    // Key to return rainbow color in intent
    const val RAINBOW_COLOR = "RAINBOW_COLOR" 
    const val DEFAULT_COLOR = "#FFFFFF" // White
    class MainActivity : AppCompatActivity()…
  7. Then, at the bottom of onCreate(savedInstanceState: Bundle?) in MainActivity add the following:
            findViewById<Button>(R.id.submit_button).setOnClickListener {
            //Set the name of the Activity to launch passing 
            //in request code
                Intent(this, RainbowColorPickerActivity::class.java)
                .also { rainbowColorPickerIntent ->
                    startActivityForResult(
                        rainbowColorPickerIntent,
                        PICK_RAINBOW_COLOR_INTENT
                    )
                }
            }

    This uses the syntax you used previously with also to create an intent and use it with a named lambda parameter of the context object. In this case, you are using rainbowColorPickerIntent to refer to the intent you just created with Intent(this, RainbowColorPickerActivity::class.java).

    The key call is startActivityForResult(rainbowColorPickerIntent, PICK_RAINBOW_COLOR_INTENT), which launches RainbowColorPickerActivity with a request code. So, when do we get this result back? You receive the result when it is set by overriding onActivityResult(requestCode: Int, resultCode: Int, data: Intent?).

    This call specifies the request code, which you can check to confirm that it is the same as the request code you sent. resultCode reports the status of the operation. You can set your own code, but it is usually set to Activity.RESULT_OK or Activity.RESULT_CANCELED, and the last parameter, data, is the intent that has been set by the activity started for the result, RainbowColorPickerActivity.

  8. Add the following to onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) callback in MainActivity:
    override fun onActivityResult(requestCode: Int, resultCode:   Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == PICK_RAINBOW_COLOR_INTENT &&       resultCode == Activity.RESULT_OK) {
            val backgroundColor = data?.getIntExtra(RAINBOW_COLOR,           Color.parseColor(DEFAULT_COLOR)) ?:             Color.parseColor(DEFAULT_COLOR)
            val colorName = data?.getStringExtra           (RAINBOW_COLOR_NAME) ?: ""
            val colorMessage = getString           (R.string.color_chosen_message, colorName)
            val rainbowColor = findViewById           <TextView>(R.id.rainbow_color)
    rainbowColor.setBackgroundColor(ContextCompat.getColor(this,   backgroundColor))
            rainbowColor.text = colorMessage
            rainbowColor.isVisible = true
        }
    }
  9. So, you check that the request code and response code values are what is expected, and then proceed to query the intent data for the values you are expecting. For this exercise, you want to get the background color name (colorName) and the hexadecimal value of the color (backgroundColor) so that we can display it. The ? operator checks whether the value is null (that is, not set in the intent), and if so, the Elvis operator (?:) sets the default value. The color message uses String formatting to set a message replacing the placeholder in the resource value with the color name. Now that you've got the colors, you can make the rainbow_color TextView field visible and set the background color of the View to backgroundColor and add text displaying the name of the user's favorite color of the rainbow.
  10. For the layout of the RainbowColorPickerActivity activity, you are going to display a button with a background color and color name for each of the seven colors of the rainbow: RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, and VIOLET. These will be displayed in a LinearLayout vertical list. For most of the layout files in the course, you will be using ConstrainLayout, as it provides fine-grained positioning of individual Views. For situations where you need to display a vertical or horizontal list of a small number of items, LinearLayout is also a good choice. If you need to display a large number of items, then RecyclerView is a better option as it can cache layouts for individual rows and recycle views that are no longer displayed on the screen. You will learn about RecyclerView in Chapter 5, RecyclerView.
  11. The first thing you need to do in RainbowColorPickerActivity is create the layout. This will be where you present the user with the option to choose their favorite color of the rainbow.
  12. Open activity_rainbow_color_picker.xml and replace the layout, inserting the following:
    <?xml version="1.0" encoding="utf-8"?>
    <ScrollView 
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
    </ScrollView>

    We are adding ScrollView to allow the contents to scroll if the screen height cannot display all of the items. ScrollView can only take one child View, which is the layout to scroll.

  13. Next, add LinearLayout within ScrollView to display the contained views in the order that they are added with a header and a footer. The first child View is a header with the title of the page and the last View that is added is a footer with instructions to the user to pick their favorite color:
        <LinearLayout
            style="@style/page"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:orientation="vertical"
            tools:context=".RainbowColorPickerActivity">
        <TextView
            android:id="@+id/header_text"
            style="@style/header.rainbows"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/header_text_picker"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
        <TextView
            style="@style/body"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/footer_text_picker"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>
        </LinearLayout>

    The layout should now look as in Figure 2.16 in the app:

    Figure 2.16: Rainbow colors screen with a header and footer

    Figure 2.16: Rainbow colors screen with a header and footer

  14. Now, finally, add the button views between the header and the footer to select a color of the rainbow, and then run the app:
        <Button
            android:id="@+id/red_button"
            style="@style/button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/red"
            android:text="@string/red"/>
        <Button
            .......
            android:text="@string/orange"/>
        <Button
            .......
            android:text="@string/yellow"/>
        <Button
            .......
            android:text="@string/green"/>
        <Button
            .......
            android:text="@string/blue"/>
        <Button
            .......
            android:text="@string/indigo"/>
        <Button
            .......
            android:text="@string/violet"/>

    The preceding layout created is available at the following link: http://packt.live/2M7okBX

    These Views are buttons that are displayed in the order of the colors of the rainbow. Although there is a button label for the color and the background color, which is filled in with the appropriate color, the most important XML attribute is id. This is what you will use in the Activity to prepare the result of what is returned to the calling activity.

  15. Now, open RainbowColorPickerActivity and replace the content with the following:
    package com.example.activityresults
    import android.app.Activity
    import android.content.Intent
    import androidx.appcompat.app.AppCompatActivity
    import android.os.Bundle
    import android.view.View
    import android.widget.Toast
    class RainbowColorPickerActivity : AppCompatActivity() {
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_rainbow_color_picker)
        }
        private fun setRainbowColor(colorName: String, color: Int) {
            Intent().let { pickedColorIntent ->
                pickedColorIntent.putExtra(RAINBOW_COLOR_NAME,               colorName)
                pickedColorIntent.putExtra(RAINBOW_COLOR, color)
                setResult(Activity.RESULT_OK, pickedColorIntent)
                finish()
            }
        }
    }

    This is the function that creates an intent and puts the relevant String extras holding the rainbow color name and the rainbow color hex value. The result is then returned to the calling Activity, and as you have no further use of this Activity, you call finish() so that the calling Activity is displayed. The way that you retrieve the rainbow color that the user has chosen is done by adding a listener for all the buttons in the layout.

  16. Now, add the following to the bottom of onCreate(savedInstanceState: Bundle?):
    val colorPickerClickListener = View.OnClickListener { view ->
        when (view.id) {
            R.id.red_button -> setRainbowColor(          getString(R.string.red), R.color.red)
            R.id.orange_button -> setRainbowColor(          getString(R.string.orange), R.color.orange)
            R.id.yellow_button -> setRainbowColor(          getString(R.string.yellow), R.color.yellow)
            R.id.green_button -> setRainbowColor(          getString(R.string.green), R.color.green)
            R.id.blue_button -> setRainbowColor(          getString(R.string.blue), R.color.blue)
            R.id.indigo_button -> setRainbowColor(          getString(R.string.indigo), R.color.indigo)
            R.id.violet_button -> setRainbowColor(          getString(R.string.violet), R.color.violet)
            else -> {
                Toast.makeText(this, getString(              R.string.unexpected_color), Toast.LENGTH_LONG)                .show()
            }
        }
    }

    The colorPickerClickListener click listener added in the preceding code determines which colors to set for the setRainbowColor(colorName: String, color: Int) function by using a when statement. The when statement is the equivalent of the switch statement in Java and languages based on C. It allows multiple conditions to be satisfied with one branch and is more concise. In the preceding example, view.id is matched against the IDs of the rainbow layout buttons and when found, executes the branch, setting the color name and hex value from the string resources to pass into setRainbowColor(colorName: String, color: Int).

  17. Now, add this click listener to the buttons from the layout:
    findViewById<View>(R.id.red_button).setOnClickListener(  colorPickerClickListener)
    findViewById<View>(R.id.orange_button).setOnClickListener(  colorPickerClickListener)
    findViewById<View>(R.id.yellow_button).setOnClickListener(  colorPickerClickListener)
    findViewById<View>(R.id.green_button).setOnClickListener(  colorPickerClickListener)
    findViewById<View>(R.id.blue_button).setOnClickListener(  colorPickerClickListener)
    findViewById<View>(R.id.indigo_button).setOnClickListener(  colorPickerClickListener)
    findViewById<View>(R.id.violet_button).setOnClickListener(  colorPickerClickListener)

    Every button has a ClickListener interface attached, and as the operation is the same, they have the same ClickListener interface attached. Then, when the button is pressed, it sets the result of the color that the user has chosen and returns it to the calling activity.

  18. Now, run the app and press the CHOOSE COLOR button, as shown in Figure 2.17:
    Figure 2.17: The rainbow colors app start screen

    Figure 2.17: The rainbow colors app start screen

  19. Now, select your favorite color of the rainbow:
    Figure 2.18: The rainbow colors selection screen

    Figure 2.18: The rainbow colors selection screen

  20. Once you've chosen your favorite color, a screen with your favorite color will be displayed, as shown in Figure 2.19:
    Figure 2.19: The app displaying the selected color

Figure 2.19: The app displaying the selected color

As you can see, the app displays the color that you've selected as your favorite color in Figure 2.19.

This exercise introduced you to another way of creating user flows using startActivityForResult. This can be very useful for carrying out a dedicated Task where you need a result before proceeding with the user's flow through the app. Next, you will explore launch modes and how they impact the flow of user journeys when building apps.

You have been reading a chapter from
How to Build Android Apps with Kotlin
Published in: Feb 2021
Publisher: Packt
ISBN-13: 9781838984113
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image