diff --git a/MADSkillsNavigationSample/.gitignore b/MADSkillsNavigationSample/.gitignore
new file mode 100644
index 00000000..09b993d0
--- /dev/null
+++ b/MADSkillsNavigationSample/.gitignore
@@ -0,0 +1,8 @@
+*.iml
+.gradle
+/local.properties
+/.idea
+.DS_Store
+/build
+/captures
+.externalNativeBuild
diff --git a/MADSkillsNavigationSample/README.md b/MADSkillsNavigationSample/README.md
new file mode 100644
index 00000000..e0e919e4
--- /dev/null
+++ b/MADSkillsNavigationSample/README.md
@@ -0,0 +1,46 @@
+MAD Skills Navigation Sample (Donut Tracker)
+==============================================
+
+This sample shows the features of Navigation component highlighted by the Navigation
+episodes in the MAD Skills series of [videos](https://www.youtube.com/user/androiddevelopers)
+and [articles](https://medium.com/androiddevelopers). Specifically, episodes
+2, 3, andd 4 walk through code from this sample.
+
+### Features
+
+This sample showcases the following features of the Navigation component:
+
+ * Dialog destinations (episode 2)
+ * Using SafeArgs to pass data between destinations (episode 3)
+ * Navigating via shortcuts and notifications with Deep Links (episode 4)
+
+### Screenshots
+
+
+### Other Resources
+
+ * For an overview of using Navigation component, check out
+ [Get started with the Navigation component](https://developer.android.com/guide/navigation/navigation-getting-started)
+ * Consider including the [Navigation KTX libraries](https://developer.android.com/topic/libraries/architecture/adding-components#navigation)
+ for more concise uses of the Navigation component. For example, calls to `Navigation.findNavController(view)` can
+ be expressed as `view.findNavController()`.
+
+License
+-------
+
+Copyright 2020 The Android Open Source Project, Inc.
+
+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.
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/build.gradle b/MADSkillsNavigationSample/app/build.gradle
new file mode 100644
index 00000000..c15ffd79
--- /dev/null
+++ b/MADSkillsNavigationSample/app/build.gradle
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: "androidx.navigation.safeargs.kotlin"
+apply plugin: 'kotlin-kapt'
+
+android {
+ compileSdkVersion 29
+ buildToolsVersion "29.0.2"
+
+ buildFeatures {
+ viewBinding = true
+ }
+
+ defaultConfig {
+ applicationId "com.android.samples.navdonutcreator"
+ minSdkVersion 21
+ targetSdkVersion 29
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+
+// To inline the bytecode built with JVM target 1.8 into
+// bytecode that is being built with JVM target 1.6. (e.g. navArgs)
+
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
+
+ packagingOptions {
+ exclude 'META-INF/atomicfu.kotlin_module'
+ }
+}
+
+configurations {
+ ktlint
+}
+
+dependencies {
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+ implementation "androidx.appcompat:appcompat:$appCompatVersion"
+ implementation "androidx.core:core-ktx:$ktxVersion"
+ implementation "com.google.android.material:material:$materialVersion"
+ implementation "androidx.constraintlayout:constraintlayout:$constraintLayoutVersion"
+ implementation "androidx.navigation:navigation-fragment-ktx:$navigationVersion"
+ implementation "androidx.navigation:navigation-ui-ktx:$navigationVersion"
+
+ implementation "androidx.fragment:fragment-ktx:$fragmentVersion"
+
+ implementation "androidx.lifecycle:lifecycle-livedata-core-ktx:$archLifecycleVersion"
+ implementation "androidx.lifecycle:lifecycle-livedata-ktx:$archLifecycleVersion"
+
+ implementation "androidx.room:room-runtime:$roomVersion"
+ kapt "androidx.room:room-compiler:$roomVersion"
+ implementation "androidx.room:room-ktx:$roomVersion"
+
+ api "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion"
+ api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
+
+ ktlint "com.pinterest:ktlint:$ktlintVersion"
+}
+
+
+// Disable the 'paren-spacing' rule which conflicts with non-paren annotations on functional types
+task ktlint(type: JavaExec, group: "verification") {
+ description = "Check Kotlin code style."
+ main = "com.pinterest.ktlint.Main"
+ classpath = configurations.ktlint
+ args "src/**/*.kt", "--disabled_rules", "paren-spacing"
+}
+check.dependsOn ktlint
+task ktlintFormat(type: JavaExec, group: "formatting") {
+ description = "Fix Kotlin code style deviations."
+ main = "com.pinterest.ktlint.Main"
+ classpath = configurations.ktlint
+ args "-F", "src/**/*.kt", "--disabled_rules", "paren-spacing"
+}
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/proguard-rules.pro b/MADSkillsNavigationSample/app/proguard-rules.pro
new file mode 100644
index 00000000..f1b42451
--- /dev/null
+++ b/MADSkillsNavigationSample/app/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/MADSkillsNavigationSample/app/src/main/AndroidManifest.xml b/MADSkillsNavigationSample/app/src/main/AndroidManifest.xml
new file mode 100644
index 00000000..b9d491df
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/AndroidManifest.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/Donut.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/Donut.kt
new file mode 100644
index 00000000..59f96eeb
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/Donut.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker
+
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * This class holds the data that we are tracking for each donut: its name, a description, and
+ * a rating.
+ */
+@Entity
+data class Donut(
+ @PrimaryKey(autoGenerate = true) val id: Long,
+ val name: String,
+ val description: String = "",
+ val rating: Int
+)
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutEntryDialogFragment.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutEntryDialogFragment.kt
new file mode 100644
index 00000000..3d5c1ee8
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutEntryDialogFragment.kt
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.observe
+import androidx.navigation.fragment.findNavController
+import androidx.navigation.fragment.navArgs
+import com.android.samples.donuttracker.databinding.DonutEntryDialogBinding
+import com.android.samples.donuttracker.storage.DonutDatabase
+import com.google.android.material.bottomsheet.BottomSheetDialogFragment
+
+/**
+ * This dialog allows the user to enter information about a donut, either creating a new
+ * entry or updating an existing one.
+ */
+class DonutEntryDialogFragment : BottomSheetDialogFragment() {
+
+ private lateinit var donutEntryViewModel: DonutEntryViewModel
+
+ private enum class EditingState {
+ NEW_DONUT,
+ EXISTING_DONUT
+ }
+
+ @SuppressLint("SetTextI18n")
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ val donutDao = DonutDatabase.getDatabase(requireContext()).donutDao()
+
+ donutEntryViewModel = ViewModelProvider(this, ViewModelFactory(donutDao))
+ .get(DonutEntryViewModel::class.java)
+
+ val binding = DonutEntryDialogBinding.bind(view)
+
+ var donut: Donut? = null
+ val args: DonutEntryDialogFragmentArgs by navArgs()
+ val editingState =
+ if (args.itemId > 0) EditingState.EXISTING_DONUT
+ else EditingState.NEW_DONUT
+
+ // If we arrived here with an itemId of >= 0, then we are editing an existing item
+ if (editingState == EditingState.EXISTING_DONUT) {
+ // Request to edit an existing item, whose id was passed in as an argument.
+ // Retrieve that item and populate the UI with its details
+ donutEntryViewModel.get(args.itemId).observe(viewLifecycleOwner) { donutItem ->
+ binding.name.setText(donutItem.name)
+ binding.description.setText(donutItem.description)
+ binding.ratingBar.rating = donutItem.rating.toFloat()
+ donut = donutItem
+ }
+ }
+
+ // When the user clicks the Done button, use the data here to either update
+ // an existing item or create a new one
+ binding.doneButton.setOnClickListener {
+ // Grab these now since the Fragment may go away before the setupNotification
+ // lambda below is called
+ val context = requireContext().applicationContext
+ val navController = findNavController()
+
+ donutEntryViewModel.addData(
+ donut?.id ?: 0,
+ binding.name.text.toString(),
+ binding.description.text.toString(),
+ binding.ratingBar.rating.toInt()
+ ) { actualId ->
+ val arg = DonutEntryDialogFragmentArgs(actualId).toBundle()
+ val pendingIntent = navController
+ .createDeepLink()
+ .setDestination(R.id.donutEntryDialogFragment)
+ .setArguments(arg)
+ .createPendingIntent()
+
+ Notifier.postNotification(actualId, context, pendingIntent)
+ }
+ dismiss()
+ }
+
+ // User clicked the Cancel button; just exit the dialog without saving the data
+ binding.cancelButton.setOnClickListener {
+ dismiss()
+ }
+ }
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return DonutEntryDialogBinding.inflate(inflater, container, false).root
+ }
+}
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutEntryViewModel.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutEntryViewModel.kt
new file mode 100644
index 00000000..62ee3b68
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutEntryViewModel.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.liveData
+import androidx.lifecycle.viewModelScope
+import com.android.samples.donuttracker.storage.DonutDao
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+class DonutEntryViewModel(private val donutDao: DonutDao) : ViewModel() {
+
+ private var donutLiveData: LiveData? = null
+
+ fun get(id: Long): LiveData {
+ return donutLiveData ?: liveData {
+ emit(donutDao.get(id))
+ }.also {
+ donutLiveData = it
+ }
+ }
+
+ fun addData(
+ id: Long,
+ name: String,
+ description: String,
+ rating: Int,
+ setupNotification: (Long) -> Unit
+ ) {
+ val donut = Donut(id, name, description, rating)
+
+ CoroutineScope(Dispatchers.Main.immediate).launch {
+ var actualId = id
+
+ if (id > 0) {
+ update(donut)
+ } else {
+ actualId = insert(donut)
+ }
+
+ setupNotification(actualId)
+ }
+ }
+
+ private suspend fun insert(donut: Donut): Long {
+ return donutDao.insert(donut)
+ }
+
+ private fun update(donut: Donut) = viewModelScope.launch(Dispatchers.IO) {
+ donutDao.update(donut)
+ }
+}
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutList.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutList.kt
new file mode 100644
index 00000000..35952d4e
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutList.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.app.NotificationManagerCompat
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.observe
+import androidx.navigation.findNavController
+import androidx.navigation.fragment.findNavController
+import com.android.samples.donuttracker.databinding.DonutListBinding
+import com.android.samples.donuttracker.storage.DonutDatabase
+import kotlinx.android.synthetic.main.donut_list.*
+
+/**
+ * Fragment containing the RecyclerView which shows the current list of donuts being tracked.
+ */
+class DonutList : Fragment() {
+
+ private lateinit var donutListViewModel: DonutListViewModel
+
+ private val adapter = DonutListAdapter(
+ onEdit = { donut ->
+ findNavController().navigate(
+ DonutListDirections.actionDonutListToDonutEntryDialogFragment(donut.id)
+ )
+ },
+ onDelete = { donut ->
+ NotificationManagerCompat.from(requireContext()).cancel(donut.id.toInt())
+ donutListViewModel.delete(donut)
+ }
+ )
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ val binding = DonutListBinding.bind(view)
+ val donutDao = DonutDatabase.getDatabase(requireContext()).donutDao()
+ donutListViewModel = ViewModelProvider(this, ViewModelFactory(donutDao))
+ .get(DonutListViewModel::class.java)
+
+ donutListViewModel.donuts.observe(viewLifecycleOwner) { donuts ->
+ adapter.submitList(donuts)
+ }
+
+ recyclerView.adapter = adapter
+
+ binding.fab.setOnClickListener { fabView ->
+ fabView.findNavController().navigate(
+ DonutListDirections.actionDonutListToDonutEntryDialogFragment()
+ )
+ }
+ }
+
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ return DonutListBinding.inflate(inflater, container, false).root
+ }
+}
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutListAdapter.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutListAdapter.kt
new file mode 100644
index 00000000..8c3a821c
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutListAdapter.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.android.samples.donuttracker.databinding.DonutItemBinding
+
+/**
+ * The adapter used by the RecyclerView to display the current list of donuts
+ */
+class DonutListAdapter(private var onEdit: (Donut) -> Unit, private var onDelete: (Donut) -> Unit) :
+ ListAdapter(DonutDiffCallback()) {
+
+ class DonutListViewHolder(
+ private val binding: DonutItemBinding,
+ private var onEdit: (Donut) -> Unit,
+ private var onDelete: (Donut) -> Unit
+ ) : RecyclerView.ViewHolder(binding.root) {
+ private var donutId: Long = -1
+ private var nameView = binding.name
+ private var description = binding.description
+ private var thumbnail = binding.thumbnail
+ private var rating = binding.rating
+ private var donut: Donut? = null
+
+ fun bind(donut: Donut) {
+ donutId = donut.id
+ nameView.text = donut.name
+ description.text = donut.description
+ rating.text = donut.rating.toString()
+ thumbnail.setImageResource(R.drawable.donut_with_sprinkles)
+ this.donut = donut
+ binding.deleteButton.setOnClickListener {
+ onDelete(donut)
+ }
+ binding.root.setOnClickListener {
+ onEdit(donut)
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DonutListViewHolder {
+
+ return DonutListViewHolder(
+ DonutItemBinding.inflate(LayoutInflater.from(parent.context), parent, false),
+ onEdit,
+ onDelete
+ )
+ }
+
+ override fun onBindViewHolder(holder: DonutListViewHolder, position: Int) {
+ holder.bind(getItem(position))
+ }
+}
+
+class DonutDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: Donut, newItem: Donut): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(oldItem: Donut, newItem: Donut): Boolean {
+ return oldItem == newItem
+ }
+}
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutListViewModel.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutListViewModel.kt
new file mode 100644
index 00000000..0e65efe1
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/DonutListViewModel.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import com.android.samples.donuttracker.storage.DonutDao
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+/**
+ * This ViewModel is used to access the underlying data and to observe changes to it.
+ */
+class DonutListViewModel(private val donutDao: DonutDao) : ViewModel() {
+
+ // Users of this ViewModel will observe changes to its donuts list to know when
+ // to redisplay those changes
+ val donuts: LiveData> = donutDao.getAll()
+
+ fun delete(donut: Donut) = viewModelScope.launch(Dispatchers.IO) {
+ donutDao.delete(donut)
+ }
+}
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/MainActivity.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/MainActivity.kt
new file mode 100644
index 00000000..8ca72eff
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/MainActivity.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.Menu
+import android.view.MenuItem
+import androidx.appcompat.app.AppCompatActivity
+import com.android.samples.donuttracker.databinding.ActivityMainBinding
+
+/**
+ * Main activity class. Not much happens here, just some basic UI setup.
+ * The main logic occurs in the fragments which populate this activity.
+ */
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val binding = ActivityMainBinding.inflate(LayoutInflater.from(this))
+ setContentView(binding.root)
+ setSupportActionBar(binding.toolbar)
+
+ Notifier.init(this)
+ }
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ menuInflater.inflate(R.menu.menu_main, menu)
+ return true
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ return when (item.itemId) {
+ R.id.action_settings -> true
+ else -> super.onOptionsItemSelected(item)
+ }
+ }
+}
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/Notifier.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/Notifier.kt
new file mode 100644
index 00000000..1f983fdb
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/Notifier.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker
+
+import android.app.Activity
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.os.Build
+import androidx.appcompat.app.AppCompatActivity
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+
+/**
+ * Utility class for posting notifications.
+ * This class creates the notification channel (as necessary) and posts to it when requested.
+ */
+object Notifier {
+
+ private const val channelId = "Default"
+
+ fun init(activity: Activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ val notificationManager =
+ activity.getSystemService(AppCompatActivity.NOTIFICATION_SERVICE) as NotificationManager
+ val existingChannel = notificationManager.getNotificationChannel(channelId)
+ if (existingChannel == null) {
+ // Create the NotificationChannel
+ val name = activity.getString(R.string.defaultChannel)
+ val importance = NotificationManager.IMPORTANCE_DEFAULT
+ val mChannel = NotificationChannel(channelId, name, importance)
+ mChannel.description = activity.getString(R.string.notificationDescription)
+ notificationManager.createNotificationChannel(mChannel)
+ }
+ }
+ }
+
+ fun postNotification(id: Long, context: Context, intent: PendingIntent) {
+ val builder = NotificationCompat.Builder(context, channelId)
+ builder.setContentTitle(context.getString(R.string.deepLinkNotificationTitle))
+ .setSmallIcon(R.drawable.donut_with_sprinkles)
+ val text = context.getString(R.string.addDonutInfo)
+ val notification = builder.setContentText(text)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setContentIntent(intent)
+ .setAutoCancel(true)
+ .build()
+ val notificationManager = NotificationManagerCompat.from(context)
+ // Remove prior notifications; only allow one at a time to edit the latest item
+ notificationManager.cancelAll()
+ notificationManager.notify(id.toInt(), notification)
+ }
+}
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/ViewModelFactory.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/ViewModelFactory.kt
new file mode 100644
index 00000000..5c87c736
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/ViewModelFactory.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.android.samples.donuttracker.storage.DonutDao
+
+class ViewModelFactory(private val donutDao: DonutDao) : ViewModelProvider.Factory {
+
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(DonutListViewModel::class.java)) {
+ @Suppress("UNCHECKED_CAST")
+ return DonutListViewModel(donutDao) as T
+ } else if (modelClass.isAssignableFrom(DonutEntryViewModel::class.java)) {
+ @Suppress("UNCHECKED_CAST")
+ return DonutEntryViewModel(donutDao) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/storage/DonutDao.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/storage/DonutDao.kt
new file mode 100644
index 00000000..b6857d81
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/storage/DonutDao.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker.storage
+
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Delete
+import androidx.room.Insert
+import androidx.room.Query
+import androidx.room.Update
+import com.android.samples.donuttracker.Donut
+
+/**
+ * The Data Access Object used to retrieve and store data from/to the underlying database.
+ * This API is not used directly; instead, callers should use the Repository which calls into
+ * this DAO.
+ */
+@Dao
+interface DonutDao {
+ @Query("SELECT * FROM donut")
+ fun getAll(): LiveData>
+
+ @Query("SELECT * FROM donut WHERE id = :id")
+ suspend fun get(id: Long): Donut
+
+ @Insert
+ suspend fun insert(donut: Donut): Long
+
+ @Delete
+ suspend fun delete(donut: Donut)
+
+ @Update
+ suspend fun update(donut: Donut)
+}
diff --git a/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/storage/DonutDatabase.kt b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/storage/DonutDatabase.kt
new file mode 100644
index 00000000..91fbe2bc
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/java/com/android/samples/donuttracker/storage/DonutDatabase.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+package com.android.samples.donuttracker.storage
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import com.android.samples.donuttracker.Donut
+
+/**
+ * The underlying database where information about the donuts is stored.
+ */
+@Database(entities = arrayOf(Donut::class), version = 1)
+abstract class DonutDatabase : RoomDatabase() {
+
+ abstract fun donutDao(): DonutDao
+
+ companion object {
+ @Volatile private var INSTANCE: DonutDatabase? = null
+
+ fun getDatabase(context: Context): DonutDatabase {
+ val tempInstance =
+ INSTANCE
+ if (tempInstance != null) {
+ return tempInstance
+ }
+ synchronized(this) {
+ val instance = Room.databaseBuilder(
+ context,
+ DonutDatabase::class.java,
+ "donut_database"
+ ).build()
+ INSTANCE = instance
+ return instance
+ }
+ }
+ }
+}
diff --git a/MADSkillsNavigationSample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/MADSkillsNavigationSample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
new file mode 100644
index 00000000..81a0b812
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/drawable-v24/ic_launcher_foreground.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/src/main/res/drawable/donut_with_sprinkles.xml b/MADSkillsNavigationSample/app/src/main/res/drawable/donut_with_sprinkles.xml
new file mode 100644
index 00000000..6bcafd9f
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/drawable/donut_with_sprinkles.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/drawable/ic_clear_24px.xml b/MADSkillsNavigationSample/app/src/main/res/drawable/ic_clear_24px.xml
new file mode 100644
index 00000000..8336dad4
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/drawable/ic_clear_24px.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/drawable/ic_launcher_background.xml b/MADSkillsNavigationSample/app/src/main/res/drawable/ic_launcher_background.xml
new file mode 100644
index 00000000..9d462630
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/drawable/ic_launcher_background.xml
@@ -0,0 +1,185 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/layout/activity_main.xml b/MADSkillsNavigationSample/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 00000000..e1e57d75
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/src/main/res/layout/content_main.xml b/MADSkillsNavigationSample/app/src/main/res/layout/content_main.xml
new file mode 100644
index 00000000..305fe587
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/layout/content_main.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/layout/donut_entry_dialog.xml b/MADSkillsNavigationSample/app/src/main/res/layout/donut_entry_dialog.xml
new file mode 100644
index 00000000..80720353
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/layout/donut_entry_dialog.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/src/main/res/layout/donut_item.xml b/MADSkillsNavigationSample/app/src/main/res/layout/donut_item.xml
new file mode 100644
index 00000000..d42c1c32
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/layout/donut_item.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/src/main/res/layout/donut_list.xml b/MADSkillsNavigationSample/app/src/main/res/layout/donut_list.xml
new file mode 100644
index 00000000..09183873
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/layout/donut_list.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/src/main/res/menu/menu_main.xml b/MADSkillsNavigationSample/app/src/main/res/menu/menu_main.xml
new file mode 100644
index 00000000..78826baa
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/menu/menu_main.xml
@@ -0,0 +1,26 @@
+
+
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/MADSkillsNavigationSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 00000000..81fc0fb1
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/MADSkillsNavigationSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 00000000..81fc0fb1
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-hdpi/ic_launcher.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 00000000..a571e600
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 00000000..61da551c
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-mdpi/ic_launcher.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 00000000..c41dd285
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 00000000..db5080a7
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 00000000..6dba46da
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..da31a871
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 00000000..15ac6817
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..b216f2d3
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 00000000..f25a4197
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/MADSkillsNavigationSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 00000000..e96783cc
Binary files /dev/null and b/MADSkillsNavigationSample/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/MADSkillsNavigationSample/app/src/main/res/navigation/nav_graph.xml b/MADSkillsNavigationSample/app/src/main/res/navigation/nav_graph.xml
new file mode 100644
index 00000000..1150d2a7
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/navigation/nav_graph.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/values/colors.xml b/MADSkillsNavigationSample/app/src/main/res/values/colors.xml
new file mode 100644
index 00000000..0707a854
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/values/colors.xml
@@ -0,0 +1,21 @@
+
+
+
+ #6200EE
+ #3700B3
+ #03DAC5
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/values/dimens.xml b/MADSkillsNavigationSample/app/src/main/res/values/dimens.xml
new file mode 100644
index 00000000..69d62ac3
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/values/dimens.xml
@@ -0,0 +1,19 @@
+
+
+
+ 16dp
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/values/strings.xml b/MADSkillsNavigationSample/app/src/main/res/values/strings.xml
new file mode 100644
index 00000000..2604e132
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/values/strings.xml
@@ -0,0 +1,38 @@
+
+
+
+ Donut Tracker
+ Settings
+
+
+ New Donut
+ Create New Donut
+ New Donut Shortcut Disabled
+ Donut name
+ Description
+ Rating
+ delete
+ DonutTracker default channel
+ Notifications from DonutTracker
+ Add more donut info!
+ Deep Link
+ donutName
+ Picture of donut
+ Name
+ Done
+ Rating
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/values/styles.xml b/MADSkillsNavigationSample/app/src/main/res/values/styles.xml
new file mode 100644
index 00000000..a23e6b2b
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/values/styles.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MADSkillsNavigationSample/app/src/main/res/xml-v25/shortcuts.xml b/MADSkillsNavigationSample/app/src/main/res/xml-v25/shortcuts.xml
new file mode 100644
index 00000000..d3fe357b
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/xml-v25/shortcuts.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/app/src/main/res/xml/shortcuts.xml b/MADSkillsNavigationSample/app/src/main/res/xml/shortcuts.xml
new file mode 100644
index 00000000..1c8e7dfb
--- /dev/null
+++ b/MADSkillsNavigationSample/app/src/main/res/xml/shortcuts.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/build.gradle b/MADSkillsNavigationSample/build.gradle
new file mode 100644
index 00000000..ef73041b
--- /dev/null
+++ b/MADSkillsNavigationSample/build.gradle
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed 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.
+ */
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ ext.kotlin_version = '1.4.10'
+ repositories {
+ google()
+ jcenter()
+
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.0.1'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ def nav_version = "2.3.0"
+ classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
+
+ext {
+ roomVersion = '2.2.5'
+ archLifecycleVersion = '2.2.0'
+ coreTestingVersion = '2.1.0'
+ materialVersion = '1.2.1'
+ coroutinesVersion = '1.3.7'
+ navigationVersion = '2.3.0'
+ fragmentVersion = '1.2.5'
+ appCompatVersion = '1.2.0'
+ ktxVersion = '1.3.2'
+ constraintLayoutVersion = '2.0.2'
+ ktlintVersion = '0.37.2'
+}
\ No newline at end of file
diff --git a/MADSkillsNavigationSample/gradle.properties b/MADSkillsNavigationSample/gradle.properties
new file mode 100644
index 00000000..23339e0d
--- /dev/null
+++ b/MADSkillsNavigationSample/gradle.properties
@@ -0,0 +1,21 @@
+# Project-wide Gradle settings.
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
+# AndroidX package structure to make it clearer which packages are bundled with the
+# Android operating system, and which are packaged with your app's APK
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
+android.useAndroidX=true
+# Automatically convert third-party libraries to use AndroidX
+android.enableJetifier=true
+# Kotlin code style for this project: "official" or "obsolete":
+kotlin.code.style=official
diff --git a/MADSkillsNavigationSample/gradle/wrapper/gradle-wrapper.jar b/MADSkillsNavigationSample/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000..f6b961fd
Binary files /dev/null and b/MADSkillsNavigationSample/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/MADSkillsNavigationSample/gradle/wrapper/gradle-wrapper.properties b/MADSkillsNavigationSample/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000..b7a07cca
--- /dev/null
+++ b/MADSkillsNavigationSample/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 05 14:37:34 PDT 2020
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
diff --git a/MADSkillsNavigationSample/gradlew b/MADSkillsNavigationSample/gradlew
new file mode 100755
index 00000000..cccdd3d5
--- /dev/null
+++ b/MADSkillsNavigationSample/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/MADSkillsNavigationSample/gradlew.bat b/MADSkillsNavigationSample/gradlew.bat
new file mode 100644
index 00000000..e95643d6
--- /dev/null
+++ b/MADSkillsNavigationSample/gradlew.bat
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/MADSkillsNavigationSample/screenshot.png b/MADSkillsNavigationSample/screenshot.png
new file mode 100644
index 00000000..167b8338
Binary files /dev/null and b/MADSkillsNavigationSample/screenshot.png differ
diff --git a/MADSkillsNavigationSample/settings.gradle b/MADSkillsNavigationSample/settings.gradle
new file mode 100644
index 00000000..3bfff7b8
--- /dev/null
+++ b/MADSkillsNavigationSample/settings.gradle
@@ -0,0 +1,2 @@
+rootProject.name='Donut Tracker'
+include ':app'