Final Results

Our revised app/build.gradle should resemble:

plugins {
  id 'com.android.application'
  id 'kotlin-android'
  id 'androidx.navigation.safeargs.kotlin'
  id 'kotlin-kapt'
}

android {
  compileSdk 31

  defaultConfig {
    applicationId "com.commonsware.todo"
    minSdk 21
    targetSdk 31
    versionCode 1
    versionName "1.0"

    testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
  }

  buildTypes {
    release {
      minifyEnabled false
      proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
    }
  }

  buildFeatures {
    viewBinding true
  }

  compileOptions {
    coreLibraryDesugaringEnabled true
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }

  kotlinOptions {
    jvmTarget = '1.8'
  }

  packagingOptions {
    exclude 'META-INF/AL2.0'
    exclude 'META-INF/LGPL2.1'
  }
}

dependencies {
  implementation 'androidx.core:core-ktx:1.6.0'
  implementation 'androidx.appcompat:appcompat:1.3.1'
  implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
  implementation "androidx.recyclerview:recyclerview:1.2.1"
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
  implementation "androidx.preference:preference-ktx:1.1.1"
  implementation 'com.google.android.material:material:1.4.0'
  implementation "io.insert-koin:koin-android:$koin_version"
  implementation "com.github.jknack:handlebars:4.1.2"
  implementation "androidx.room:room-runtime:$room_version"
  implementation "androidx.room:room-ktx:$room_version"
  kapt "androidx.room:room-compiler:$room_version"
  coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
  testImplementation 'junit:junit:4.13.2'
  testImplementation "org.mockito:mockito-inline:3.12.1"
  testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0"
  testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.1'
  androidTestImplementation 'androidx.test.ext:junit:1.1.3'
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
  androidTestImplementation "androidx.arch.core:core-testing:2.1.0"
  androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.1'
}

Our new prefs XML resource should contain:

<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto">

  <EditTextPreference
    android:key="@string/web_service_url_key"
    android:selectAllOnFocus="true"
    android:title="@string/pref_url_title"
    app:defaultValue="@string/web_service_url_default" />
</PreferenceScreen>

Our updated strings resource should look like:

<resources>
  <string name="app_name">ToDo</string>
  <string name="msg_empty">Click the + icon to add a todo item!</string>
  <string name="msg_empty_filtered">Click the + icon to add a todo item, or change your filter to show other items</string>
  <string name="menu_about">About</string>
  <string name="is_completed">Item is completed</string>
  <string name="created_on">Created on:</string>
  <string name="menu_edit">Edit</string>
  <string name="desc">Description</string>
  <string name="notes">Notes</string>
  <string name="menu_save">Save</string>
  <string name="menu_add">Add</string>
  <string name="menu_delete">Delete</string>
  <string name="menu_filter">Filter</string>
  <string name="menu_filter_all">All</string>
  <string name="menu_filter_completed">Completed</string>
  <string name="menu_filter_outstanding">Outstanding</string>
  <string name="oops">Sorry! Something went wrong!</string>
  <string name="report_template"><![CDATA[<h1>To-Do Items</h1>

<h2></h2>
<p><b>COMPLETED</b> &mdash; Created on: </p>
<p></p>

]]></string>
  <string name="menu_share">Share</string>
  <string name="pref_url_title">Web service URL</string>
  <string name="web_service_url_key">webServiceUrl</string>
  <string name="web_service_url_default">https://commonsware.com/AndExplore/2.0/items.json</string>
  <string name="settings">Settings</string>
</resources>

The new PrefsFragment should contain:

package com.commonsware.todo.ui.prefs

import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import com.commonsware.todo.R

class PrefsFragment : PreferenceFragmentCompat() {
  override fun onCreatePreferences(state: Bundle?, rootKey: String?) {
    setPreferencesFromResource(R.xml.prefs, rootKey)
  }
}

The revised nav_graph navigation resource should resemble:

<?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"
  android:id="@+id/nav_graph.xml"
  app:startDestination="@id/rosterListFragment">

  <fragment
    android:id="@+id/rosterListFragment"
    android:name="com.commonsware.todo.ui.roster.RosterListFragment"
    android:label="@string/app_name">
    <action
      android:id="@+id/displayModel"
      app:destination="@id/displayFragment" />
    <action
      android:id="@+id/createModel"
      app:destination="@id/editFragment" >
      <argument
        android:name="modelId"
        android:defaultValue="@null" />
    </action>
  </fragment>
  <fragment
    android:id="@+id/displayFragment"
    android:name="com.commonsware.todo.ui.display.DisplayFragment"
    android:label="@string/app_name" >
    <argument
      android:name="modelId"
      app:argType="string" />
    <action
      android:id="@+id/editModel"
      app:destination="@id/editFragment" />
  </fragment>
  <fragment
    android:id="@+id/editFragment"
    android:name="com.commonsware.todo.ui.edit.EditFragment"
    android:label="@string/app_name" >
    <argument
      android:name="modelId"
      app:argType="string"
      app:nullable="true" />
  </fragment>
  <fragment
    android:id="@+id/prefsFragment"
    android:name="com.commonsware.todo.ui.prefs.PrefsFragment"
    android:label="@string/settings" />
  <action android:id="@+id/editPrefs" app:destination="@id/prefsFragment" />
</navigation>

The updated actions menu resource should look like:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
  xmlns:android="http://schemas.android.com/apk/res/android">

  <item
    android:id="@+id/settings"
    android:icon="@drawable/ic_settings"
    android:orderInCategory="90"
    android:title="@string/settings"
    app:showAsAction="never" />
  <item
    android:id="@+id/about"
    android:icon="@drawable/ic_about"
    android:orderInCategory="100"
    android:title="@string/menu_about"
    app:showAsAction="never" />
</menu>

And the updated MainActivity should contain:

package com.commonsware.todo.ui

import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.findNavController
import androidx.navigation.fragment.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.NavigationUI.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import com.commonsware.todo.R
import com.commonsware.todo.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
  private lateinit var appBarConfiguration: AppBarConfiguration

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val binding = ActivityMainBinding.inflate(layoutInflater)

    setContentView(binding.root)
    setSupportActionBar(binding.toolbar)

    supportFragmentManager.findFragmentById(R.id.nav_host)?.findNavController()?.let { nav ->
      appBarConfiguration = AppBarConfiguration(nav.graph)
      setupActionBarWithNavController(nav, appBarConfiguration)
    }
  }

  override fun onCreateOptionsMenu(menu: Menu): Boolean {
    menuInflater.inflate(R.menu.actions, menu)

    return super.onCreateOptionsMenu(menu)
  }

  override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
    R.id.about -> {
      startActivity(Intent(this, AboutActivity::class.java))
      true
    }
    R.id.settings -> {
      findNavController(R.id.nav_host).navigate(R.id.editPrefs)
      true
    }
    else -> super.onOptionsItemSelected(item)
  }

  override fun onSupportNavigateUp() =
    navigateUp(findNavController(R.id.nav_host), appBarConfiguration)
}

Prev Table of Contents Next

This book is licensed under the Creative Commons Attribution-ShareAlike 4.0 International license.