To Examples Index Page

SecretMessage Example

Reference: Griffiths and Griffiths, Head First Android Development, O'Reilly, 2021, pp. 219 to 292.

Goal:

Directions:

  1. Create an Android project named SecretMessage.
    Set the language to Kotlin.
  2. Create the Welcome Fragment layout.
    In the Project Explorer, right click on app  and select New >> Fragment >> Fragment (Blank). Set the fragment name to WelcomeFragment and verify that the Fragment Layout Name is fragment_welcome and that the language is Kotlin.
  3. Set the layout to LinearLayout and add a textview and a button. Set these attributes:

    View/Widget Namespace Attribute Value
    LinearLayout android layout_width match_parent
    LinearLayout android layout_height match_parent
    LinearLayout android orientation vertical
    LinearLayout android gravity center_horizontal
    LinearLayout tools context .WelcomeFragment
    TextView android layout_width match_parent
    TextView android layout_height wrap_content
    TextView android text Welcome to the
    Secret Message\n
    app! Use this app
    to encrypt a\n
    secret message.
    Click on the\n
    Start button to
    begin.
    TextView android layout_marginTop 32dp
    TextView android textSize 20sp
    Button android id @+id/btn_start
    Button android layout_width wrap_content
    Button android layout_height wrap_content
    Button android layout_marginTop 32dp
    Button android text Start

    Move the text strings to the strings.xml resource file using Alt-Enter.
  4. Remove the extra code in the WelcomeFragment.kt file so that the onCreateView method looks like this:
    override fun onCreateView(inflater: LayoutInflater, 
        container: ViewGroup?, savedInstanceState: Bundle?): View? {
    
        // Inflate the layout for WelcomeFragment
        val view =  inflater.inflate(R.layout.fragment_welcome,
                container, false)
    }
    
  5. Display WelcomeFragment in a FragmentContainerView. Replace the default ConstraintLayout with this code:
    activity_main.xml layout file:
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.fragment.app.FragmentContainerView
        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:id="@+id/fragment_container_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:name="it372.secretmessage.WelcomeFragment" />
    
    Also set the padding to 16dp and set the context:
    tools:context=".MainActivity"
    
  6. Run the app. MessageFragment should display with the welcome text and the Start button.
  7. Here are the source code files so far:
         fragment_welcome.xml   strings.xml   WelcomeFragment.kt   activity_main.xml
  8. Create the MessageFragment layout.
    In the Project Explorer, right click on app  and select New >> Fragment >> Fragment (Blank). Set the fragment name to MessageFragment, verify that the Fragment Layout Name is fragment_welcome, that the language is Kotlin, and that the Target Source Set is main.
  9. Set the layout to LinearLayout and add a textview and a button. Set these attributes:

    View/Widget Namespace Attribute Value
    LinearLayout android layout_width match_parent
    LinearLayout android layout_height match_parent
    LinearLayout android orientation vertical
    LinearLayout android padding 16dp
    LinearLayout android gravity center_vertical
    LinearLayout tools context .MessageFragment
    EditText android id @+id/edt_message
    EditText android layout_width match_parent
    EditText android layout_height wrap_content
    EditText android textSize 20sp
    EditText android hint Please enter secret message.
    EditText android inputType textMultiLine
    Button android id @+id/next
    Button android layout_width wrap_content
    Button android layout_height wrap_content
    Button android layout_marginTop 32dp
    Button android text Next

    Move the text strings to the strings.xml resource file using Alt-Enter.
  10. Remove the extra code in the MessageFragment.kt file so that the onCreateView method looks like this:
    override fun onCreateView(inflater: LayoutInflater, 
        container: ViewGroup?, savedInstanceState: Bundle?): View? {
    
        // Inflate the layout for WelcomeFragment
        val view =  inflater.inflate(R.layout.fragment_welcome,
                container, false)
    }
    
  11. Use Gradle to add the Navigation component.
    In the project Gradle file, add the line
    ext.nav_version = "2.4.1"
    
    to the buildscript section at the beginning of the Gradle project files:
    buildscript {
        ext.nav_version = "2.4.1"
    }
    
    In the module Gradle module (app) file, in the dependencies section change the line
    implementation "androidx.navigation:navigation-fragment-ktx:2.4.1"
    
    to
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    
    Now click the Sync Project with Gradle Files button ( Sync Button ). This downloads and adds the Navigation component.
  12. Create a navigation graph.
    Right click on app and select New >> New Android Resource File.
    In the New Resource File Dialog, set the filename to nav_graph and the Resource Type to Navigation.
    This creates the new file nav_graph.xml in the res/navigation folder. Double click on this file if it is not already open and verify that the Design option is selected.  Click the New Destination Icon ( New Destination ) and select fragment_welcome. The WelcomeFragment layout should appear in the nav_graph.xml Design Window.

    Also, click the New Destination Icon and select fragment_message so that the MessageFragment layout appears in the Design Window.

    Click on WelcomeFragment and drag the small circle that appears on the right side to the WindowFragment.  This causes an arrow to appear from WelcomeFragment to MessageFragment representing a navigation action. In the Attributes Panel, check that the ID of this action is action_welcomeFragment_to_messageFragment.

    Here is the resulting code for the XML file nav_graph.xml:
    <?xml version="1.0" encoding="utf-8"?>
    <navigation 
        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:id="@+id/nav_graph"
        app:startDestination="@id/welcomeFragment">
    
        <fragment
            android:id="@+id/welcomeFragment"
            android:name="it372.secretmessage2.WelcomeFragment"
            android:label="fragment_welcome"
            tools:layout="@layout/fragment_welcome" >
    
            <action
                android:id="@+id/action_welcomeFragment_to_messageFragment"
                app:destination="@id/messageFragment" />
            </fragment>
    
        <fragment
            android:id="@+id/messageFragment"
            android:name="it372.secretmessage2.MessageFragment"
            android:label="fragment_message"
            tools:layout="@layout/fragment_message" />
    
    </navigation>
    
  13. Add a navigation host to the FragmentContainerView in the layout.
    Modify the activity_main.xml file to contain a NavHostFragment:
    <?xml version="1.0" encoding="utf-8"?>
    <androidx.fragment.app.FragmentContainerView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/nav_host_fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="16dp"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/nav_graph"
        app:defaultNavHost="true" 
        tools:context=".MainActivity" />
    
  14. Add an OnClickListener to the WelcomeFragment button.
    Modify the WelcomeFragment.kt file to look like this:
    // WelcomeFragment is a derived class of Fragment.
    class WelcomeFragment : Fragment( ) {
    
        override fun onCreateView(inflater: LayoutInflater,
            container: ViewGroup?, savedInstanceState: Bundle?): View? {
    
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_welcome,
            container, false)
    
        // Create Button object and add OnClickListener to button.
        val startButton = view.findViewById<Button>(R.id.btn_start)
        startButton.setOnClickListener {
            // Code that runs when the button is clicked
            view.findNavController( ).
                navigate(R.id.action_welcomeFragment_to_messageFragment)
        }
        return view
    }
    
  15. Extract strings to the strings.xml resource file.
  16. Run the app. Click the Next Button to navigate from WelcomeFragment to MessageFragment.
    The source code files that have changed:
         fragment_message.xml  strings.xml  WelcomeFragment.kt
         MessageFragment.kt  nav_graph.xml  activity_main.xml
  17. Create the EncryptionFragment layout.
    In the Project Explorer, right click on app  and select New >> Fragment >> Fragment (Blank). Set the fragment name to EncryptFragment, verify that the Fragment Layout Name is fragment_encrypt, that the language is Kotlin, and that the Target Source Set is main.
  18. Set the layout to LinearLayout and add a textview and a button. Set these attributes:

    View/Widget Namespace Attribute Value
    LinearLayout android layout_width match_parent
    LinearLayout android layout_height match_parent
    LinearLayout android orientation vertical
    LinearLayout android padding 16dp
    LinearLayout android gravity center_vertical
    LinearLayout tools context .MessageFragment
    TextView android layout_width match_parent
    TextView android layout_height wrap_content
    TextView android layout_marginTop 20sp
    TextView android textSize 20sp
    TextView android text Here is your encrypted message.
    TextView android inputType textMultiLine
    TextView android id @+id/txt_encrypted_message
    TextView android layout_width match_parent
    TextView android layout_height wrap_content
    TextView android layout_marginTop 20dp

    Move the text strings to the strings.xml resource file using Alt-Enter.
  19. Remove the extra code in the EncryptFragment.kt file so that the onCreateView method looks like this:
    override fun onCreateView(inflater: LayoutInflater, 
        container: ViewGroup?, savedInstanceState: Bundle?): View? {
    
        // Inflate the layout for WelcomeFragment
        val view =  inflater.inflate(R.layout.fragment_welcome,
                container, false)
    }
    
  20. Open nav_graph.xml in the Design Window. Add encryptFragment as a new destination. Add an action arrow from messageFragrment to encryptFragment. Here is the updated nav_graph.xml file:
    <?xml version="1.0" encoding="utf-8"?>
    <navigation 
        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:id="@+id/nav_graph"
        app:startDestination="@id/welcomeFragment">
    
    <fragment
        android:id="@+id/welcomeFragment"
        android:name="it372.secretmessage2.WelcomeFragment"
        android:label="fragment_welcome"
        tools:layout="@layout/fragment_welcome">
        <action
            android:id="@+id/action_welcomeFragment_to_messageFragment"
            app:destination="@id/messageFragment" />
    </fragment>
    
    <fragment
        android:id="@+id/messageFragment"
        android:name="it372.secretmessage2.MessageFragment"
        android:label="fragment_message"
        tools:layout="@layout/fragment_message">
        <action
            android:id="@+id/action_messageFragment_to_encryptFragment"
            app:destination="@id/encryptFragment" />
    </fragment>
    
    <fragment
        android:id="@+id/encryptFragment"
        android:name="it372.secretmessage2.EncryptFragment"
        android:label="fragment_encrypt"
        tools:layout="@layout/fragment_encrypt" />
    </navigation>
    
  21. Modify MessageFragment.kt so that clicking the button navigates to EncryptFragment.
    // SecretMessage Example
    // Source code file: MessageFragment.kt
    
    package it372.secretmessage2
    import android.os.Bundle
    import androidx.fragment.app.Fragment
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.Button
    import androidx.navigation.findNavController
    class MessageFragment : Fragment() {
    
        override fun onCreateView(inflater: LayoutInflater,
            container: ViewGroup?, savedInstanceState: Bundle?): View? {
    
            val view = inflater.inflate(R.layout.fragment_message,
                container, false)
            val nextButton = view.findViewById<Button>(R.id.btn_next);
            nextButton.setOnClickListener {
                view.findNavController( ).
                navigate(R.id.action_messageFragment_to_encryptFragment)
            }
            return view
        }
    }
    
  22. Add Safe Args to the Gradle files.
    Add a classpath to the project build.gradle file. Add this code to the buildscript section:
    buildscript {
        ext.nav_version = "2.4.2"
        dependencies {
            classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
        }
    }
    Add this line to the plugins section of the module build.gradle file:
    plugins {
        id 'android:navigation.safeargs'
    }
    
  23. Set EncryptFragment to accept a String argument.
    Open nav_graph.xml in the Design Window and click the + to the right of Arguments. In the Add Argument dialog, enter the name of the argument as message and type of the argument as String. Click the Add button.
    Then check the Code Window for nav_graph.xml. This code has been added to MessageFragment:
    <argument
        android:name="message"
        app:argType="string">
    
  24. Update MessageFragment.kt
    Here is the updated code that sends the message from MessageFragment to EncryptFragment:
    import android.view.ViewGroup
    import android.widget.Button
    import android.widget.EditText;
    import androidx.navigation.findNavController
    class MessageFragment : Fragment() {
    
        override fun onCreateView(inflater: LayoutInflater,
            container: ViewGroup?, savedInstanceState: Bundle?): View? {
    
        // Inflate the layout for this fragment
        val view = inflater.inflate(R.layout.fragment_message,
            container, false)
        val nextButton = view.findViewById<Button>(R.id.btn_next)
        val messageView = view.findViewById<EditText>(R.id.edt_message)
        nextButton.setOnClickListener {
            val message = messageView.text.toString( )
            val action = MessageFragmentDirections.
            actionMessageFragmentToEncryptFragment(message)
            view.findNavController( ).navigate(action)
        }
        return view
    }
  25. Update EncryptFragment.kt.
    Here is the updated code that receives the message in EncryptFragment from MessageFragment:
    package it372.secretmessage2
    import android.os.Bundle
    import androidx.fragment.app.Fragment
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import android.widget.TextView
    class EncryptFragment : Fragment( ) {
    
        override fun onCreateView(inflater: LayoutInflater,
            container: ViewGroup?, savedInstanceState: Bundle?): View? {
    
            val view = inflater.inflate(R.layout.fragment_encrypt,
                container, false)
            val message = EncryptFragmentArgs.fromBundle(
                requireArguments( )).message
            val encryptedView = view.findViewById<TextView>(
                R.id.txt_encrypted_message)
            encryptedView.text = message.reversed( )
            return view;
        }
    }
    
  26. Change the back behavior.
    Change the behavior of the back button to go from EncryptFragment directly to the WelcomeFragment. Use the navigation graph to pop fragments off of the back stack. Open nav_graph.xml in the Design Window. Select the action arrow that connects MessageFragment to EncryptFragment. In the Navigation pane, go to Pop Behavior >> popUpTo. Select WelcomeFragment.
  27. Run the app. Here are the final source code files:
        fragment_welcome.xml  fragment_message.xml  fragment_encrypt.xml
        strings.xml  WelcomeFragment.kt  MessageFragment.kt  EncryptFragment.kt
        activity_main.xml  MainActivity.kt  nav_graph.xml 
        build.gradle (Project)  build.gradle (app)