diff --git a/RecyclerViewClickHandler/README.md b/RecyclerViewClickHandler/README.md
new file mode 100755
index 0000000..2df9584
--- /dev/null
+++ b/RecyclerViewClickHandler/README.md
@@ -0,0 +1,55 @@
+TrackMySleepQuality with RecyclerView - Solution Code for 7.4
+=============================================================
+
+Solution code for Android Kotlin Fundamentals Codelab 7.4 Interacting with RecyclerView items
+
+Introduction
+------------
+
+TrackMySleepQuality is an app for recording sleep data for each night.
+You can record a start and stop time, assign a quality rating, and clear the database.
+
+Learn how to make items in the RecyclerView clickable.
+Implement a click listener and navigate on click in your Android Kotlin app.
+
+Pre-requisites
+--------------
+
+You should be familiar with:
+
+* Building a basic user interface (UI) using an activity, fragments, and views.
+* Navigating between fragments, and using safeArgs to pass data between fragments.
+* Using view models, view model factories, transformations, and LiveData and their observers.
+* Creating a Room database, creating a DAO, and defining entities.
+* Using coroutines for database tasks and other long-running tasks.
+* How to implement a basic RecyclerView with an Adapter, ViewHolder, and item layout.
+* How to implement data binding for RecyclerView.
+* How to create and use binding adapters to transform data.
+* How to use GridLayoutManager.
+
+
+Getting Started
+---------------
+
+1. Download and run the app.
+
+License
+-------
+
+Copyright 2019 Google, 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.
+
diff --git a/RecyclerViewClickHandler/app/.gitignore b/RecyclerViewClickHandler/app/.gitignore
new file mode 100755
index 0000000..796b96d
--- /dev/null
+++ b/RecyclerViewClickHandler/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/RecyclerViewClickHandler/app/build.gradle b/RecyclerViewClickHandler/app/build.gradle
new file mode 100755
index 0000000..89f56de
--- /dev/null
+++ b/RecyclerViewClickHandler/app/build.gradle
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019, 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: 'kotlin-kapt'
+apply plugin: 'androidx.navigation.safeargs'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.example.android.trackmysleepqualityrecyclerview"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ // Enables data binding.
+ dataBinding {
+ enabled = true
+ }
+
+}
+
+dependencies {
+
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ // Support libraries
+ implementation "androidx.appcompat:appcompat:1.0.0"
+ implementation "androidx.fragment:fragment:1.0.0"
+ implementation "androidx.constraintlayout:constraintlayout:2.0.0-alpha2"
+
+ // Android KTX
+ implementation 'androidx.core:core-ktx:1.0.1'
+
+ // Room and Lifecycle dependencies
+ implementation "androidx.room:room-runtime:$room_version"
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ kapt "androidx.room:room-compiler:$room_version"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
+
+ // Coroutines
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
+
+ // Navigation
+ implementation "android.arch.navigation:navigation-fragment-ktx:$navigationVersion"
+ implementation "android.arch.navigation:navigation-ui-ktx:$navigationVersion"
+
+ // Testing
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
+}
+
diff --git a/RecyclerViewClickHandler/app/proguard-rules.pro b/RecyclerViewClickHandler/app/proguard-rules.pro
new file mode 100755
index 0000000..f1b4245
--- /dev/null
+++ b/RecyclerViewClickHandler/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/RecyclerViewClickHandler/app/src/main/AndroidManifest.xml b/RecyclerViewClickHandler/app/src/main/AndroidManifest.xml
new file mode 100755
index 0000000..58a0b98
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/ic_launcher_sleep_tracker-web.png b/RecyclerViewClickHandler/app/src/main/ic_launcher_sleep_tracker-web.png
new file mode 100755
index 0000000..668bfae
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/ic_launcher_sleep_tracker-web.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
new file mode 100755
index 0000000..f06095d
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_main)
+ }
+}
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/Util.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
new file mode 100755
index 0000000..bf2f542
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.annotation.SuppressLint
+import android.content.res.Resources
+import android.os.Build
+import android.text.Html
+import android.text.Spanned
+import android.view.View
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.trackmysleepquality.database.SleepNight
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+/**
+ * These functions create a formatted string that can be set in a TextView.
+ */
+
+private val ONE_MINUTE_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES)
+private val ONE_HOUR_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)
+
+/**
+ * Convert a duration to a formatted string for display.
+ *
+ * Examples:
+ *
+ * 6 seconds on Wednesday
+ * 2 minutes on Monday
+ * 40 hours on Thursday
+ *
+ * @param startTimeMilli the start of the interval
+ * @param endTimeMilli the end of the interval
+ * @param res resources used to load formatted strings
+ */
+fun convertDurationToFormatted(startTimeMilli: Long, endTimeMilli: Long, res: Resources): String {
+ val durationMilli = endTimeMilli - startTimeMilli
+ val weekdayString = SimpleDateFormat("EEEE", Locale.getDefault()).format(startTimeMilli)
+ return when {
+ durationMilli < ONE_MINUTE_MILLIS -> {
+ val seconds = TimeUnit.SECONDS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.seconds_length, seconds, weekdayString)
+ }
+ durationMilli < ONE_HOUR_MILLIS -> {
+ val minutes = TimeUnit.MINUTES.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.minutes_length, minutes, weekdayString)
+ }
+ else -> {
+ val hours = TimeUnit.HOURS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.hours_length, hours, weekdayString)
+ }
+ }
+}
+
+/**
+ * Returns a string representing the numeric quality rating.
+ */
+fun convertNumericQualityToString(quality: Int, resources: Resources): String {
+ var qualityString = resources.getString(R.string.three_ok)
+ when (quality) {
+ -1 -> qualityString = "--"
+ 0 -> qualityString = resources.getString(R.string.zero_very_bad)
+ 1 -> qualityString = resources.getString(R.string.one_poor)
+ 2 -> qualityString = resources.getString(R.string.two_soso)
+ 4 -> qualityString = resources.getString(R.string.four_pretty_good)
+ 5 -> qualityString = resources.getString(R.string.five_excellent)
+ }
+ return qualityString
+}
+
+
+/**
+ * Take the Long milliseconds returned by the system and stored in Room,
+ * and convert it to a nicely formatted string for display.
+ *
+ * EEEE - Display the long letter version of the weekday
+ * MMM - Display the letter abbreviation of the nmotny
+ * dd-yyyy - day in month and full year numerically
+ * HH:mm - Hours and minutes in 24hr format
+ */
+@SuppressLint("SimpleDateFormat")
+fun convertLongToDateString(systemTime: Long): String {
+ return SimpleDateFormat("EEEE MMM-dd-yyyy' Time: 'HH:mm")
+ .format(systemTime).toString()
+}
+
+/**
+ * Takes a list of SleepNights and converts and formats it into one string for display.
+ *
+ * For display in a TextView, we have to supply one string, and styles are per TextView, not
+ * applicable per word. So, we build a formatted string using HTML. This is handy, but we will
+ * learn a better way of displaying this data in a future lesson.
+ *
+ * @param nights - List of all SleepNights in the database.
+ * @param resources - Resources object for all the resources defined for our app.
+ *
+ * @return Spanned - An interface for text that has formatting attached to it.
+ * See: https://developer.android.com/reference/android/text/Spanned
+ */
+fun formatNights(nights: List, resources: Resources): Spanned {
+ val sb = StringBuilder()
+ sb.apply {
+ append(resources.getString(R.string.title))
+ nights.forEach {
+ append(" ")
+ append(resources.getString(R.string.start_time))
+ append("\t${convertLongToDateString(it.startTimeMilli)} ")
+ if (it.endTimeMilli != it.startTimeMilli) {
+ append(resources.getString(R.string.end_time))
+ append("\t${convertLongToDateString(it.endTimeMilli)} ")
+ append(resources.getString(R.string.quality))
+ append("\t${convertNumericQualityToString(it.sleepQuality, resources)} ")
+ append(resources.getString(R.string.hours_slept))
+ // Hours
+ append("\t ${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60 / 60}:")
+ // Minutes
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60}:")
+ // Seconds
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000}
")
+ }
+ }
+ }
+ // fromHtml is deprecated for target API without a flag, but since our minSDK is 19, we
+ // can't use the newer version, which requires minSDK of 24
+ //https://developer.android.com/reference/android/text/Html#fromHtml(java.lang.String,%20int)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return Html.fromHtml(sb.toString(), Html.FROM_HTML_MODE_LEGACY)
+ } else {
+ @Suppress("DEPRECATION")
+ return Html.fromHtml(sb.toString())
+ }
+}
+
+/**
+ * ViewHolder that holds a single [TextView].
+ *
+ * A ViewHolder holds a view for the [RecyclerView] as well as providing additional information
+ * to the RecyclerView such as where on the screen it was last drawn during scrolling.
+ */
+class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
new file mode 100755
index 0000000..483642b
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+
+/**
+ * A database that stores SleepNight information.
+ * And a global method to get access to the database.
+ *
+ * This pattern is pretty much the same for any database,
+ * so you can reuse it.
+ */
+@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
+abstract class SleepDatabase : RoomDatabase() {
+
+ /**
+ * Connects the database to the DAO.
+ */
+ abstract val sleepDatabaseDao: SleepDatabaseDao
+
+ /**
+ * Define a companion object, this allows us to add functions on the SleepDatabase class.
+ *
+ * For example, clients can call `SleepDatabase.getInstance(context)` to instantiate
+ * a new SleepDatabase.
+ */
+ companion object {
+ /**
+ * INSTANCE will keep a reference to any database returned via getInstance.
+ *
+ * This will help us avoid repeatedly initializing the database, which is expensive.
+ *
+ * The value of a volatile variable will never be cached, and all writes and
+ * reads will be done to and from the main memory. It means that changes made by one
+ * thread to shared data are visible to other threads.
+ */
+ @Volatile
+ private var INSTANCE: SleepDatabase? = null
+
+ /**
+ * Helper function to get the database.
+ *
+ * If a database has already been retrieved, the previous database will be returned.
+ * Otherwise, create a new database.
+ *
+ * This function is threadsafe, and callers should cache the result for multiple database
+ * calls to avoid overhead.
+ *
+ * This is an example of a simple Singleton pattern that takes another Singleton as an
+ * argument in Kotlin.
+ *
+ * To learn more about Singleton read the wikipedia article:
+ * https://en.wikipedia.org/wiki/Singleton_pattern
+ *
+ * @param context The application context Singleton, used to get access to the filesystem.
+ */
+ fun getInstance(context: Context): SleepDatabase {
+ // Multiple threads can ask for the database at the same time, ensure we only initialize
+ // it once by using synchronized. Only one thread may enter a synchronized block at a
+ // time.
+ synchronized(this) {
+
+ // Copy the current value of INSTANCE to a local variable so Kotlin can smart cast.
+ // Smart cast is only available to local variables.
+ var instance = INSTANCE
+
+ // If instance is `null` make a new database instance.
+ if (instance == null) {
+ instance = Room.databaseBuilder(
+ context.applicationContext,
+ SleepDatabase::class.java,
+ "sleep_history_database"
+ )
+ // Wipes and rebuilds instead of migrating if no Migration object.
+ // Migration is not part of this lesson. You can learn more about
+ // migration with Room in this blog post:
+ // https://medium.com/androiddevelopers/understanding-migrations-with-room-f01e04b07929
+ .fallbackToDestructiveMigration()
+ .build()
+ // Assign INSTANCE to the newly created database.
+ INSTANCE = instance
+ }
+
+ // Return instance; smart cast to be non-null.
+ return instance
+ }
+ }
+ }
+}
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
new file mode 100755
index 0000000..39c5444
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Update
+
+
+/**
+ * Defines methods for using the SleepNight class with Room.
+ */
+@Dao
+interface SleepDatabaseDao {
+
+ @Insert
+ fun insert(night: SleepNight)
+
+ /**
+ * When updating a row with a value already set in a column,
+ * replaces the old value with the new one.
+ *
+ * @param night new value to write
+ */
+ @Update
+ fun update(night: SleepNight)
+
+ /**
+ * Selects and returns the row that matches the supplied start time, which is our key.
+ *
+ * @param key startTimeMilli to match
+ */
+ @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
+ fun get(key: Long): SleepNight
+
+ /**
+ * Deletes all values from the table.
+ *
+ * This does not delete the table, only its contents.
+ */
+ @Query("DELETE FROM daily_sleep_quality_table")
+ fun clear()
+
+ /**
+ * Selects and returns all rows in the table,
+ *
+ * sorted by start time in descending order.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
+ fun getAllNights(): LiveData>
+
+ /**
+ * Selects and returns the latest night.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
+ fun getTonight(): SleepNight?
+
+ /**
+ * Selects and returns the night with given nightId.
+ */
+ @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
+ fun getNightWithId(key: Long): LiveData
+}
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
new file mode 100755
index 0000000..b776812
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * Represents one night's sleep through start, end times, and the sleep quality.
+ */
+@Entity(tableName = "daily_sleep_quality_table")
+data class SleepNight(
+ @PrimaryKey(autoGenerate = true)
+ var nightId: Long = 0L,
+
+ @ColumnInfo(name = "start_time_milli")
+ val startTimeMilli: Long = System.currentTimeMillis(),
+
+ @ColumnInfo(name = "end_time_milli")
+ var endTimeMilli: Long = startTimeMilli,
+
+ @ColumnInfo(name = "quality_rating")
+ var sleepQuality: Int = -1)
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailFragment.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailFragment.kt
new file mode 100644
index 0000000..81b3310
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailFragment.kt
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepdetail
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepDetailBinding
+
+
+/**
+ * A simple [Fragment] subclass.
+ * Activities that contain this fragment must implement the
+ * [SleepDetailFragment.OnFragmentInteractionListener] interface
+ * to handle interaction events.
+ * Use the [SleepDetailFragment.newInstance] factory method to
+ * create an instance of this fragment.
+ *
+ */
+class SleepDetailFragment : Fragment() {
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepDetailBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_detail, container, false)
+
+ val application = requireNotNull(this.activity).application
+ val arguments = SleepDetailFragmentArgs.fromBundle(arguments)
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepDetailViewModelFactory(arguments.sleepNightKey, dataSource)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepDetailViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepDetailViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepDetailViewModel = sleepDetailViewModel
+
+ binding.setLifecycleOwner(this)
+
+ // Add an Observer to the state variable for Navigating when a Quality icon is tapped.
+ sleepDetailViewModel.navigateToSleepTracker.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ this.findNavController().navigate(
+ SleepDetailFragmentDirections.actionSleepDetailFragmentToSleepTrackerFragment())
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepDetailViewModel.doneNavigating()
+ }
+ })
+
+ return binding.root
+ }
+
+
+}
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModel.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModel.kt
new file mode 100644
index 0000000..e449a94
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModel.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepdetail
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import com.example.android.trackmysleepquality.database.SleepNight
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepQualityFragment.
+ *
+ * @param sleepNightKey The key of the current night we are working on.
+ */
+class SleepDetailViewModel(
+ private val sleepNightKey: Long = 0L,
+ dataSource: SleepDatabaseDao) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via its SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine setup variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private val viewModelJob = Job()
+
+ private val night: LiveData
+
+ fun getNight() = night
+
+
+ init {
+ night=database.getNightWithId(sleepNightKey)
+ }
+
+ /**
+ * Variable that tells the fragment whether it should navigate to [SleepTrackerFragment].
+ *
+ * This is `private` because we don't want to expose the ability to set [MutableLiveData] to
+ * the [Fragment]
+ */
+ private val _navigateToSleepTracker = MutableLiveData()
+
+ /**
+ * When true immediately navigate back to the [SleepTrackerFragment]
+ */
+ val navigateToSleepTracker: LiveData
+ get() = _navigateToSleepTracker
+
+ /**
+ * Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work.
+ *
+ * onCleared() gets called when the ViewModel is destroyed.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+
+ /**
+ * Call this immediately after navigating to [SleepTrackerFragment]
+ */
+ fun doneNavigating() {
+ _navigateToSleepTracker.value = null
+ }
+
+ fun onClose() {
+ _navigateToSleepTracker.value = true
+ }
+
+}
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModelFactory.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModelFactory.kt
new file mode 100644
index 0000000..fb59a98
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModelFactory.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepdetail
+
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the key for the night and the SleepDatabaseDao to the ViewModel.
+ */
+class SleepDetailViewModelFactory(
+ private val sleepNightKey: Long,
+ private val dataSource: SleepDatabaseDao) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepDetailViewModel::class.java)) {
+ return SleepDetailViewModel(sleepNightKey, dataSource) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
new file mode 100755
index 0000000..db22082
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepQualityBinding
+
+/**
+ * Fragment that displays a list of clickable icons,
+ * each representing a sleep quality rating.
+ * Once the user taps an icon, the quality is set in the current sleepNight
+ * and the database is updated.
+ */
+class SleepQualityFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepQualityViewModel] to the
+ * [FragmentSleepQualityBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepQualityBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_quality, container, false)
+
+ val application = requireNotNull(this.activity).application
+ val arguments = SleepQualityFragmentArgs.fromBundle(arguments)
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepQualityViewModelFactory(arguments.sleepNightKey, dataSource)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepQualityViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepQualityViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepQualityViewModel = sleepQualityViewModel
+
+ // Add an Observer to the state variable for Navigating when a Quality icon is tapped.
+ sleepQualityViewModel.navigateToSleepTracker.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ this.findNavController().navigate(
+ SleepQualityFragmentDirections.actionSleepQualityFragmentToSleepTrackerFragment())
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepQualityViewModel.doneNavigating()
+ }
+ })
+
+ return binding.root
+ }
+}
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
new file mode 100755
index 0000000..52ce4d8
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepQualityFragment.
+ *
+ * @param sleepNightKey The key of the current night we are working on.
+ */
+class SleepQualityViewModel(
+ private val sleepNightKey: Long = 0L,
+ dataSource: SleepDatabaseDao) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via its SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine setup variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private val viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this scope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ /**
+ * Variable that tells the fragment whether it should navigate to [SleepTrackerFragment].
+ *
+ * This is `private` because we don't want to expose the ability to set [MutableLiveData] to
+ * the [Fragment]
+ */
+ private val _navigateToSleepTracker = MutableLiveData()
+
+ /**
+ * When true immediately navigate back to the [SleepTrackerFragment]
+ */
+ val navigateToSleepTracker: LiveData
+ get() = _navigateToSleepTracker
+
+ /**
+ * Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work.
+ *
+ * onCleared() gets called when the ViewModel is destroyed.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+ /**
+ * Call this immediately after navigating to [SleepTrackerFragment]
+ */
+ fun doneNavigating() {
+ _navigateToSleepTracker.value = null
+ }
+
+ /**
+ * Sets the sleep quality and updates the database.
+ *
+ * Then navigates back to the SleepTrackerFragment.
+ */
+ fun onSetSleepQuality(quality: Int) {
+ uiScope.launch {
+ // IO is a thread pool for running operations that access the disk, such as
+ // our Room database.
+ withContext(Dispatchers.IO) {
+ val tonight = database.get(sleepNightKey)
+ tonight.sleepQuality = quality
+ database.update(tonight)
+ }
+
+ // Setting this state variable to true will alert the observer and trigger navigation.
+ _navigateToSleepTracker.value = true
+ }
+ }
+}
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
new file mode 100755
index 0000000..3c6a35d
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the key for the night and the SleepDatabaseDao to the ViewModel.
+ */
+class SleepQualityViewModelFactory(
+ private val sleepNightKey: Long,
+ private val dataSource: SleepDatabaseDao) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepQualityViewModel::class.java)) {
+ return SleepQualityViewModel(sleepNightKey, dataSource) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt
new file mode 100644
index 0000000..b5089e0
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.databinding.BindingAdapter
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.convertDurationToFormatted
+import com.example.android.trackmysleepquality.convertNumericQualityToString
+import com.example.android.trackmysleepquality.database.SleepNight
+
+
+@BindingAdapter("sleepDurationFormatted")
+fun TextView.setSleepDurationFormatted(item: SleepNight?) {
+ item?.let {
+ text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
+ }
+}
+
+
+@BindingAdapter("sleepQualityString")
+fun TextView.setSleepQualityString(item: SleepNight?) {
+ item?.let {
+ text = convertNumericQualityToString(item.sleepQuality, context.resources)
+ }
+}
+
+
+@BindingAdapter("sleepImage")
+fun ImageView.setSleepImage(item: SleepNight?) {
+ item?.let {
+ setImageResource(when (item.sleepQuality) {
+ 0 -> R.drawable.ic_sleep_0
+ 1 -> R.drawable.ic_sleep_1
+ 2 -> R.drawable.ic_sleep_2
+
+ 3 -> R.drawable.ic_sleep_3
+
+ 4 -> R.drawable.ic_sleep_4
+ 5 -> R.drawable.ic_sleep_5
+ else -> R.drawable.ic_sleep_active
+ })
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
new file mode 100644
index 0000000..9382360
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+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.example.android.trackmysleepquality.database.SleepNight
+import com.example.android.trackmysleepquality.databinding.ListItemSleepNightBinding
+
+class SleepNightAdapter(val clickListener: SleepNightListener) : ListAdapter(SleepNightDiffCallback()) {
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ holder.bind(getItem(position)!!, clickListener)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ return ViewHolder.from(parent)
+ }
+
+ class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){
+
+ fun bind(item: SleepNight, clickListener: SleepNightListener) {
+ binding.sleep = item
+ binding.clickListener = clickListener
+ binding.executePendingBindings()
+ }
+
+ companion object {
+ fun from(parent: ViewGroup): ViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ val binding = ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
+ return ViewHolder(binding)
+ }
+ }
+ }
+}
+
+
+class SleepNightDiffCallback : DiffUtil.ItemCallback() {
+
+ override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
+ return oldItem.nightId == newItem.nightId
+ }
+
+ override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
+ return oldItem == newItem
+ }
+}
+
+
+class SleepNightListener(val clickListener: (sleepId: Long) -> Unit) {
+ fun onClick(night: SleepNight) = clickListener(night.nightId)
+}
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
new file mode 100755
index 0000000..10d7d34
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
@@ -0,0 +1,140 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.GridLayoutManager
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepTrackerBinding
+import com.google.android.material.snackbar.Snackbar
+
+/**
+ * A fragment with buttons to record start and end times for sleep, which are saved in
+ * a database. Cumulative data is displayed in a simple scrollable TextView.
+ * (Because we have not learned about RecyclerView yet.)
+ * The Clear button will clear all data from the database.
+ */
+class SleepTrackerFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepTrackerViewModel] to the
+ * [FragmentSleepTrackerBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_tracker, container, false)
+
+ val application = requireNotNull(this.activity).application
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepTrackerViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepTrackerViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepTrackerViewModel = sleepTrackerViewModel
+
+ val adapter = SleepNightAdapter(SleepNightListener { nightId ->
+ //Toast.makeText(context, "${nightId}", Toast.LENGTH_LONG).show()
+ sleepTrackerViewModel.onSleepNightClicked(nightId)
+ })
+ binding.sleepList.adapter = adapter
+
+
+ sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
+ it?.let {
+ adapter.submitList(it)
+ }
+ })
+
+ // Specify the current activity as the lifecycle owner of the binding.
+ // This is necessary so that the binding can observe LiveData updates.
+ binding.setLifecycleOwner(this)
+
+ // Add an Observer on the state variable for showing a Snackbar message
+ // when the CLEAR button is pressed.
+ sleepTrackerViewModel.showSnackBarEvent.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ Snackbar.make(
+ activity!!.findViewById(android.R.id.content),
+ getString(R.string.cleared_message),
+ Snackbar.LENGTH_SHORT // How long to display the message.
+ ).show()
+ // Reset state to make sure the toast is only shown once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneShowingSnackbar()
+ }
+ })
+
+ // Add an Observer on the state variable for Navigating when STOP button is pressed.
+ sleepTrackerViewModel.navigateToSleepQuality.observe(this, Observer { night ->
+ night?.let {
+ // We need to get the navController from this, because button is not ready, and it
+ // just has to be a view. For some reason, this only matters if we hit stop again
+ // after using the back button, not if we hit stop and choose a quality.
+ // Also, in the Navigation Editor, for Quality -> Tracker, check "Inclusive" for
+ // popping the stack to get the correct behavior if we press stop multiple times
+ // followed by back.
+ // Also: https://stackoverflow.com/questions/28929637/difference-and-uses-of-oncreate-oncreateview-and-onactivitycreated-in-fra
+ this.findNavController().navigate(
+ SleepTrackerFragmentDirections
+ .actionSleepTrackerFragmentToSleepQualityFragment(night.nightId))
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneNavigating()
+ }
+ })
+
+ // Add an Observer on the state variable for Navigating when and item is clicked.
+ sleepTrackerViewModel.navigateToSleepDetail.observe(this, Observer { night ->
+ night?.let {
+
+ this.findNavController().navigate(
+ SleepTrackerFragmentDirections
+ .actionSleepTrackerFragmentToSleepDetailFragment(night))
+ sleepTrackerViewModel.onSleepDetailNavigated()
+ }
+ })
+
+ val manager = GridLayoutManager(activity, 3)
+ binding.sleepList.layoutManager = manager
+
+ return binding.root
+ }
+}
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
new file mode 100755
index 0000000..a42e69f
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.app.Application
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Transformations
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import com.example.android.trackmysleepquality.database.SleepNight
+import com.example.android.trackmysleepquality.formatNights
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepTrackerFragment.
+ */
+class SleepTrackerViewModel(
+ dataSource: SleepDatabaseDao,
+ application: Application) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private var viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this uiScope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ private var tonight = MutableLiveData()
+
+ val nights = database.getAllNights()
+
+ /**
+ * Converted nights to Spanned for displaying.
+ */
+ val nightsString = Transformations.map(nights) { nights ->
+ formatNights(nights, application.resources)
+ }
+
+ /**
+ * If tonight has not been set, then the START button should be visible.
+ */
+ val startButtonVisible = Transformations.map(tonight) {
+ null == it
+ }
+
+ /**
+ * If tonight has been set, then the STOP button should be visible.
+ */
+ val stopButtonVisible = Transformations.map(tonight) {
+ null != it
+ }
+
+ /**
+ * If there are any nights in the database, show the CLEAR button.
+ */
+ val clearButtonVisible = Transformations.map(nights) {
+ it?.isNotEmpty()
+ }
+
+ /**
+ * Request a toast by setting this value to true.
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private var _showSnackbarEvent = MutableLiveData()
+
+ /**
+ * If this is true, immediately `show()` a toast and call `doneShowingSnackbar()`.
+ */
+ val showSnackBarEvent: LiveData
+ get() = _showSnackbarEvent
+
+ /**
+ * Variable that tells the Fragment to navigate to a specific [SleepQualityFragment]
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private val _navigateToSleepQuality = MutableLiveData()
+
+ /**
+ * If this is non-null, immediately navigate to [SleepQualityFragment] and call [doneNavigating]
+ */
+ val navigateToSleepQuality: LiveData
+ get() = _navigateToSleepQuality
+
+ /**
+ * Call this immediately after calling `show()` on a toast.
+ *
+ * It will clear the toast request, so if the user rotates their phone it won't show a duplicate
+ * toast.
+ */
+ fun doneShowingSnackbar() {
+ _showSnackbarEvent.value = null
+ }
+
+ /**
+ * Call this immediately after navigating to [SleepQualityFragment]
+ *
+ * It will clear the navigation request, so if the user rotates their phone it won't navigate
+ * twice.
+ */
+ fun doneNavigating() {
+ _navigateToSleepQuality.value = null
+ }
+
+ /**
+ * Navigation for the SleepDetail fragment.
+ */
+ private val _navigateToSleepDetail = MutableLiveData()
+ val navigateToSleepDetail
+ get() = _navigateToSleepDetail
+
+ fun onSleepNightClicked(id: Long) {
+ _navigateToSleepDetail.value = id
+ }
+
+ fun onSleepDetailNavigated() {
+ _navigateToSleepDetail.value = null
+ }
+
+ init {
+ initializeTonight()
+ }
+
+ private fun initializeTonight() {
+ uiScope.launch {
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Handling the case of the stopped app or forgotten recording,
+ * the start and end times will be the same.j
+ *
+ * If the start time and end time are not the same, then we do not have an unfinished
+ * recording.
+ */
+ private suspend fun getTonightFromDatabase(): SleepNight? {
+ return withContext(Dispatchers.IO) {
+ var night = database.getTonight()
+ if (night?.endTimeMilli != night?.startTimeMilli) {
+ night = null
+ }
+ night
+ }
+ }
+
+ private suspend fun insert(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.insert(night)
+ }
+ }
+
+ private suspend fun update(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.update(night)
+ }
+ }
+
+ private suspend fun clear() {
+ withContext(Dispatchers.IO) {
+ database.clear()
+ }
+ }
+
+ /**
+ * Executes when the START button is clicked.
+ */
+ fun onStart() {
+ uiScope.launch {
+ // Create a new night, which captures the current time,
+ // and insert it into the database.
+ val newNight = SleepNight()
+
+ insert(newNight)
+
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Executes when the STOP button is clicked.
+ */
+ fun onStop() {
+ uiScope.launch {
+ // In Kotlin, the return@label syntax is used for specifying which function among
+ // several nested ones this statement returns from.
+ // In this case, we are specifying to return from launch().
+ val oldNight = tonight.value ?: return@launch
+
+ // Update the night in the database to add the end time.
+ oldNight.endTimeMilli = System.currentTimeMillis()
+
+ update(oldNight)
+
+ // Set state to navigate to the SleepQualityFragment.
+ _navigateToSleepQuality.value = oldNight
+ }
+ }
+
+ /**
+ * Executes when the CLEAR button is clicked.
+ */
+ fun onClear() {
+ uiScope.launch {
+ // Clear the database table.
+ clear()
+
+ // And clear tonight since it's no longer in the database
+ tonight.value = null
+
+ // Show a snackbar message, because it's friendly.
+ _showSnackbarEvent.value = true
+ }
+ }
+
+ /**
+ * Called when the ViewModel is dismantled.
+ * At this point, we want to cancel all coroutines;
+ * otherwise we end up with processes that have nowhere to return to
+ * using memory and resources.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
new file mode 100755
index 0000000..abcd8ac
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the SleepDatabaseDao and context to the ViewModel.
+ */
+class SleepTrackerViewModelFactory(
+ private val dataSource: SleepDatabaseDao,
+ private val application: Application) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
+ return SleepTrackerViewModel(dataSource, application) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewClickHandler/app/src/main/res/anim/slide_in_right.xml b/RecyclerViewClickHandler/app/src/main/res/anim/slide_in_right.xml
new file mode 100755
index 0000000..49f7779
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/anim/slide_in_right.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
new file mode 100755
index 0000000..0b1f6bd
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_0.xml b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_0.xml
new file mode 100755
index 0000000..91c0b47
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_0.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_1.xml b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_1.xml
new file mode 100755
index 0000000..8cc34f7
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_1.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_2.xml b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_2.xml
new file mode 100755
index 0000000..0b7b818
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_2.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_3.xml b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_3.xml
new file mode 100755
index 0000000..677b9ae
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_3.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_4.xml b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_4.xml
new file mode 100755
index 0000000..39eab52
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_4.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_5.xml b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_5.xml
new file mode 100755
index 0000000..f23a2ae
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_5.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_active.xml b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_active.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/drawable/ic_sleep_active.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/font/roboto.xml b/RecyclerViewClickHandler/app/src/main/res/font/roboto.xml
new file mode 100755
index 0000000..2641caf
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/font/roboto.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/layout/activity_main.xml b/RecyclerViewClickHandler/app/src/main/res/layout/activity_main.xml
new file mode 100755
index 0000000..a21679c
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/layout/fragment_sleep_detail.xml b/RecyclerViewClickHandler/app/src/main/res/layout/fragment_sleep_detail.xml
new file mode 100644
index 0000000..a4a70ca
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/layout/fragment_sleep_detail.xml
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/res/layout/fragment_sleep_quality.xml b/RecyclerViewClickHandler/app/src/main/res/layout/fragment_sleep_quality.xml
new file mode 100755
index 0000000..ab0ba0c
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/layout/fragment_sleep_quality.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/layout/fragment_sleep_tracker.xml b/RecyclerViewClickHandler/app/src/main/res/layout/fragment_sleep_tracker.xml
new file mode 100755
index 0000000..d21055b
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/layout/fragment_sleep_tracker.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/layout/list_item_sleep_night.xml b/RecyclerViewClickHandler/app/src/main/res/layout/list_item_sleep_night.xml
new file mode 100755
index 0000000..f762e15
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/layout/list_item_sleep_night.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/res/layout/list_item_sleep_night_linear.xml b/RecyclerViewClickHandler/app/src/main/res/layout/list_item_sleep_night_linear.xml
new file mode 100755
index 0000000..8d3f008
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/layout/list_item_sleep_night_linear.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/res/layout/text_item_view.xml b/RecyclerViewClickHandler/app/src/main/res/layout/text_item_view.xml
new file mode 100644
index 0000000..855c9d6
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/layout/text_item_view.xml
@@ -0,0 +1,22 @@
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml b/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml b/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..898f3ed
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dffca36
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..e5e92ac
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..ae7a1a2
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..64ba76f
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dae5e08
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1250b4c
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..5646308
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..e5ed465
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..14ed0af
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..fd07714
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..86c0074
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..b0907ca
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..d8ae031
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..ae4d8d3
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..37a4a99
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..2c18de9
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..beed3cd
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1fc200c
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..1125b8c
Binary files /dev/null and b/RecyclerViewClickHandler/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewClickHandler/app/src/main/res/navigation/navigation.xml b/RecyclerViewClickHandler/app/src/main/res/navigation/navigation.xml
new file mode 100755
index 0000000..3d387dd
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/navigation/navigation.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/values/colors.xml b/RecyclerViewClickHandler/app/src/main/res/values/colors.xml
new file mode 100755
index 0000000..fe8cd82
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ #6ab343
+ #388310
+ #6ab343
+
+ #388310
+ #f0f0f0
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/values/dimens.xml b/RecyclerViewClickHandler/app/src/main/res/values/dimens.xml
new file mode 100755
index 0000000..5385c2e
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/values/dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+ 16dp
+ 64dp
+ 20sp
+ 48dp
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/values/font_certs.xml b/RecyclerViewClickHandler/app/src/main/res/values/font_certs.xml
new file mode 100755
index 0000000..98db6fb
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/values/font_certs.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ @array/com_google_android_gms_fonts_certs_dev
+ @array/com_google_android_gms_fonts_certs_prod
+
+
+
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+
+
+
+
+ MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
+
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/values/preloaded_fonts.xml b/RecyclerViewClickHandler/app/src/main/res/values/preloaded_fonts.xml
new file mode 100755
index 0000000..0a2c03c
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/values/preloaded_fonts.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ @font/roboto
+
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/values/strings.xml b/RecyclerViewClickHandler/app/src/main/res/values/strings.xml
new file mode 100755
index 0000000..223c807
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/values/strings.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Track My Sleep Quality
+ SleepTrackerFragment
+ SleepQualityFragment
+
+
+ Start
+ Stop
+ Clear
+
+
+ Sleep Quality 0
+ Sleep Quality 1
+ Sleep Quality 2
+ Sleep Quality 3
+ Sleep Quality 4
+ Sleep Quality 5
+
+
+ HERE IS YOUR SLEEP DATA]]>
+ Start:]]>
+ End:]]>
+ Quality:]]>
+ Hours:Minutes:Seconds]]>
+
+
+ How was your sleep?
+ Very bad
+ Poor
+ So-so
+ OK
+ Pretty good
+ Excellent!
+
+
+ All your data is gone forever.
+
+ Sleep Results
+ Close
+ %d minutes on %s
+ %d hours on %s
+ %d seconds on %s
+
diff --git a/RecyclerViewClickHandler/app/src/main/res/values/styles.xml b/RecyclerViewClickHandler/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..76369f2
--- /dev/null
+++ b/RecyclerViewClickHandler/app/src/main/res/values/styles.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewClickHandler/build.gradle b/RecyclerViewClickHandler/build.gradle
new file mode 100755
index 0000000..cd8f0e1
--- /dev/null
+++ b/RecyclerViewClickHandler/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018, 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.3.10'
+ archLifecycleVersion = '1.1.1'
+ room_version = '2.0.0'
+ coroutine_version = '1.0.0'
+ gradleVersion = '3.4.0'
+ navigationVersion = '1.0.0-alpha07'
+ dataBindingCompilerVersion = gradleVersion // Always need to be the same.
+ }
+
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath "com.android.tools.build:gradle:$gradleVersion"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
+
+ // 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
+}
diff --git a/RecyclerViewClickHandler/gradle.properties b/RecyclerViewClickHandler/gradle.properties
new file mode 100755
index 0000000..9592636
--- /dev/null
+++ b/RecyclerViewClickHandler/gradle.properties
@@ -0,0 +1,15 @@
+# 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
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/RecyclerViewClickHandler/gradle/wrapper/gradle-wrapper.jar b/RecyclerViewClickHandler/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..f6b961f
Binary files /dev/null and b/RecyclerViewClickHandler/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/RecyclerViewClickHandler/gradle/wrapper/gradle-wrapper.properties b/RecyclerViewClickHandler/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..1c8c3db
--- /dev/null
+++ b/RecyclerViewClickHandler/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue May 14 17:22:57 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/RecyclerViewClickHandler/gradlew b/RecyclerViewClickHandler/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/RecyclerViewClickHandler/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/RecyclerViewClickHandler/gradlew.bat b/RecyclerViewClickHandler/gradlew.bat
new file mode 100755
index 0000000..e95643d
--- /dev/null
+++ b/RecyclerViewClickHandler/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/RecyclerViewClickHandler/settings.gradle b/RecyclerViewClickHandler/settings.gradle
new file mode 100755
index 0000000..e7b4def
--- /dev/null
+++ b/RecyclerViewClickHandler/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/RecyclerViewDiffUtilDataBinding/README.md b/RecyclerViewDiffUtilDataBinding/README.md
new file mode 100755
index 0000000..ec318f1
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/README.md
@@ -0,0 +1,55 @@
+TrackMySleepQuality with RecyclerView - Solution Code for 7.2
+============================================================
+
+Solution code for Android Kotlin Fundamentals Codelab 7.2 DiffUtil and data binding with RecyclerView
+
+Introduction
+------------
+
+TrackMySleepQuality is an app for recording sleep data for each night.
+You can record a start and stop time, assign a quality rating, and clear the database.
+
+Learn techniques that make RecyclerView more efficient for large lists.
+You will also learn techniques to make your code easier to maintain and extend for complex
+lists and grids in your Android Kotlin apps.
+
+
+Pre-requisites
+--------------
+
+You should be familiar with:
+
+* Building a basic user interface (UI) using an activity, fragments, and views.
+* Navigating between fragments, and using safeArgs to pass data between fragments.
+* Using view models, view model factories, transformations, and LiveData and their observers.
+* Creating a Room database, creating a DAO, and defining entities.
+* Using coroutines for database tasks and other long-running tasks.
+* How to implement a basic RecyclerView with an Adapter, ViewHolder, and item layout.
+
+
+
+Getting Started
+---------------
+
+1. Download and run the app.
+
+License
+-------
+
+Copyright 2019 Google, 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.
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/.gitignore b/RecyclerViewDiffUtilDataBinding/app/.gitignore
new file mode 100755
index 0000000..796b96d
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/RecyclerViewDiffUtilDataBinding/app/build.gradle b/RecyclerViewDiffUtilDataBinding/app/build.gradle
new file mode 100755
index 0000000..89f56de
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/build.gradle
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019, 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: 'kotlin-kapt'
+apply plugin: 'androidx.navigation.safeargs'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.example.android.trackmysleepqualityrecyclerview"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ // Enables data binding.
+ dataBinding {
+ enabled = true
+ }
+
+}
+
+dependencies {
+
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ // Support libraries
+ implementation "androidx.appcompat:appcompat:1.0.0"
+ implementation "androidx.fragment:fragment:1.0.0"
+ implementation "androidx.constraintlayout:constraintlayout:2.0.0-alpha2"
+
+ // Android KTX
+ implementation 'androidx.core:core-ktx:1.0.1'
+
+ // Room and Lifecycle dependencies
+ implementation "androidx.room:room-runtime:$room_version"
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ kapt "androidx.room:room-compiler:$room_version"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
+
+ // Coroutines
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
+
+ // Navigation
+ implementation "android.arch.navigation:navigation-fragment-ktx:$navigationVersion"
+ implementation "android.arch.navigation:navigation-ui-ktx:$navigationVersion"
+
+ // Testing
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
+}
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/proguard-rules.pro b/RecyclerViewDiffUtilDataBinding/app/proguard-rules.pro
new file mode 100755
index 0000000..f1b4245
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/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/RecyclerViewDiffUtilDataBinding/app/src/main/AndroidManifest.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/AndroidManifest.xml
new file mode 100755
index 0000000..58a0b98
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/ic_launcher_sleep_tracker-web.png b/RecyclerViewDiffUtilDataBinding/app/src/main/ic_launcher_sleep_tracker-web.png
new file mode 100755
index 0000000..668bfae
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/ic_launcher_sleep_tracker-web.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
new file mode 100755
index 0000000..f06095d
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_main)
+ }
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/Util.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
new file mode 100755
index 0000000..bf2f542
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.annotation.SuppressLint
+import android.content.res.Resources
+import android.os.Build
+import android.text.Html
+import android.text.Spanned
+import android.view.View
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.trackmysleepquality.database.SleepNight
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+/**
+ * These functions create a formatted string that can be set in a TextView.
+ */
+
+private val ONE_MINUTE_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES)
+private val ONE_HOUR_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)
+
+/**
+ * Convert a duration to a formatted string for display.
+ *
+ * Examples:
+ *
+ * 6 seconds on Wednesday
+ * 2 minutes on Monday
+ * 40 hours on Thursday
+ *
+ * @param startTimeMilli the start of the interval
+ * @param endTimeMilli the end of the interval
+ * @param res resources used to load formatted strings
+ */
+fun convertDurationToFormatted(startTimeMilli: Long, endTimeMilli: Long, res: Resources): String {
+ val durationMilli = endTimeMilli - startTimeMilli
+ val weekdayString = SimpleDateFormat("EEEE", Locale.getDefault()).format(startTimeMilli)
+ return when {
+ durationMilli < ONE_MINUTE_MILLIS -> {
+ val seconds = TimeUnit.SECONDS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.seconds_length, seconds, weekdayString)
+ }
+ durationMilli < ONE_HOUR_MILLIS -> {
+ val minutes = TimeUnit.MINUTES.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.minutes_length, minutes, weekdayString)
+ }
+ else -> {
+ val hours = TimeUnit.HOURS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.hours_length, hours, weekdayString)
+ }
+ }
+}
+
+/**
+ * Returns a string representing the numeric quality rating.
+ */
+fun convertNumericQualityToString(quality: Int, resources: Resources): String {
+ var qualityString = resources.getString(R.string.three_ok)
+ when (quality) {
+ -1 -> qualityString = "--"
+ 0 -> qualityString = resources.getString(R.string.zero_very_bad)
+ 1 -> qualityString = resources.getString(R.string.one_poor)
+ 2 -> qualityString = resources.getString(R.string.two_soso)
+ 4 -> qualityString = resources.getString(R.string.four_pretty_good)
+ 5 -> qualityString = resources.getString(R.string.five_excellent)
+ }
+ return qualityString
+}
+
+
+/**
+ * Take the Long milliseconds returned by the system and stored in Room,
+ * and convert it to a nicely formatted string for display.
+ *
+ * EEEE - Display the long letter version of the weekday
+ * MMM - Display the letter abbreviation of the nmotny
+ * dd-yyyy - day in month and full year numerically
+ * HH:mm - Hours and minutes in 24hr format
+ */
+@SuppressLint("SimpleDateFormat")
+fun convertLongToDateString(systemTime: Long): String {
+ return SimpleDateFormat("EEEE MMM-dd-yyyy' Time: 'HH:mm")
+ .format(systemTime).toString()
+}
+
+/**
+ * Takes a list of SleepNights and converts and formats it into one string for display.
+ *
+ * For display in a TextView, we have to supply one string, and styles are per TextView, not
+ * applicable per word. So, we build a formatted string using HTML. This is handy, but we will
+ * learn a better way of displaying this data in a future lesson.
+ *
+ * @param nights - List of all SleepNights in the database.
+ * @param resources - Resources object for all the resources defined for our app.
+ *
+ * @return Spanned - An interface for text that has formatting attached to it.
+ * See: https://developer.android.com/reference/android/text/Spanned
+ */
+fun formatNights(nights: List, resources: Resources): Spanned {
+ val sb = StringBuilder()
+ sb.apply {
+ append(resources.getString(R.string.title))
+ nights.forEach {
+ append(" ")
+ append(resources.getString(R.string.start_time))
+ append("\t${convertLongToDateString(it.startTimeMilli)} ")
+ if (it.endTimeMilli != it.startTimeMilli) {
+ append(resources.getString(R.string.end_time))
+ append("\t${convertLongToDateString(it.endTimeMilli)} ")
+ append(resources.getString(R.string.quality))
+ append("\t${convertNumericQualityToString(it.sleepQuality, resources)} ")
+ append(resources.getString(R.string.hours_slept))
+ // Hours
+ append("\t ${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60 / 60}:")
+ // Minutes
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60}:")
+ // Seconds
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000}
")
+ }
+ }
+ }
+ // fromHtml is deprecated for target API without a flag, but since our minSDK is 19, we
+ // can't use the newer version, which requires minSDK of 24
+ //https://developer.android.com/reference/android/text/Html#fromHtml(java.lang.String,%20int)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return Html.fromHtml(sb.toString(), Html.FROM_HTML_MODE_LEGACY)
+ } else {
+ @Suppress("DEPRECATION")
+ return Html.fromHtml(sb.toString())
+ }
+}
+
+/**
+ * ViewHolder that holds a single [TextView].
+ *
+ * A ViewHolder holds a view for the [RecyclerView] as well as providing additional information
+ * to the RecyclerView such as where on the screen it was last drawn during scrolling.
+ */
+class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
\ No newline at end of file
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
new file mode 100755
index 0000000..483642b
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+
+/**
+ * A database that stores SleepNight information.
+ * And a global method to get access to the database.
+ *
+ * This pattern is pretty much the same for any database,
+ * so you can reuse it.
+ */
+@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
+abstract class SleepDatabase : RoomDatabase() {
+
+ /**
+ * Connects the database to the DAO.
+ */
+ abstract val sleepDatabaseDao: SleepDatabaseDao
+
+ /**
+ * Define a companion object, this allows us to add functions on the SleepDatabase class.
+ *
+ * For example, clients can call `SleepDatabase.getInstance(context)` to instantiate
+ * a new SleepDatabase.
+ */
+ companion object {
+ /**
+ * INSTANCE will keep a reference to any database returned via getInstance.
+ *
+ * This will help us avoid repeatedly initializing the database, which is expensive.
+ *
+ * The value of a volatile variable will never be cached, and all writes and
+ * reads will be done to and from the main memory. It means that changes made by one
+ * thread to shared data are visible to other threads.
+ */
+ @Volatile
+ private var INSTANCE: SleepDatabase? = null
+
+ /**
+ * Helper function to get the database.
+ *
+ * If a database has already been retrieved, the previous database will be returned.
+ * Otherwise, create a new database.
+ *
+ * This function is threadsafe, and callers should cache the result for multiple database
+ * calls to avoid overhead.
+ *
+ * This is an example of a simple Singleton pattern that takes another Singleton as an
+ * argument in Kotlin.
+ *
+ * To learn more about Singleton read the wikipedia article:
+ * https://en.wikipedia.org/wiki/Singleton_pattern
+ *
+ * @param context The application context Singleton, used to get access to the filesystem.
+ */
+ fun getInstance(context: Context): SleepDatabase {
+ // Multiple threads can ask for the database at the same time, ensure we only initialize
+ // it once by using synchronized. Only one thread may enter a synchronized block at a
+ // time.
+ synchronized(this) {
+
+ // Copy the current value of INSTANCE to a local variable so Kotlin can smart cast.
+ // Smart cast is only available to local variables.
+ var instance = INSTANCE
+
+ // If instance is `null` make a new database instance.
+ if (instance == null) {
+ instance = Room.databaseBuilder(
+ context.applicationContext,
+ SleepDatabase::class.java,
+ "sleep_history_database"
+ )
+ // Wipes and rebuilds instead of migrating if no Migration object.
+ // Migration is not part of this lesson. You can learn more about
+ // migration with Room in this blog post:
+ // https://medium.com/androiddevelopers/understanding-migrations-with-room-f01e04b07929
+ .fallbackToDestructiveMigration()
+ .build()
+ // Assign INSTANCE to the newly created database.
+ INSTANCE = instance
+ }
+
+ // Return instance; smart cast to be non-null.
+ return instance
+ }
+ }
+ }
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
new file mode 100755
index 0000000..49f3ea1
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Update
+
+
+/**
+ * Defines methods for using the SleepNight class with Room.
+ */
+@Dao
+interface SleepDatabaseDao {
+
+ @Insert
+ fun insert(night: SleepNight)
+
+ /**
+ * When updating a row with a value already set in a column,
+ * replaces the old value with the new one.
+ *
+ * @param night new value to write
+ */
+ @Update
+ fun update(night: SleepNight)
+
+ /**
+ * Selects and returns the row that matches the supplied start time, which is our key.
+ *
+ * @param key startTimeMilli to match
+ */
+ @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
+ fun get(key: Long): SleepNight
+
+ /**
+ * Deletes all values from the table.
+ *
+ * This does not delete the table, only its contents.
+ */
+ @Query("DELETE FROM daily_sleep_quality_table")
+ fun clear()
+
+ /**
+ * Selects and returns all rows in the table,
+ *
+ * sorted by start time in descending order.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
+ fun getAllNights(): LiveData>
+
+ /**
+ * Selects and returns the latest night.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
+ fun getTonight(): SleepNight?
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
new file mode 100755
index 0000000..b776812
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * Represents one night's sleep through start, end times, and the sleep quality.
+ */
+@Entity(tableName = "daily_sleep_quality_table")
+data class SleepNight(
+ @PrimaryKey(autoGenerate = true)
+ var nightId: Long = 0L,
+
+ @ColumnInfo(name = "start_time_milli")
+ val startTimeMilli: Long = System.currentTimeMillis(),
+
+ @ColumnInfo(name = "end_time_milli")
+ var endTimeMilli: Long = startTimeMilli,
+
+ @ColumnInfo(name = "quality_rating")
+ var sleepQuality: Int = -1)
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
new file mode 100755
index 0000000..db22082
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepQualityBinding
+
+/**
+ * Fragment that displays a list of clickable icons,
+ * each representing a sleep quality rating.
+ * Once the user taps an icon, the quality is set in the current sleepNight
+ * and the database is updated.
+ */
+class SleepQualityFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepQualityViewModel] to the
+ * [FragmentSleepQualityBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepQualityBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_quality, container, false)
+
+ val application = requireNotNull(this.activity).application
+ val arguments = SleepQualityFragmentArgs.fromBundle(arguments)
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepQualityViewModelFactory(arguments.sleepNightKey, dataSource)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepQualityViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepQualityViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepQualityViewModel = sleepQualityViewModel
+
+ // Add an Observer to the state variable for Navigating when a Quality icon is tapped.
+ sleepQualityViewModel.navigateToSleepTracker.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ this.findNavController().navigate(
+ SleepQualityFragmentDirections.actionSleepQualityFragmentToSleepTrackerFragment())
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepQualityViewModel.doneNavigating()
+ }
+ })
+
+ return binding.root
+ }
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
new file mode 100755
index 0000000..31de35e
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepQualityFragment.
+ *
+ * @param sleepNightKey The key of the current night we are working on.
+ */
+class SleepQualityViewModel(
+ private val sleepNightKey: Long = 0L,
+ dataSource: SleepDatabaseDao) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via its SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine setup variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private val viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this scope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ /**
+ * Variable that tells the fragment whether it should navigate to [SleepTrackerFragment].
+ *
+ * This is `private` because we don't want to expose the ability to set [MutableLiveData] to
+ * the [Fragment]
+ */
+ private val _navigateToSleepTracker = MutableLiveData()
+
+ /**
+ * When true immediately navigate back to the [SleepTrackerFragment]
+ */
+ val navigateToSleepTracker: LiveData
+ get() = _navigateToSleepTracker
+
+ /**
+ * Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work.
+ *
+ * onCleared() gets called when the ViewModel is destroyed.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+
+ /**
+ * Call this immediately after navigating to [SleepTrackerFragment]
+ */
+ fun doneNavigating() {
+ _navigateToSleepTracker.value = null
+ }
+
+ /**
+ * Sets the sleep quality and updates the database.
+ *
+ * Then navigates back to the SleepTrackerFragment.
+ */
+ fun onSetSleepQuality(quality: Int) {
+ uiScope.launch {
+ // IO is a thread pool for running operations that access the disk, such as
+ // our Room database.
+ withContext(Dispatchers.IO) {
+ val tonight = database.get(sleepNightKey)
+ tonight.sleepQuality = quality
+ database.update(tonight)
+ }
+
+ // Setting this state variable to true will alert the observer and trigger navigation.
+ _navigateToSleepTracker.value = true
+ }
+ }
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
new file mode 100755
index 0000000..3c6a35d
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the key for the night and the SleepDatabaseDao to the ViewModel.
+ */
+class SleepQualityViewModelFactory(
+ private val sleepNightKey: Long,
+ private val dataSource: SleepDatabaseDao) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepQualityViewModel::class.java)) {
+ return SleepQualityViewModel(sleepNightKey, dataSource) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt
new file mode 100644
index 0000000..478ffa9
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.databinding.BindingAdapter
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.convertDurationToFormatted
+import com.example.android.trackmysleepquality.convertNumericQualityToString
+import com.example.android.trackmysleepquality.database.SleepNight
+
+
+@BindingAdapter("sleepDurationFormatted")
+fun TextView.setSleepDurationFormatted(item: SleepNight) {
+ text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
+}
+
+
+@BindingAdapter("sleepQualityString")
+fun TextView.setSleepQualityString(item: SleepNight) {
+ text = convertNumericQualityToString(item.sleepQuality, context.resources)
+}
+
+
+@BindingAdapter("sleepImage")
+fun ImageView.setSleepImage(item: SleepNight) {
+ setImageResource(when (item.sleepQuality) {
+ 0 -> R.drawable.ic_sleep_0
+ 1 -> R.drawable.ic_sleep_1
+ 2 -> R.drawable.ic_sleep_2
+
+ 3 -> R.drawable.ic_sleep_3
+
+ 4 -> R.drawable.ic_sleep_4
+ 5 -> R.drawable.ic_sleep_5
+ else -> R.drawable.ic_sleep_active
+ })
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
new file mode 100644
index 0000000..31bb0f0
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+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.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.convertDurationToFormatted
+import com.example.android.trackmysleepquality.convertNumericQualityToString
+import com.example.android.trackmysleepquality.database.SleepNight
+import com.example.android.trackmysleepquality.databinding.ListItemSleepNightBinding
+
+class SleepNightAdapter : ListAdapter(SleepNightDiffCallback()) {
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = getItem(position)
+ holder.bind(item)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ return ViewHolder.from(parent)
+ }
+
+ class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){
+
+ fun bind(item: SleepNight) {
+ binding.sleep = item
+ binding.executePendingBindings()
+ }
+
+ companion object {
+ fun from(parent: ViewGroup): ViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ val binding = ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
+ return ViewHolder(binding)
+ }
+ }
+ }
+}
+
+
+class SleepNightDiffCallback : DiffUtil.ItemCallback() {
+
+ override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
+ return oldItem.nightId == newItem.nightId
+ }
+
+
+ override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
+ return oldItem == newItem
+ }
+
+
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
new file mode 100755
index 0000000..3f5fbba
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepTrackerBinding
+import com.google.android.material.snackbar.Snackbar
+
+/**
+ * A fragment with buttons to record start and end times for sleep, which are saved in
+ * a database. Cumulative data is displayed in a simple scrollable TextView.
+ * (Because we have not learned about RecyclerView yet.)
+ * The Clear button will clear all data from the database.
+ */
+class SleepTrackerFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepTrackerViewModel] to the
+ * [FragmentSleepTrackerBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_tracker, container, false)
+
+ val application = requireNotNull(this.activity).application
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepTrackerViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepTrackerViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepTrackerViewModel = sleepTrackerViewModel
+
+ val adapter = SleepNightAdapter()
+ binding.sleepList.adapter = adapter
+
+ sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
+ it?.let {
+ adapter.submitList(it)
+ }
+ })
+
+ // Specify the current activity as the lifecycle owner of the binding.
+ // This is necessary so that the binding can observe LiveData updates.
+ binding.setLifecycleOwner(this)
+
+ // Add an Observer on the state variable for showing a Snackbar message
+ // when the CLEAR button is pressed.
+ sleepTrackerViewModel.showSnackBarEvent.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ Snackbar.make(
+ activity!!.findViewById(android.R.id.content),
+ getString(R.string.cleared_message),
+ Snackbar.LENGTH_SHORT // How long to display the message.
+ ).show()
+ // Reset state to make sure the toast is only shown once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneShowingSnackbar()
+ }
+ })
+
+ // Add an Observer on the state variable for Navigating when STOP button is pressed.
+ sleepTrackerViewModel.navigateToSleepQuality.observe(this, Observer { night ->
+ night?.let {
+ // We need to get the navController from this, because button is not ready, and it
+ // just has to be a view. For some reason, this only matters if we hit stop again
+ // after using the back button, not if we hit stop and choose a quality.
+ // Also, in the Navigation Editor, for Quality -> Tracker, check "Inclusive" for
+ // popping the stack to get the correct behavior if we press stop multiple times
+ // followed by back.
+ // Also: https://stackoverflow.com/questions/28929637/difference-and-uses-of-oncreate-oncreateview-and-onactivitycreated-in-fra
+ this.findNavController().navigate(
+ SleepTrackerFragmentDirections
+ .actionSleepTrackerFragmentToSleepQualityFragment(night.nightId))
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneNavigating()
+ }
+ })
+ return binding.root
+ }
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
new file mode 100755
index 0000000..4bc4299
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.app.Application
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Transformations
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import com.example.android.trackmysleepquality.database.SleepNight
+import com.example.android.trackmysleepquality.formatNights
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepTrackerFragment.
+ */
+class SleepTrackerViewModel(
+ dataSource: SleepDatabaseDao,
+ application: Application) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private var viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this uiScope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ private var tonight = MutableLiveData()
+
+ val nights = database.getAllNights()
+
+ /**
+ * Converted nights to Spanned for displaying.
+ */
+ val nightsString = Transformations.map(nights) { nights ->
+ formatNights(nights, application.resources)
+ }
+
+ /**
+ * If tonight has not been set, then the START button should be visible.
+ */
+ val startButtonVisible = Transformations.map(tonight) {
+ null == it
+ }
+
+ /**
+ * If tonight has been set, then the STOP button should be visible.
+ */
+ val stopButtonVisible = Transformations.map(tonight) {
+ null != it
+ }
+
+ /**
+ * If there are any nights in the database, show the CLEAR button.
+ */
+ val clearButtonVisible = Transformations.map(nights) {
+ it?.isNotEmpty()
+ }
+
+
+ /**
+ * Request a toast by setting this value to true.
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private var _showSnackbarEvent = MutableLiveData()
+
+ /**
+ * If this is true, immediately `show()` a toast and call `doneShowingSnackbar()`.
+ */
+ val showSnackBarEvent: LiveData
+ get() = _showSnackbarEvent
+
+ /**
+ * Variable that tells the Fragment to navigate to a specific [SleepQualityFragment]
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private val _navigateToSleepQuality = MutableLiveData()
+
+ /**
+ * If this is non-null, immediately navigate to [SleepQualityFragment] and call [doneNavigating]
+ */
+ val navigateToSleepQuality: LiveData
+ get() = _navigateToSleepQuality
+
+ /**
+ * Call this immediately after calling `show()` on a toast.
+ *
+ * It will clear the toast request, so if the user rotates their phone it won't show a duplicate
+ * toast.
+ */
+ fun doneShowingSnackbar() {
+ _showSnackbarEvent.value = null
+ }
+
+ /**
+ * Call this immediately after navigating to [SleepQualityFragment]
+ *
+ * It will clear the navigation request, so if the user rotates their phone it won't navigate
+ * twice.
+ */
+ fun doneNavigating() {
+ _navigateToSleepQuality.value = null
+ }
+
+ init {
+ initializeTonight()
+ }
+
+ private fun initializeTonight() {
+ uiScope.launch {
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Handling the case of the stopped app or forgotten recording,
+ * the start and end times will be the same.j
+ *
+ * If the start time and end time are not the same, then we do not have an unfinished
+ * recording.
+ */
+ private suspend fun getTonightFromDatabase(): SleepNight? {
+ return withContext(Dispatchers.IO) {
+ var night = database.getTonight()
+ if (night?.endTimeMilli != night?.startTimeMilli) {
+ night = null
+ }
+ night
+ }
+ }
+
+ private suspend fun insert(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.insert(night)
+ }
+ }
+
+ private suspend fun update(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.update(night)
+ }
+ }
+
+ private suspend fun clear() {
+ withContext(Dispatchers.IO) {
+ database.clear()
+ }
+ }
+
+ /**
+ * Executes when the START button is clicked.
+ */
+ fun onStart() {
+ uiScope.launch {
+ // Create a new night, which captures the current time,
+ // and insert it into the database.
+ val newNight = SleepNight()
+
+ insert(newNight)
+
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Executes when the STOP button is clicked.
+ */
+ fun onStop() {
+ uiScope.launch {
+ // In Kotlin, the return@label syntax is used for specifying which function among
+ // several nested ones this statement returns from.
+ // In this case, we are specifying to return from launch().
+ val oldNight = tonight.value ?: return@launch
+
+ // Update the night in the database to add the end time.
+ oldNight.endTimeMilli = System.currentTimeMillis()
+
+ update(oldNight)
+
+ // Set state to navigate to the SleepQualityFragment.
+ _navigateToSleepQuality.value = oldNight
+ }
+ }
+
+ /**
+ * Executes when the CLEAR button is clicked.
+ */
+ fun onClear() {
+ uiScope.launch {
+ // Clear the database table.
+ clear()
+
+ // And clear tonight since it's no longer in the database
+ tonight.value = null
+
+ // Show a snackbar message, because it's friendly.
+ _showSnackbarEvent.value = true
+ }
+ }
+
+ /**
+ * Called when the ViewModel is dismantled.
+ * At this point, we want to cancel all coroutines;
+ * otherwise we end up with processes that have nowhere to return to
+ * using memory and resources.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
new file mode 100755
index 0000000..abcd8ac
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the SleepDatabaseDao and context to the ViewModel.
+ */
+class SleepTrackerViewModelFactory(
+ private val dataSource: SleepDatabaseDao,
+ private val application: Application) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
+ return SleepTrackerViewModel(dataSource, application) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/anim/slide_in_right.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/anim/slide_in_right.xml
new file mode 100755
index 0000000..49f7779
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/anim/slide_in_right.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
new file mode 100755
index 0000000..0b1f6bd
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_0.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_0.xml
new file mode 100755
index 0000000..91c0b47
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_0.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_1.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_1.xml
new file mode 100755
index 0000000..8cc34f7
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_1.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_2.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_2.xml
new file mode 100755
index 0000000..0b7b818
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_2.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_3.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_3.xml
new file mode 100755
index 0000000..677b9ae
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_3.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_4.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_4.xml
new file mode 100755
index 0000000..39eab52
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_4.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_5.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_5.xml
new file mode 100755
index 0000000..f23a2ae
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_5.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_active.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_active.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/drawable/ic_sleep_active.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/font/roboto.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/font/roboto.xml
new file mode 100755
index 0000000..2641caf
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/font/roboto.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/activity_main.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/activity_main.xml
new file mode 100755
index 0000000..a21679c
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/fragment_sleep_quality.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/fragment_sleep_quality.xml
new file mode 100755
index 0000000..ab0ba0c
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/fragment_sleep_quality.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/fragment_sleep_tracker.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/fragment_sleep_tracker.xml
new file mode 100755
index 0000000..66f590b
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/fragment_sleep_tracker.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/list_item_sleep_night.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/list_item_sleep_night.xml
new file mode 100755
index 0000000..8d3f008
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/list_item_sleep_night.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/text_item_view.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/text_item_view.xml
new file mode 100644
index 0000000..855c9d6
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/layout/text_item_view.xml
@@ -0,0 +1,22 @@
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..898f3ed
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dffca36
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..e5e92ac
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..ae7a1a2
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..64ba76f
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dae5e08
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1250b4c
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..5646308
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..e5ed465
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..14ed0af
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..fd07714
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..86c0074
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..b0907ca
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..d8ae031
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..ae4d8d3
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..37a4a99
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..2c18de9
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..beed3cd
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1fc200c
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..1125b8c
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/navigation/navigation.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/navigation/navigation.xml
new file mode 100755
index 0000000..41323ab
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/navigation/navigation.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/colors.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/colors.xml
new file mode 100755
index 0000000..fe8cd82
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ #6ab343
+ #388310
+ #6ab343
+
+ #388310
+ #f0f0f0
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/dimens.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/dimens.xml
new file mode 100755
index 0000000..5385c2e
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+ 16dp
+ 64dp
+ 20sp
+ 48dp
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/font_certs.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/font_certs.xml
new file mode 100755
index 0000000..98db6fb
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/font_certs.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ @array/com_google_android_gms_fonts_certs_dev
+ @array/com_google_android_gms_fonts_certs_prod
+
+
+
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+
+
+
+
+ MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/preloaded_fonts.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/preloaded_fonts.xml
new file mode 100755
index 0000000..0a2c03c
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/preloaded_fonts.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ @font/roboto
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/strings.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/strings.xml
new file mode 100755
index 0000000..223c807
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/strings.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Track My Sleep Quality
+ SleepTrackerFragment
+ SleepQualityFragment
+
+
+ Start
+ Stop
+ Clear
+
+
+ Sleep Quality 0
+ Sleep Quality 1
+ Sleep Quality 2
+ Sleep Quality 3
+ Sleep Quality 4
+ Sleep Quality 5
+
+
+ HERE IS YOUR SLEEP DATA]]>
+ Start:]]>
+ End:]]>
+ Quality:]]>
+ Hours:Minutes:Seconds]]>
+
+
+ How was your sleep?
+ Very bad
+ Poor
+ So-so
+ OK
+ Pretty good
+ Excellent!
+
+
+ All your data is gone forever.
+
+ Sleep Results
+ Close
+ %d minutes on %s
+ %d hours on %s
+ %d seconds on %s
+
diff --git a/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/styles.xml b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..76369f2
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/app/src/main/res/values/styles.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewDiffUtilDataBinding/build.gradle b/RecyclerViewDiffUtilDataBinding/build.gradle
new file mode 100755
index 0000000..cd8f0e1
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018, 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.3.10'
+ archLifecycleVersion = '1.1.1'
+ room_version = '2.0.0'
+ coroutine_version = '1.0.0'
+ gradleVersion = '3.4.0'
+ navigationVersion = '1.0.0-alpha07'
+ dataBindingCompilerVersion = gradleVersion // Always need to be the same.
+ }
+
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath "com.android.tools.build:gradle:$gradleVersion"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
+
+ // 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
+}
diff --git a/RecyclerViewDiffUtilDataBinding/gradle.properties b/RecyclerViewDiffUtilDataBinding/gradle.properties
new file mode 100755
index 0000000..9592636
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/gradle.properties
@@ -0,0 +1,15 @@
+# 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
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/RecyclerViewDiffUtilDataBinding/gradle/wrapper/gradle-wrapper.jar b/RecyclerViewDiffUtilDataBinding/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..f6b961f
Binary files /dev/null and b/RecyclerViewDiffUtilDataBinding/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/RecyclerViewDiffUtilDataBinding/gradle/wrapper/gradle-wrapper.properties b/RecyclerViewDiffUtilDataBinding/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..80bc9f8
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue May 14 17:06:11 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/RecyclerViewDiffUtilDataBinding/gradlew b/RecyclerViewDiffUtilDataBinding/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/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/RecyclerViewDiffUtilDataBinding/gradlew.bat b/RecyclerViewDiffUtilDataBinding/gradlew.bat
new file mode 100755
index 0000000..e95643d
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/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/RecyclerViewDiffUtilDataBinding/settings.gradle b/RecyclerViewDiffUtilDataBinding/settings.gradle
new file mode 100755
index 0000000..e7b4def
--- /dev/null
+++ b/RecyclerViewDiffUtilDataBinding/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/RecyclerViewFundamentals/README.md b/RecyclerViewFundamentals/README.md
new file mode 100755
index 0000000..4e66ab6
--- /dev/null
+++ b/RecyclerViewFundamentals/README.md
@@ -0,0 +1,53 @@
+TrackMySleepQuality with RecyclerView - Solution Code for 7.1
+============================================================
+
+Solution code for Android Kotlin Fundamentals Codelab 7.1 RecyclerView Fundamentals
+
+Introduction
+------------
+
+TrackMySleepQuality is an app for recording sleep data for each night.
+You can record a start and stop time, assign a quality rating, and clear the database.
+
+In this codelab you will learn how to use a RecyclerView in your Android Kotlin app
+to efficiently display lists of items.
+
+
+Pre-requisites
+--------------
+
+You should be familiar with:
+
+* Building a basic user interface (UI) using an activity, fragments, and views.
+* Navigating between fragments, and using safeArgs to pass data between fragments.
+* Using view models, view model factories, transformations, and LiveData and their observers.
+* Creating a Room database, creating a DAO, and defining entities.
+* Using coroutines for database tasks and other long-running tasks.
+
+
+
+Getting Started
+---------------
+
+1. Download and run the app.
+
+License
+-------
+
+Copyright 2019 Google, 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.
+
diff --git a/RecyclerViewFundamentals/app/.gitignore b/RecyclerViewFundamentals/app/.gitignore
new file mode 100755
index 0000000..796b96d
--- /dev/null
+++ b/RecyclerViewFundamentals/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/RecyclerViewFundamentals/app/build.gradle b/RecyclerViewFundamentals/app/build.gradle
new file mode 100755
index 0000000..89f56de
--- /dev/null
+++ b/RecyclerViewFundamentals/app/build.gradle
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019, 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: 'kotlin-kapt'
+apply plugin: 'androidx.navigation.safeargs'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.example.android.trackmysleepqualityrecyclerview"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ // Enables data binding.
+ dataBinding {
+ enabled = true
+ }
+
+}
+
+dependencies {
+
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ // Support libraries
+ implementation "androidx.appcompat:appcompat:1.0.0"
+ implementation "androidx.fragment:fragment:1.0.0"
+ implementation "androidx.constraintlayout:constraintlayout:2.0.0-alpha2"
+
+ // Android KTX
+ implementation 'androidx.core:core-ktx:1.0.1'
+
+ // Room and Lifecycle dependencies
+ implementation "androidx.room:room-runtime:$room_version"
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ kapt "androidx.room:room-compiler:$room_version"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
+
+ // Coroutines
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
+
+ // Navigation
+ implementation "android.arch.navigation:navigation-fragment-ktx:$navigationVersion"
+ implementation "android.arch.navigation:navigation-ui-ktx:$navigationVersion"
+
+ // Testing
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
+}
+
diff --git a/RecyclerViewFundamentals/app/proguard-rules.pro b/RecyclerViewFundamentals/app/proguard-rules.pro
new file mode 100755
index 0000000..f1b4245
--- /dev/null
+++ b/RecyclerViewFundamentals/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/RecyclerViewFundamentals/app/src/main/AndroidManifest.xml b/RecyclerViewFundamentals/app/src/main/AndroidManifest.xml
new file mode 100755
index 0000000..58a0b98
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/ic_launcher_sleep_tracker-web.png b/RecyclerViewFundamentals/app/src/main/ic_launcher_sleep_tracker-web.png
new file mode 100755
index 0000000..668bfae
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/ic_launcher_sleep_tracker-web.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
new file mode 100755
index 0000000..f06095d
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_main)
+ }
+}
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/Util.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
new file mode 100755
index 0000000..bf2f542
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.annotation.SuppressLint
+import android.content.res.Resources
+import android.os.Build
+import android.text.Html
+import android.text.Spanned
+import android.view.View
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.trackmysleepquality.database.SleepNight
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+/**
+ * These functions create a formatted string that can be set in a TextView.
+ */
+
+private val ONE_MINUTE_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES)
+private val ONE_HOUR_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)
+
+/**
+ * Convert a duration to a formatted string for display.
+ *
+ * Examples:
+ *
+ * 6 seconds on Wednesday
+ * 2 minutes on Monday
+ * 40 hours on Thursday
+ *
+ * @param startTimeMilli the start of the interval
+ * @param endTimeMilli the end of the interval
+ * @param res resources used to load formatted strings
+ */
+fun convertDurationToFormatted(startTimeMilli: Long, endTimeMilli: Long, res: Resources): String {
+ val durationMilli = endTimeMilli - startTimeMilli
+ val weekdayString = SimpleDateFormat("EEEE", Locale.getDefault()).format(startTimeMilli)
+ return when {
+ durationMilli < ONE_MINUTE_MILLIS -> {
+ val seconds = TimeUnit.SECONDS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.seconds_length, seconds, weekdayString)
+ }
+ durationMilli < ONE_HOUR_MILLIS -> {
+ val minutes = TimeUnit.MINUTES.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.minutes_length, minutes, weekdayString)
+ }
+ else -> {
+ val hours = TimeUnit.HOURS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.hours_length, hours, weekdayString)
+ }
+ }
+}
+
+/**
+ * Returns a string representing the numeric quality rating.
+ */
+fun convertNumericQualityToString(quality: Int, resources: Resources): String {
+ var qualityString = resources.getString(R.string.three_ok)
+ when (quality) {
+ -1 -> qualityString = "--"
+ 0 -> qualityString = resources.getString(R.string.zero_very_bad)
+ 1 -> qualityString = resources.getString(R.string.one_poor)
+ 2 -> qualityString = resources.getString(R.string.two_soso)
+ 4 -> qualityString = resources.getString(R.string.four_pretty_good)
+ 5 -> qualityString = resources.getString(R.string.five_excellent)
+ }
+ return qualityString
+}
+
+
+/**
+ * Take the Long milliseconds returned by the system and stored in Room,
+ * and convert it to a nicely formatted string for display.
+ *
+ * EEEE - Display the long letter version of the weekday
+ * MMM - Display the letter abbreviation of the nmotny
+ * dd-yyyy - day in month and full year numerically
+ * HH:mm - Hours and minutes in 24hr format
+ */
+@SuppressLint("SimpleDateFormat")
+fun convertLongToDateString(systemTime: Long): String {
+ return SimpleDateFormat("EEEE MMM-dd-yyyy' Time: 'HH:mm")
+ .format(systemTime).toString()
+}
+
+/**
+ * Takes a list of SleepNights and converts and formats it into one string for display.
+ *
+ * For display in a TextView, we have to supply one string, and styles are per TextView, not
+ * applicable per word. So, we build a formatted string using HTML. This is handy, but we will
+ * learn a better way of displaying this data in a future lesson.
+ *
+ * @param nights - List of all SleepNights in the database.
+ * @param resources - Resources object for all the resources defined for our app.
+ *
+ * @return Spanned - An interface for text that has formatting attached to it.
+ * See: https://developer.android.com/reference/android/text/Spanned
+ */
+fun formatNights(nights: List, resources: Resources): Spanned {
+ val sb = StringBuilder()
+ sb.apply {
+ append(resources.getString(R.string.title))
+ nights.forEach {
+ append(" ")
+ append(resources.getString(R.string.start_time))
+ append("\t${convertLongToDateString(it.startTimeMilli)} ")
+ if (it.endTimeMilli != it.startTimeMilli) {
+ append(resources.getString(R.string.end_time))
+ append("\t${convertLongToDateString(it.endTimeMilli)} ")
+ append(resources.getString(R.string.quality))
+ append("\t${convertNumericQualityToString(it.sleepQuality, resources)} ")
+ append(resources.getString(R.string.hours_slept))
+ // Hours
+ append("\t ${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60 / 60}:")
+ // Minutes
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60}:")
+ // Seconds
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000}
")
+ }
+ }
+ }
+ // fromHtml is deprecated for target API without a flag, but since our minSDK is 19, we
+ // can't use the newer version, which requires minSDK of 24
+ //https://developer.android.com/reference/android/text/Html#fromHtml(java.lang.String,%20int)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return Html.fromHtml(sb.toString(), Html.FROM_HTML_MODE_LEGACY)
+ } else {
+ @Suppress("DEPRECATION")
+ return Html.fromHtml(sb.toString())
+ }
+}
+
+/**
+ * ViewHolder that holds a single [TextView].
+ *
+ * A ViewHolder holds a view for the [RecyclerView] as well as providing additional information
+ * to the RecyclerView such as where on the screen it was last drawn during scrolling.
+ */
+class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
\ No newline at end of file
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
new file mode 100755
index 0000000..483642b
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+
+/**
+ * A database that stores SleepNight information.
+ * And a global method to get access to the database.
+ *
+ * This pattern is pretty much the same for any database,
+ * so you can reuse it.
+ */
+@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
+abstract class SleepDatabase : RoomDatabase() {
+
+ /**
+ * Connects the database to the DAO.
+ */
+ abstract val sleepDatabaseDao: SleepDatabaseDao
+
+ /**
+ * Define a companion object, this allows us to add functions on the SleepDatabase class.
+ *
+ * For example, clients can call `SleepDatabase.getInstance(context)` to instantiate
+ * a new SleepDatabase.
+ */
+ companion object {
+ /**
+ * INSTANCE will keep a reference to any database returned via getInstance.
+ *
+ * This will help us avoid repeatedly initializing the database, which is expensive.
+ *
+ * The value of a volatile variable will never be cached, and all writes and
+ * reads will be done to and from the main memory. It means that changes made by one
+ * thread to shared data are visible to other threads.
+ */
+ @Volatile
+ private var INSTANCE: SleepDatabase? = null
+
+ /**
+ * Helper function to get the database.
+ *
+ * If a database has already been retrieved, the previous database will be returned.
+ * Otherwise, create a new database.
+ *
+ * This function is threadsafe, and callers should cache the result for multiple database
+ * calls to avoid overhead.
+ *
+ * This is an example of a simple Singleton pattern that takes another Singleton as an
+ * argument in Kotlin.
+ *
+ * To learn more about Singleton read the wikipedia article:
+ * https://en.wikipedia.org/wiki/Singleton_pattern
+ *
+ * @param context The application context Singleton, used to get access to the filesystem.
+ */
+ fun getInstance(context: Context): SleepDatabase {
+ // Multiple threads can ask for the database at the same time, ensure we only initialize
+ // it once by using synchronized. Only one thread may enter a synchronized block at a
+ // time.
+ synchronized(this) {
+
+ // Copy the current value of INSTANCE to a local variable so Kotlin can smart cast.
+ // Smart cast is only available to local variables.
+ var instance = INSTANCE
+
+ // If instance is `null` make a new database instance.
+ if (instance == null) {
+ instance = Room.databaseBuilder(
+ context.applicationContext,
+ SleepDatabase::class.java,
+ "sleep_history_database"
+ )
+ // Wipes and rebuilds instead of migrating if no Migration object.
+ // Migration is not part of this lesson. You can learn more about
+ // migration with Room in this blog post:
+ // https://medium.com/androiddevelopers/understanding-migrations-with-room-f01e04b07929
+ .fallbackToDestructiveMigration()
+ .build()
+ // Assign INSTANCE to the newly created database.
+ INSTANCE = instance
+ }
+
+ // Return instance; smart cast to be non-null.
+ return instance
+ }
+ }
+ }
+}
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
new file mode 100755
index 0000000..49f3ea1
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Update
+
+
+/**
+ * Defines methods for using the SleepNight class with Room.
+ */
+@Dao
+interface SleepDatabaseDao {
+
+ @Insert
+ fun insert(night: SleepNight)
+
+ /**
+ * When updating a row with a value already set in a column,
+ * replaces the old value with the new one.
+ *
+ * @param night new value to write
+ */
+ @Update
+ fun update(night: SleepNight)
+
+ /**
+ * Selects and returns the row that matches the supplied start time, which is our key.
+ *
+ * @param key startTimeMilli to match
+ */
+ @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
+ fun get(key: Long): SleepNight
+
+ /**
+ * Deletes all values from the table.
+ *
+ * This does not delete the table, only its contents.
+ */
+ @Query("DELETE FROM daily_sleep_quality_table")
+ fun clear()
+
+ /**
+ * Selects and returns all rows in the table,
+ *
+ * sorted by start time in descending order.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
+ fun getAllNights(): LiveData>
+
+ /**
+ * Selects and returns the latest night.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
+ fun getTonight(): SleepNight?
+}
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
new file mode 100755
index 0000000..b776812
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * Represents one night's sleep through start, end times, and the sleep quality.
+ */
+@Entity(tableName = "daily_sleep_quality_table")
+data class SleepNight(
+ @PrimaryKey(autoGenerate = true)
+ var nightId: Long = 0L,
+
+ @ColumnInfo(name = "start_time_milli")
+ val startTimeMilli: Long = System.currentTimeMillis(),
+
+ @ColumnInfo(name = "end_time_milli")
+ var endTimeMilli: Long = startTimeMilli,
+
+ @ColumnInfo(name = "quality_rating")
+ var sleepQuality: Int = -1)
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
new file mode 100755
index 0000000..db22082
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepQualityBinding
+
+/**
+ * Fragment that displays a list of clickable icons,
+ * each representing a sleep quality rating.
+ * Once the user taps an icon, the quality is set in the current sleepNight
+ * and the database is updated.
+ */
+class SleepQualityFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepQualityViewModel] to the
+ * [FragmentSleepQualityBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepQualityBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_quality, container, false)
+
+ val application = requireNotNull(this.activity).application
+ val arguments = SleepQualityFragmentArgs.fromBundle(arguments)
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepQualityViewModelFactory(arguments.sleepNightKey, dataSource)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepQualityViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepQualityViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepQualityViewModel = sleepQualityViewModel
+
+ // Add an Observer to the state variable for Navigating when a Quality icon is tapped.
+ sleepQualityViewModel.navigateToSleepTracker.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ this.findNavController().navigate(
+ SleepQualityFragmentDirections.actionSleepQualityFragmentToSleepTrackerFragment())
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepQualityViewModel.doneNavigating()
+ }
+ })
+
+ return binding.root
+ }
+}
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
new file mode 100755
index 0000000..31de35e
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepQualityFragment.
+ *
+ * @param sleepNightKey The key of the current night we are working on.
+ */
+class SleepQualityViewModel(
+ private val sleepNightKey: Long = 0L,
+ dataSource: SleepDatabaseDao) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via its SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine setup variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private val viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this scope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ /**
+ * Variable that tells the fragment whether it should navigate to [SleepTrackerFragment].
+ *
+ * This is `private` because we don't want to expose the ability to set [MutableLiveData] to
+ * the [Fragment]
+ */
+ private val _navigateToSleepTracker = MutableLiveData()
+
+ /**
+ * When true immediately navigate back to the [SleepTrackerFragment]
+ */
+ val navigateToSleepTracker: LiveData
+ get() = _navigateToSleepTracker
+
+ /**
+ * Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work.
+ *
+ * onCleared() gets called when the ViewModel is destroyed.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+
+ /**
+ * Call this immediately after navigating to [SleepTrackerFragment]
+ */
+ fun doneNavigating() {
+ _navigateToSleepTracker.value = null
+ }
+
+ /**
+ * Sets the sleep quality and updates the database.
+ *
+ * Then navigates back to the SleepTrackerFragment.
+ */
+ fun onSetSleepQuality(quality: Int) {
+ uiScope.launch {
+ // IO is a thread pool for running operations that access the disk, such as
+ // our Room database.
+ withContext(Dispatchers.IO) {
+ val tonight = database.get(sleepNightKey)
+ tonight.sleepQuality = quality
+ database.update(tonight)
+ }
+
+ // Setting this state variable to true will alert the observer and trigger navigation.
+ _navigateToSleepTracker.value = true
+ }
+ }
+}
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
new file mode 100755
index 0000000..3c6a35d
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the key for the night and the SleepDatabaseDao to the ViewModel.
+ */
+class SleepQualityViewModelFactory(
+ private val sleepNightKey: Long,
+ private val dataSource: SleepDatabaseDao) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepQualityViewModel::class.java)) {
+ return SleepQualityViewModel(sleepNightKey, dataSource) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
new file mode 100644
index 0000000..48d79ed
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.convertDurationToFormatted
+import com.example.android.trackmysleepquality.convertNumericQualityToString
+import com.example.android.trackmysleepquality.database.SleepNight
+
+class SleepNightAdapter: RecyclerView.Adapter() {
+
+ var data = listOf()
+ set(value) {
+ field = value
+ notifyDataSetChanged()
+ }
+
+ override fun getItemCount() = data.size
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = data[position]
+ holder.bind(item)
+ }
+
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ return ViewHolder.from(parent)
+ }
+
+ class ViewHolder private constructor(itemView: View) : RecyclerView.ViewHolder(itemView){
+ val sleepLength: TextView = itemView.findViewById(R.id.sleep_length)
+ val quality: TextView = itemView.findViewById(R.id.quality_string)
+ val qualityImage: ImageView = itemView.findViewById(R.id.quality_image)
+
+ fun bind(item: SleepNight) {
+ val res = itemView.context.resources
+ sleepLength.text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, res)
+ quality.text = convertNumericQualityToString(item.sleepQuality, res)
+ qualityImage.setImageResource(when (item.sleepQuality) {
+ 0 -> R.drawable.ic_sleep_0
+ 1 -> R.drawable.ic_sleep_1
+ 2 -> R.drawable.ic_sleep_2
+ 3 -> R.drawable.ic_sleep_3
+ 4 -> R.drawable.ic_sleep_4
+ 5 -> R.drawable.ic_sleep_5
+ else -> R.drawable.ic_sleep_active
+ })
+ }
+
+ companion object {
+ fun from(parent: ViewGroup): ViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ val view = layoutInflater
+ .inflate(R.layout.list_item_sleep_night, parent, false)
+
+ return ViewHolder(view)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
new file mode 100755
index 0000000..e260ea7
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepTrackerBinding
+import com.google.android.material.snackbar.Snackbar
+
+/**
+ * A fragment with buttons to record start and end times for sleep, which are saved in
+ * a database. Cumulative data is displayed in a simple scrollable TextView.
+ * (Because we have not learned about RecyclerView yet.)
+ * The Clear button will clear all data from the database.
+ */
+class SleepTrackerFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepTrackerViewModel] to the
+ * [FragmentSleepTrackerBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_tracker, container, false)
+
+ val application = requireNotNull(this.activity).application
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepTrackerViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepTrackerViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepTrackerViewModel = sleepTrackerViewModel
+
+ val adapter = SleepNightAdapter()
+ binding.sleepList.adapter = adapter
+
+ sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
+ it?.let {
+ adapter.data = it
+ }
+ })
+
+ // Specify the current activity as the lifecycle owner of the binding.
+ // This is necessary so that the binding can observe LiveData updates.
+ binding.setLifecycleOwner(this)
+
+ // Add an Observer on the state variable for showing a Snackbar message
+ // when the CLEAR button is pressed.
+ sleepTrackerViewModel.showSnackBarEvent.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ Snackbar.make(
+ activity!!.findViewById(android.R.id.content),
+ getString(R.string.cleared_message),
+ Snackbar.LENGTH_SHORT // How long to display the message.
+ ).show()
+ // Reset state to make sure the toast is only shown once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneShowingSnackbar()
+ }
+ })
+
+ // Add an Observer on the state variable for Navigating when STOP button is pressed.
+ sleepTrackerViewModel.navigateToSleepQuality.observe(this, Observer { night ->
+ night?.let {
+ // We need to get the navController from this, because button is not ready, and it
+ // just has to be a view. For some reason, this only matters if we hit stop again
+ // after using the back button, not if we hit stop and choose a quality.
+ // Also, in the Navigation Editor, for Quality -> Tracker, check "Inclusive" for
+ // popping the stack to get the correct behavior if we press stop multiple times
+ // followed by back.
+ // Also: https://stackoverflow.com/questions/28929637/difference-and-uses-of-oncreate-oncreateview-and-onactivitycreated-in-fra
+ this.findNavController().navigate(
+ SleepTrackerFragmentDirections
+ .actionSleepTrackerFragmentToSleepQualityFragment(night.nightId))
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneNavigating()
+ }
+ })
+ return binding.root
+ }
+}
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
new file mode 100755
index 0000000..4bc4299
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.app.Application
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Transformations
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import com.example.android.trackmysleepquality.database.SleepNight
+import com.example.android.trackmysleepquality.formatNights
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepTrackerFragment.
+ */
+class SleepTrackerViewModel(
+ dataSource: SleepDatabaseDao,
+ application: Application) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private var viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this uiScope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ private var tonight = MutableLiveData()
+
+ val nights = database.getAllNights()
+
+ /**
+ * Converted nights to Spanned for displaying.
+ */
+ val nightsString = Transformations.map(nights) { nights ->
+ formatNights(nights, application.resources)
+ }
+
+ /**
+ * If tonight has not been set, then the START button should be visible.
+ */
+ val startButtonVisible = Transformations.map(tonight) {
+ null == it
+ }
+
+ /**
+ * If tonight has been set, then the STOP button should be visible.
+ */
+ val stopButtonVisible = Transformations.map(tonight) {
+ null != it
+ }
+
+ /**
+ * If there are any nights in the database, show the CLEAR button.
+ */
+ val clearButtonVisible = Transformations.map(nights) {
+ it?.isNotEmpty()
+ }
+
+
+ /**
+ * Request a toast by setting this value to true.
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private var _showSnackbarEvent = MutableLiveData()
+
+ /**
+ * If this is true, immediately `show()` a toast and call `doneShowingSnackbar()`.
+ */
+ val showSnackBarEvent: LiveData
+ get() = _showSnackbarEvent
+
+ /**
+ * Variable that tells the Fragment to navigate to a specific [SleepQualityFragment]
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private val _navigateToSleepQuality = MutableLiveData()
+
+ /**
+ * If this is non-null, immediately navigate to [SleepQualityFragment] and call [doneNavigating]
+ */
+ val navigateToSleepQuality: LiveData
+ get() = _navigateToSleepQuality
+
+ /**
+ * Call this immediately after calling `show()` on a toast.
+ *
+ * It will clear the toast request, so if the user rotates their phone it won't show a duplicate
+ * toast.
+ */
+ fun doneShowingSnackbar() {
+ _showSnackbarEvent.value = null
+ }
+
+ /**
+ * Call this immediately after navigating to [SleepQualityFragment]
+ *
+ * It will clear the navigation request, so if the user rotates their phone it won't navigate
+ * twice.
+ */
+ fun doneNavigating() {
+ _navigateToSleepQuality.value = null
+ }
+
+ init {
+ initializeTonight()
+ }
+
+ private fun initializeTonight() {
+ uiScope.launch {
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Handling the case of the stopped app or forgotten recording,
+ * the start and end times will be the same.j
+ *
+ * If the start time and end time are not the same, then we do not have an unfinished
+ * recording.
+ */
+ private suspend fun getTonightFromDatabase(): SleepNight? {
+ return withContext(Dispatchers.IO) {
+ var night = database.getTonight()
+ if (night?.endTimeMilli != night?.startTimeMilli) {
+ night = null
+ }
+ night
+ }
+ }
+
+ private suspend fun insert(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.insert(night)
+ }
+ }
+
+ private suspend fun update(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.update(night)
+ }
+ }
+
+ private suspend fun clear() {
+ withContext(Dispatchers.IO) {
+ database.clear()
+ }
+ }
+
+ /**
+ * Executes when the START button is clicked.
+ */
+ fun onStart() {
+ uiScope.launch {
+ // Create a new night, which captures the current time,
+ // and insert it into the database.
+ val newNight = SleepNight()
+
+ insert(newNight)
+
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Executes when the STOP button is clicked.
+ */
+ fun onStop() {
+ uiScope.launch {
+ // In Kotlin, the return@label syntax is used for specifying which function among
+ // several nested ones this statement returns from.
+ // In this case, we are specifying to return from launch().
+ val oldNight = tonight.value ?: return@launch
+
+ // Update the night in the database to add the end time.
+ oldNight.endTimeMilli = System.currentTimeMillis()
+
+ update(oldNight)
+
+ // Set state to navigate to the SleepQualityFragment.
+ _navigateToSleepQuality.value = oldNight
+ }
+ }
+
+ /**
+ * Executes when the CLEAR button is clicked.
+ */
+ fun onClear() {
+ uiScope.launch {
+ // Clear the database table.
+ clear()
+
+ // And clear tonight since it's no longer in the database
+ tonight.value = null
+
+ // Show a snackbar message, because it's friendly.
+ _showSnackbarEvent.value = true
+ }
+ }
+
+ /**
+ * Called when the ViewModel is dismantled.
+ * At this point, we want to cancel all coroutines;
+ * otherwise we end up with processes that have nowhere to return to
+ * using memory and resources.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
new file mode 100755
index 0000000..abcd8ac
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the SleepDatabaseDao and context to the ViewModel.
+ */
+class SleepTrackerViewModelFactory(
+ private val dataSource: SleepDatabaseDao,
+ private val application: Application) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
+ return SleepTrackerViewModel(dataSource, application) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewFundamentals/app/src/main/res/anim/slide_in_right.xml b/RecyclerViewFundamentals/app/src/main/res/anim/slide_in_right.xml
new file mode 100755
index 0000000..49f7779
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/anim/slide_in_right.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewFundamentals/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
new file mode 100755
index 0000000..0b1f6bd
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_0.xml b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_0.xml
new file mode 100755
index 0000000..91c0b47
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_0.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_1.xml b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_1.xml
new file mode 100755
index 0000000..8cc34f7
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_1.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_2.xml b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_2.xml
new file mode 100755
index 0000000..0b7b818
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_2.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_3.xml b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_3.xml
new file mode 100755
index 0000000..677b9ae
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_3.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_4.xml b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_4.xml
new file mode 100755
index 0000000..39eab52
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_4.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_5.xml b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_5.xml
new file mode 100755
index 0000000..f23a2ae
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_5.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_active.xml b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_active.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/drawable/ic_sleep_active.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/font/roboto.xml b/RecyclerViewFundamentals/app/src/main/res/font/roboto.xml
new file mode 100755
index 0000000..2641caf
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/font/roboto.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/layout/activity_main.xml b/RecyclerViewFundamentals/app/src/main/res/layout/activity_main.xml
new file mode 100755
index 0000000..a21679c
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/layout/fragment_sleep_quality.xml b/RecyclerViewFundamentals/app/src/main/res/layout/fragment_sleep_quality.xml
new file mode 100755
index 0000000..ab0ba0c
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/layout/fragment_sleep_quality.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/layout/fragment_sleep_tracker.xml b/RecyclerViewFundamentals/app/src/main/res/layout/fragment_sleep_tracker.xml
new file mode 100755
index 0000000..66f590b
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/layout/fragment_sleep_tracker.xml
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/layout/list_item_sleep_night.xml b/RecyclerViewFundamentals/app/src/main/res/layout/list_item_sleep_night.xml
new file mode 100755
index 0000000..8e304d5
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/layout/list_item_sleep_night.xml
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewFundamentals/app/src/main/res/layout/text_item_view.xml b/RecyclerViewFundamentals/app/src/main/res/layout/text_item_view.xml
new file mode 100644
index 0000000..855c9d6
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/layout/text_item_view.xml
@@ -0,0 +1,22 @@
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml b/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml b/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..898f3ed
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dffca36
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..e5e92ac
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..ae7a1a2
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..64ba76f
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dae5e08
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1250b4c
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..5646308
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..e5ed465
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..14ed0af
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..fd07714
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..86c0074
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..b0907ca
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..d8ae031
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..ae4d8d3
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..37a4a99
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..2c18de9
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..beed3cd
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1fc200c
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..1125b8c
Binary files /dev/null and b/RecyclerViewFundamentals/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewFundamentals/app/src/main/res/navigation/navigation.xml b/RecyclerViewFundamentals/app/src/main/res/navigation/navigation.xml
new file mode 100755
index 0000000..41323ab
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/navigation/navigation.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/values/colors.xml b/RecyclerViewFundamentals/app/src/main/res/values/colors.xml
new file mode 100755
index 0000000..fe8cd82
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ #6ab343
+ #388310
+ #6ab343
+
+ #388310
+ #f0f0f0
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/values/dimens.xml b/RecyclerViewFundamentals/app/src/main/res/values/dimens.xml
new file mode 100755
index 0000000..5385c2e
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/values/dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+ 16dp
+ 64dp
+ 20sp
+ 48dp
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/values/font_certs.xml b/RecyclerViewFundamentals/app/src/main/res/values/font_certs.xml
new file mode 100755
index 0000000..98db6fb
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/values/font_certs.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ @array/com_google_android_gms_fonts_certs_dev
+ @array/com_google_android_gms_fonts_certs_prod
+
+
+
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+
+
+
+
+ MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
+
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/values/preloaded_fonts.xml b/RecyclerViewFundamentals/app/src/main/res/values/preloaded_fonts.xml
new file mode 100755
index 0000000..0a2c03c
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/values/preloaded_fonts.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ @font/roboto
+
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/values/strings.xml b/RecyclerViewFundamentals/app/src/main/res/values/strings.xml
new file mode 100755
index 0000000..223c807
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/values/strings.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Track My Sleep Quality
+ SleepTrackerFragment
+ SleepQualityFragment
+
+
+ Start
+ Stop
+ Clear
+
+
+ Sleep Quality 0
+ Sleep Quality 1
+ Sleep Quality 2
+ Sleep Quality 3
+ Sleep Quality 4
+ Sleep Quality 5
+
+
+ HERE IS YOUR SLEEP DATA]]>
+ Start:]]>
+ End:]]>
+ Quality:]]>
+ Hours:Minutes:Seconds]]>
+
+
+ How was your sleep?
+ Very bad
+ Poor
+ So-so
+ OK
+ Pretty good
+ Excellent!
+
+
+ All your data is gone forever.
+
+ Sleep Results
+ Close
+ %d minutes on %s
+ %d hours on %s
+ %d seconds on %s
+
diff --git a/RecyclerViewFundamentals/app/src/main/res/values/styles.xml b/RecyclerViewFundamentals/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..76369f2
--- /dev/null
+++ b/RecyclerViewFundamentals/app/src/main/res/values/styles.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewFundamentals/build.gradle b/RecyclerViewFundamentals/build.gradle
new file mode 100755
index 0000000..c861b85
--- /dev/null
+++ b/RecyclerViewFundamentals/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019, 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.3.10'
+ archLifecycleVersion = '1.1.1'
+ room_version = '2.0.0'
+ coroutine_version = '1.0.0'
+ gradleVersion = '3.4.0'
+ navigationVersion = '1.0.0-alpha07'
+ dataBindingCompilerVersion = gradleVersion // Always need to be the same.
+ }
+
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath "com.android.tools.build:gradle:$gradleVersion"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
+
+ // 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
+}
diff --git a/RecyclerViewFundamentals/gradle.properties b/RecyclerViewFundamentals/gradle.properties
new file mode 100755
index 0000000..9592636
--- /dev/null
+++ b/RecyclerViewFundamentals/gradle.properties
@@ -0,0 +1,15 @@
+# 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
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/RecyclerViewFundamentals/gradle/wrapper/gradle-wrapper.jar b/RecyclerViewFundamentals/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..f6b961f
Binary files /dev/null and b/RecyclerViewFundamentals/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/RecyclerViewFundamentals/gradle/wrapper/gradle-wrapper.properties b/RecyclerViewFundamentals/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..6566bca
--- /dev/null
+++ b/RecyclerViewFundamentals/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue May 14 16:53:07 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/RecyclerViewFundamentals/gradlew b/RecyclerViewFundamentals/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/RecyclerViewFundamentals/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/RecyclerViewFundamentals/gradlew.bat b/RecyclerViewFundamentals/gradlew.bat
new file mode 100755
index 0000000..e95643d
--- /dev/null
+++ b/RecyclerViewFundamentals/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/RecyclerViewFundamentals/settings.gradle b/RecyclerViewFundamentals/settings.gradle
new file mode 100755
index 0000000..e7b4def
--- /dev/null
+++ b/RecyclerViewFundamentals/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/RecyclerViewGridLayout/README.md b/RecyclerViewGridLayout/README.md
new file mode 100755
index 0000000..594abe9
--- /dev/null
+++ b/RecyclerViewGridLayout/README.md
@@ -0,0 +1,54 @@
+TrackMySleepQuality with RecyclerView - Solution Code for 7.3
+============================================================
+
+Solution code for Android Kotlin Fundamentals Codelab 7.3 GridLayout with RecyclerView
+
+Introduction
+------------
+
+TrackMySleepQuality is an app for recording sleep data for each night.
+You can record a start and stop time, assign a quality rating, and clear the database.
+
+Learn how to display items in a grid in a RecyclerView in your Android Kotlin apps.
+
+
+Pre-requisites
+--------------
+
+You should be familiar with:
+
+* Building a basic user interface (UI) using an activity, fragments, and views.
+* Navigating between fragments, and using safeArgs to pass data between fragments.
+* Using view models, view model factories, transformations, and LiveData and their observers.
+* Creating a Room database, creating a DAO, and defining entities.
+* Using coroutines for database tasks and other long-running tasks.
+* How to implement a basic RecyclerView with an Adapter, ViewHolder, and item layout.
+* How to implement data binding for RecyclerView.
+* How to create and use binding adapters to transform data.
+
+
+Getting Started
+---------------
+
+1. Download and run the app.
+
+License
+-------
+
+Copyright 2019 Google, 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.
+
diff --git a/RecyclerViewGridLayout/app/.gitignore b/RecyclerViewGridLayout/app/.gitignore
new file mode 100755
index 0000000..796b96d
--- /dev/null
+++ b/RecyclerViewGridLayout/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/RecyclerViewGridLayout/app/build.gradle b/RecyclerViewGridLayout/app/build.gradle
new file mode 100755
index 0000000..89f56de
--- /dev/null
+++ b/RecyclerViewGridLayout/app/build.gradle
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019, 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: 'kotlin-kapt'
+apply plugin: 'androidx.navigation.safeargs'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.example.android.trackmysleepqualityrecyclerview"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ // Enables data binding.
+ dataBinding {
+ enabled = true
+ }
+
+}
+
+dependencies {
+
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ // Support libraries
+ implementation "androidx.appcompat:appcompat:1.0.0"
+ implementation "androidx.fragment:fragment:1.0.0"
+ implementation "androidx.constraintlayout:constraintlayout:2.0.0-alpha2"
+
+ // Android KTX
+ implementation 'androidx.core:core-ktx:1.0.1'
+
+ // Room and Lifecycle dependencies
+ implementation "androidx.room:room-runtime:$room_version"
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ kapt "androidx.room:room-compiler:$room_version"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
+
+ // Coroutines
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
+
+ // Navigation
+ implementation "android.arch.navigation:navigation-fragment-ktx:$navigationVersion"
+ implementation "android.arch.navigation:navigation-ui-ktx:$navigationVersion"
+
+ // Testing
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
+}
+
diff --git a/RecyclerViewGridLayout/app/proguard-rules.pro b/RecyclerViewGridLayout/app/proguard-rules.pro
new file mode 100755
index 0000000..f1b4245
--- /dev/null
+++ b/RecyclerViewGridLayout/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/RecyclerViewGridLayout/app/src/main/AndroidManifest.xml b/RecyclerViewGridLayout/app/src/main/AndroidManifest.xml
new file mode 100755
index 0000000..58a0b98
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/ic_launcher_sleep_tracker-web.png b/RecyclerViewGridLayout/app/src/main/ic_launcher_sleep_tracker-web.png
new file mode 100755
index 0000000..668bfae
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/ic_launcher_sleep_tracker-web.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
new file mode 100755
index 0000000..f06095d
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_main)
+ }
+}
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/Util.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
new file mode 100755
index 0000000..bf2f542
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.annotation.SuppressLint
+import android.content.res.Resources
+import android.os.Build
+import android.text.Html
+import android.text.Spanned
+import android.view.View
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.trackmysleepquality.database.SleepNight
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+/**
+ * These functions create a formatted string that can be set in a TextView.
+ */
+
+private val ONE_MINUTE_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES)
+private val ONE_HOUR_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)
+
+/**
+ * Convert a duration to a formatted string for display.
+ *
+ * Examples:
+ *
+ * 6 seconds on Wednesday
+ * 2 minutes on Monday
+ * 40 hours on Thursday
+ *
+ * @param startTimeMilli the start of the interval
+ * @param endTimeMilli the end of the interval
+ * @param res resources used to load formatted strings
+ */
+fun convertDurationToFormatted(startTimeMilli: Long, endTimeMilli: Long, res: Resources): String {
+ val durationMilli = endTimeMilli - startTimeMilli
+ val weekdayString = SimpleDateFormat("EEEE", Locale.getDefault()).format(startTimeMilli)
+ return when {
+ durationMilli < ONE_MINUTE_MILLIS -> {
+ val seconds = TimeUnit.SECONDS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.seconds_length, seconds, weekdayString)
+ }
+ durationMilli < ONE_HOUR_MILLIS -> {
+ val minutes = TimeUnit.MINUTES.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.minutes_length, minutes, weekdayString)
+ }
+ else -> {
+ val hours = TimeUnit.HOURS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.hours_length, hours, weekdayString)
+ }
+ }
+}
+
+/**
+ * Returns a string representing the numeric quality rating.
+ */
+fun convertNumericQualityToString(quality: Int, resources: Resources): String {
+ var qualityString = resources.getString(R.string.three_ok)
+ when (quality) {
+ -1 -> qualityString = "--"
+ 0 -> qualityString = resources.getString(R.string.zero_very_bad)
+ 1 -> qualityString = resources.getString(R.string.one_poor)
+ 2 -> qualityString = resources.getString(R.string.two_soso)
+ 4 -> qualityString = resources.getString(R.string.four_pretty_good)
+ 5 -> qualityString = resources.getString(R.string.five_excellent)
+ }
+ return qualityString
+}
+
+
+/**
+ * Take the Long milliseconds returned by the system and stored in Room,
+ * and convert it to a nicely formatted string for display.
+ *
+ * EEEE - Display the long letter version of the weekday
+ * MMM - Display the letter abbreviation of the nmotny
+ * dd-yyyy - day in month and full year numerically
+ * HH:mm - Hours and minutes in 24hr format
+ */
+@SuppressLint("SimpleDateFormat")
+fun convertLongToDateString(systemTime: Long): String {
+ return SimpleDateFormat("EEEE MMM-dd-yyyy' Time: 'HH:mm")
+ .format(systemTime).toString()
+}
+
+/**
+ * Takes a list of SleepNights and converts and formats it into one string for display.
+ *
+ * For display in a TextView, we have to supply one string, and styles are per TextView, not
+ * applicable per word. So, we build a formatted string using HTML. This is handy, but we will
+ * learn a better way of displaying this data in a future lesson.
+ *
+ * @param nights - List of all SleepNights in the database.
+ * @param resources - Resources object for all the resources defined for our app.
+ *
+ * @return Spanned - An interface for text that has formatting attached to it.
+ * See: https://developer.android.com/reference/android/text/Spanned
+ */
+fun formatNights(nights: List, resources: Resources): Spanned {
+ val sb = StringBuilder()
+ sb.apply {
+ append(resources.getString(R.string.title))
+ nights.forEach {
+ append(" ")
+ append(resources.getString(R.string.start_time))
+ append("\t${convertLongToDateString(it.startTimeMilli)} ")
+ if (it.endTimeMilli != it.startTimeMilli) {
+ append(resources.getString(R.string.end_time))
+ append("\t${convertLongToDateString(it.endTimeMilli)} ")
+ append(resources.getString(R.string.quality))
+ append("\t${convertNumericQualityToString(it.sleepQuality, resources)} ")
+ append(resources.getString(R.string.hours_slept))
+ // Hours
+ append("\t ${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60 / 60}:")
+ // Minutes
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60}:")
+ // Seconds
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000}
")
+ }
+ }
+ }
+ // fromHtml is deprecated for target API without a flag, but since our minSDK is 19, we
+ // can't use the newer version, which requires minSDK of 24
+ //https://developer.android.com/reference/android/text/Html#fromHtml(java.lang.String,%20int)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return Html.fromHtml(sb.toString(), Html.FROM_HTML_MODE_LEGACY)
+ } else {
+ @Suppress("DEPRECATION")
+ return Html.fromHtml(sb.toString())
+ }
+}
+
+/**
+ * ViewHolder that holds a single [TextView].
+ *
+ * A ViewHolder holds a view for the [RecyclerView] as well as providing additional information
+ * to the RecyclerView such as where on the screen it was last drawn during scrolling.
+ */
+class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
\ No newline at end of file
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
new file mode 100755
index 0000000..483642b
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+
+/**
+ * A database that stores SleepNight information.
+ * And a global method to get access to the database.
+ *
+ * This pattern is pretty much the same for any database,
+ * so you can reuse it.
+ */
+@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
+abstract class SleepDatabase : RoomDatabase() {
+
+ /**
+ * Connects the database to the DAO.
+ */
+ abstract val sleepDatabaseDao: SleepDatabaseDao
+
+ /**
+ * Define a companion object, this allows us to add functions on the SleepDatabase class.
+ *
+ * For example, clients can call `SleepDatabase.getInstance(context)` to instantiate
+ * a new SleepDatabase.
+ */
+ companion object {
+ /**
+ * INSTANCE will keep a reference to any database returned via getInstance.
+ *
+ * This will help us avoid repeatedly initializing the database, which is expensive.
+ *
+ * The value of a volatile variable will never be cached, and all writes and
+ * reads will be done to and from the main memory. It means that changes made by one
+ * thread to shared data are visible to other threads.
+ */
+ @Volatile
+ private var INSTANCE: SleepDatabase? = null
+
+ /**
+ * Helper function to get the database.
+ *
+ * If a database has already been retrieved, the previous database will be returned.
+ * Otherwise, create a new database.
+ *
+ * This function is threadsafe, and callers should cache the result for multiple database
+ * calls to avoid overhead.
+ *
+ * This is an example of a simple Singleton pattern that takes another Singleton as an
+ * argument in Kotlin.
+ *
+ * To learn more about Singleton read the wikipedia article:
+ * https://en.wikipedia.org/wiki/Singleton_pattern
+ *
+ * @param context The application context Singleton, used to get access to the filesystem.
+ */
+ fun getInstance(context: Context): SleepDatabase {
+ // Multiple threads can ask for the database at the same time, ensure we only initialize
+ // it once by using synchronized. Only one thread may enter a synchronized block at a
+ // time.
+ synchronized(this) {
+
+ // Copy the current value of INSTANCE to a local variable so Kotlin can smart cast.
+ // Smart cast is only available to local variables.
+ var instance = INSTANCE
+
+ // If instance is `null` make a new database instance.
+ if (instance == null) {
+ instance = Room.databaseBuilder(
+ context.applicationContext,
+ SleepDatabase::class.java,
+ "sleep_history_database"
+ )
+ // Wipes and rebuilds instead of migrating if no Migration object.
+ // Migration is not part of this lesson. You can learn more about
+ // migration with Room in this blog post:
+ // https://medium.com/androiddevelopers/understanding-migrations-with-room-f01e04b07929
+ .fallbackToDestructiveMigration()
+ .build()
+ // Assign INSTANCE to the newly created database.
+ INSTANCE = instance
+ }
+
+ // Return instance; smart cast to be non-null.
+ return instance
+ }
+ }
+ }
+}
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
new file mode 100755
index 0000000..49f3ea1
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Update
+
+
+/**
+ * Defines methods for using the SleepNight class with Room.
+ */
+@Dao
+interface SleepDatabaseDao {
+
+ @Insert
+ fun insert(night: SleepNight)
+
+ /**
+ * When updating a row with a value already set in a column,
+ * replaces the old value with the new one.
+ *
+ * @param night new value to write
+ */
+ @Update
+ fun update(night: SleepNight)
+
+ /**
+ * Selects and returns the row that matches the supplied start time, which is our key.
+ *
+ * @param key startTimeMilli to match
+ */
+ @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
+ fun get(key: Long): SleepNight
+
+ /**
+ * Deletes all values from the table.
+ *
+ * This does not delete the table, only its contents.
+ */
+ @Query("DELETE FROM daily_sleep_quality_table")
+ fun clear()
+
+ /**
+ * Selects and returns all rows in the table,
+ *
+ * sorted by start time in descending order.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
+ fun getAllNights(): LiveData>
+
+ /**
+ * Selects and returns the latest night.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
+ fun getTonight(): SleepNight?
+}
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
new file mode 100755
index 0000000..b776812
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * Represents one night's sleep through start, end times, and the sleep quality.
+ */
+@Entity(tableName = "daily_sleep_quality_table")
+data class SleepNight(
+ @PrimaryKey(autoGenerate = true)
+ var nightId: Long = 0L,
+
+ @ColumnInfo(name = "start_time_milli")
+ val startTimeMilli: Long = System.currentTimeMillis(),
+
+ @ColumnInfo(name = "end_time_milli")
+ var endTimeMilli: Long = startTimeMilli,
+
+ @ColumnInfo(name = "quality_rating")
+ var sleepQuality: Int = -1)
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
new file mode 100755
index 0000000..db22082
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepQualityBinding
+
+/**
+ * Fragment that displays a list of clickable icons,
+ * each representing a sleep quality rating.
+ * Once the user taps an icon, the quality is set in the current sleepNight
+ * and the database is updated.
+ */
+class SleepQualityFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepQualityViewModel] to the
+ * [FragmentSleepQualityBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepQualityBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_quality, container, false)
+
+ val application = requireNotNull(this.activity).application
+ val arguments = SleepQualityFragmentArgs.fromBundle(arguments)
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepQualityViewModelFactory(arguments.sleepNightKey, dataSource)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepQualityViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepQualityViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepQualityViewModel = sleepQualityViewModel
+
+ // Add an Observer to the state variable for Navigating when a Quality icon is tapped.
+ sleepQualityViewModel.navigateToSleepTracker.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ this.findNavController().navigate(
+ SleepQualityFragmentDirections.actionSleepQualityFragmentToSleepTrackerFragment())
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepQualityViewModel.doneNavigating()
+ }
+ })
+
+ return binding.root
+ }
+}
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
new file mode 100755
index 0000000..31de35e
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepQualityFragment.
+ *
+ * @param sleepNightKey The key of the current night we are working on.
+ */
+class SleepQualityViewModel(
+ private val sleepNightKey: Long = 0L,
+ dataSource: SleepDatabaseDao) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via its SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine setup variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private val viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this scope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ /**
+ * Variable that tells the fragment whether it should navigate to [SleepTrackerFragment].
+ *
+ * This is `private` because we don't want to expose the ability to set [MutableLiveData] to
+ * the [Fragment]
+ */
+ private val _navigateToSleepTracker = MutableLiveData()
+
+ /**
+ * When true immediately navigate back to the [SleepTrackerFragment]
+ */
+ val navigateToSleepTracker: LiveData
+ get() = _navigateToSleepTracker
+
+ /**
+ * Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work.
+ *
+ * onCleared() gets called when the ViewModel is destroyed.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+
+ /**
+ * Call this immediately after navigating to [SleepTrackerFragment]
+ */
+ fun doneNavigating() {
+ _navigateToSleepTracker.value = null
+ }
+
+ /**
+ * Sets the sleep quality and updates the database.
+ *
+ * Then navigates back to the SleepTrackerFragment.
+ */
+ fun onSetSleepQuality(quality: Int) {
+ uiScope.launch {
+ // IO is a thread pool for running operations that access the disk, such as
+ // our Room database.
+ withContext(Dispatchers.IO) {
+ val tonight = database.get(sleepNightKey)
+ tonight.sleepQuality = quality
+ database.update(tonight)
+ }
+
+ // Setting this state variable to true will alert the observer and trigger navigation.
+ _navigateToSleepTracker.value = true
+ }
+ }
+}
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
new file mode 100755
index 0000000..3c6a35d
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the key for the night and the SleepDatabaseDao to the ViewModel.
+ */
+class SleepQualityViewModelFactory(
+ private val sleepNightKey: Long,
+ private val dataSource: SleepDatabaseDao) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepQualityViewModel::class.java)) {
+ return SleepQualityViewModel(sleepNightKey, dataSource) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt
new file mode 100644
index 0000000..e015165
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.databinding.BindingAdapter
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.convertDurationToFormatted
+import com.example.android.trackmysleepquality.convertNumericQualityToString
+import com.example.android.trackmysleepquality.database.SleepNight
+
+
+@BindingAdapter("sleepDurationFormatted")
+fun TextView.setSleepDurationFormatted(item: SleepNight) {
+ text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
+}
+
+
+@BindingAdapter("sleepQualityString")
+fun TextView.setSleepQualityString(item: SleepNight) {
+ text = convertNumericQualityToString(item.sleepQuality, context.resources)
+}
+
+
+@BindingAdapter("sleepImage")
+fun ImageView.setSleepImage(item: SleepNight) {
+ setImageResource(when (item.sleepQuality) {
+ 0 -> R.drawable.ic_sleep_0
+ 1 -> R.drawable.ic_sleep_1
+ 2 -> R.drawable.ic_sleep_2
+
+ 3 -> R.drawable.ic_sleep_3
+
+ 4 -> R.drawable.ic_sleep_4
+ 5 -> R.drawable.ic_sleep_5
+ else -> R.drawable.ic_sleep_active
+ })
+}
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
new file mode 100644
index 0000000..31bb0f0
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+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.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.convertDurationToFormatted
+import com.example.android.trackmysleepquality.convertNumericQualityToString
+import com.example.android.trackmysleepquality.database.SleepNight
+import com.example.android.trackmysleepquality.databinding.ListItemSleepNightBinding
+
+class SleepNightAdapter : ListAdapter(SleepNightDiffCallback()) {
+
+ override fun onBindViewHolder(holder: ViewHolder, position: Int) {
+ val item = getItem(position)
+ holder.bind(item)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+ return ViewHolder.from(parent)
+ }
+
+ class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){
+
+ fun bind(item: SleepNight) {
+ binding.sleep = item
+ binding.executePendingBindings()
+ }
+
+ companion object {
+ fun from(parent: ViewGroup): ViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ val binding = ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
+ return ViewHolder(binding)
+ }
+ }
+ }
+}
+
+
+class SleepNightDiffCallback : DiffUtil.ItemCallback() {
+
+ override fun areItemsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
+ return oldItem.nightId == newItem.nightId
+ }
+
+
+ override fun areContentsTheSame(oldItem: SleepNight, newItem: SleepNight): Boolean {
+ return oldItem == newItem
+ }
+
+
+}
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
new file mode 100755
index 0000000..e4eddba
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.GridLayoutManager
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepTrackerBinding
+import com.google.android.material.snackbar.Snackbar
+
+/**
+ * A fragment with buttons to record start and end times for sleep, which are saved in
+ * a database. Cumulative data is displayed in a simple scrollable TextView.
+ * (Because we have not learned about RecyclerView yet.)
+ * The Clear button will clear all data from the database.
+ */
+class SleepTrackerFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepTrackerViewModel] to the
+ * [FragmentSleepTrackerBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_tracker, container, false)
+
+ val application = requireNotNull(this.activity).application
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepTrackerViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepTrackerViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepTrackerViewModel = sleepTrackerViewModel
+
+ val adapter = SleepNightAdapter()
+ binding.sleepList.adapter = adapter
+
+ sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
+ it?.let {
+ adapter.submitList(it)
+ }
+ })
+
+ // Specify the current activity as the lifecycle owner of the binding.
+ // This is necessary so that the binding can observe LiveData updates.
+ binding.setLifecycleOwner(this)
+
+ // Add an Observer on the state variable for showing a Snackbar message
+ // when the CLEAR button is pressed.
+ sleepTrackerViewModel.showSnackBarEvent.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ Snackbar.make(
+ activity!!.findViewById(android.R.id.content),
+ getString(R.string.cleared_message),
+ Snackbar.LENGTH_SHORT // How long to display the message.
+ ).show()
+ // Reset state to make sure the toast is only shown once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneShowingSnackbar()
+ }
+ })
+
+ // Add an Observer on the state variable for Navigating when STOP button is pressed.
+ sleepTrackerViewModel.navigateToSleepQuality.observe(this, Observer { night ->
+ night?.let {
+ // We need to get the navController from this, because button is not ready, and it
+ // just has to be a view. For some reason, this only matters if we hit stop again
+ // after using the back button, not if we hit stop and choose a quality.
+ // Also, in the Navigation Editor, for Quality -> Tracker, check "Inclusive" for
+ // popping the stack to get the correct behavior if we press stop multiple times
+ // followed by back.
+ // Also: https://stackoverflow.com/questions/28929637/difference-and-uses-of-oncreate-oncreateview-and-onactivitycreated-in-fra
+ this.findNavController().navigate(
+ SleepTrackerFragmentDirections
+ .actionSleepTrackerFragmentToSleepQualityFragment(night.nightId))
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneNavigating()
+ }
+ })
+
+ val manager = GridLayoutManager(activity, 3)
+ binding.sleepList.layoutManager = manager
+
+
+ return binding.root
+ }
+}
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
new file mode 100755
index 0000000..4bc4299
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
@@ -0,0 +1,249 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.app.Application
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Transformations
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import com.example.android.trackmysleepquality.database.SleepNight
+import com.example.android.trackmysleepquality.formatNights
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepTrackerFragment.
+ */
+class SleepTrackerViewModel(
+ dataSource: SleepDatabaseDao,
+ application: Application) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private var viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this uiScope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ private var tonight = MutableLiveData()
+
+ val nights = database.getAllNights()
+
+ /**
+ * Converted nights to Spanned for displaying.
+ */
+ val nightsString = Transformations.map(nights) { nights ->
+ formatNights(nights, application.resources)
+ }
+
+ /**
+ * If tonight has not been set, then the START button should be visible.
+ */
+ val startButtonVisible = Transformations.map(tonight) {
+ null == it
+ }
+
+ /**
+ * If tonight has been set, then the STOP button should be visible.
+ */
+ val stopButtonVisible = Transformations.map(tonight) {
+ null != it
+ }
+
+ /**
+ * If there are any nights in the database, show the CLEAR button.
+ */
+ val clearButtonVisible = Transformations.map(nights) {
+ it?.isNotEmpty()
+ }
+
+
+ /**
+ * Request a toast by setting this value to true.
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private var _showSnackbarEvent = MutableLiveData()
+
+ /**
+ * If this is true, immediately `show()` a toast and call `doneShowingSnackbar()`.
+ */
+ val showSnackBarEvent: LiveData
+ get() = _showSnackbarEvent
+
+ /**
+ * Variable that tells the Fragment to navigate to a specific [SleepQualityFragment]
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private val _navigateToSleepQuality = MutableLiveData()
+
+ /**
+ * If this is non-null, immediately navigate to [SleepQualityFragment] and call [doneNavigating]
+ */
+ val navigateToSleepQuality: LiveData
+ get() = _navigateToSleepQuality
+
+ /**
+ * Call this immediately after calling `show()` on a toast.
+ *
+ * It will clear the toast request, so if the user rotates their phone it won't show a duplicate
+ * toast.
+ */
+ fun doneShowingSnackbar() {
+ _showSnackbarEvent.value = null
+ }
+
+ /**
+ * Call this immediately after navigating to [SleepQualityFragment]
+ *
+ * It will clear the navigation request, so if the user rotates their phone it won't navigate
+ * twice.
+ */
+ fun doneNavigating() {
+ _navigateToSleepQuality.value = null
+ }
+
+ init {
+ initializeTonight()
+ }
+
+ private fun initializeTonight() {
+ uiScope.launch {
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Handling the case of the stopped app or forgotten recording,
+ * the start and end times will be the same.j
+ *
+ * If the start time and end time are not the same, then we do not have an unfinished
+ * recording.
+ */
+ private suspend fun getTonightFromDatabase(): SleepNight? {
+ return withContext(Dispatchers.IO) {
+ var night = database.getTonight()
+ if (night?.endTimeMilli != night?.startTimeMilli) {
+ night = null
+ }
+ night
+ }
+ }
+
+ private suspend fun insert(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.insert(night)
+ }
+ }
+
+ private suspend fun update(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.update(night)
+ }
+ }
+
+ private suspend fun clear() {
+ withContext(Dispatchers.IO) {
+ database.clear()
+ }
+ }
+
+ /**
+ * Executes when the START button is clicked.
+ */
+ fun onStart() {
+ uiScope.launch {
+ // Create a new night, which captures the current time,
+ // and insert it into the database.
+ val newNight = SleepNight()
+
+ insert(newNight)
+
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Executes when the STOP button is clicked.
+ */
+ fun onStop() {
+ uiScope.launch {
+ // In Kotlin, the return@label syntax is used for specifying which function among
+ // several nested ones this statement returns from.
+ // In this case, we are specifying to return from launch().
+ val oldNight = tonight.value ?: return@launch
+
+ // Update the night in the database to add the end time.
+ oldNight.endTimeMilli = System.currentTimeMillis()
+
+ update(oldNight)
+
+ // Set state to navigate to the SleepQualityFragment.
+ _navigateToSleepQuality.value = oldNight
+ }
+ }
+
+ /**
+ * Executes when the CLEAR button is clicked.
+ */
+ fun onClear() {
+ uiScope.launch {
+ // Clear the database table.
+ clear()
+
+ // And clear tonight since it's no longer in the database
+ tonight.value = null
+
+ // Show a snackbar message, because it's friendly.
+ _showSnackbarEvent.value = true
+ }
+ }
+
+ /**
+ * Called when the ViewModel is dismantled.
+ * At this point, we want to cancel all coroutines;
+ * otherwise we end up with processes that have nowhere to return to
+ * using memory and resources.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
new file mode 100755
index 0000000..abcd8ac
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the SleepDatabaseDao and context to the ViewModel.
+ */
+class SleepTrackerViewModelFactory(
+ private val dataSource: SleepDatabaseDao,
+ private val application: Application) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
+ return SleepTrackerViewModel(dataSource, application) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewGridLayout/app/src/main/res/anim/slide_in_right.xml b/RecyclerViewGridLayout/app/src/main/res/anim/slide_in_right.xml
new file mode 100755
index 0000000..49f7779
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/anim/slide_in_right.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewGridLayout/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
new file mode 100755
index 0000000..0b1f6bd
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_0.xml b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_0.xml
new file mode 100755
index 0000000..91c0b47
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_0.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_1.xml b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_1.xml
new file mode 100755
index 0000000..8cc34f7
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_1.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_2.xml b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_2.xml
new file mode 100755
index 0000000..0b7b818
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_2.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_3.xml b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_3.xml
new file mode 100755
index 0000000..677b9ae
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_3.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_4.xml b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_4.xml
new file mode 100755
index 0000000..39eab52
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_4.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_5.xml b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_5.xml
new file mode 100755
index 0000000..f23a2ae
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_5.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_active.xml b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_active.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/drawable/ic_sleep_active.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/font/roboto.xml b/RecyclerViewGridLayout/app/src/main/res/font/roboto.xml
new file mode 100755
index 0000000..2641caf
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/font/roboto.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/layout/activity_main.xml b/RecyclerViewGridLayout/app/src/main/res/layout/activity_main.xml
new file mode 100755
index 0000000..a21679c
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/layout/fragment_sleep_quality.xml b/RecyclerViewGridLayout/app/src/main/res/layout/fragment_sleep_quality.xml
new file mode 100755
index 0000000..ab0ba0c
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/layout/fragment_sleep_quality.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/layout/fragment_sleep_tracker.xml b/RecyclerViewGridLayout/app/src/main/res/layout/fragment_sleep_tracker.xml
new file mode 100755
index 0000000..d21055b
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/layout/fragment_sleep_tracker.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/layout/list_item_sleep_night.xml b/RecyclerViewGridLayout/app/src/main/res/layout/list_item_sleep_night.xml
new file mode 100755
index 0000000..a574a53
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/layout/list_item_sleep_night.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewGridLayout/app/src/main/res/layout/list_item_sleep_night_linear.xml b/RecyclerViewGridLayout/app/src/main/res/layout/list_item_sleep_night_linear.xml
new file mode 100755
index 0000000..8d3f008
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/layout/list_item_sleep_night_linear.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewGridLayout/app/src/main/res/layout/text_item_view.xml b/RecyclerViewGridLayout/app/src/main/res/layout/text_item_view.xml
new file mode 100644
index 0000000..855c9d6
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/layout/text_item_view.xml
@@ -0,0 +1,22 @@
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml b/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml b/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..898f3ed
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dffca36
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..e5e92ac
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..ae7a1a2
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..64ba76f
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dae5e08
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1250b4c
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..5646308
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..e5ed465
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..14ed0af
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..fd07714
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..86c0074
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..b0907ca
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..d8ae031
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..ae4d8d3
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..37a4a99
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..2c18de9
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..beed3cd
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1fc200c
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..1125b8c
Binary files /dev/null and b/RecyclerViewGridLayout/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewGridLayout/app/src/main/res/navigation/navigation.xml b/RecyclerViewGridLayout/app/src/main/res/navigation/navigation.xml
new file mode 100755
index 0000000..41323ab
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/navigation/navigation.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/values/colors.xml b/RecyclerViewGridLayout/app/src/main/res/values/colors.xml
new file mode 100755
index 0000000..fe8cd82
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ #6ab343
+ #388310
+ #6ab343
+
+ #388310
+ #f0f0f0
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/values/dimens.xml b/RecyclerViewGridLayout/app/src/main/res/values/dimens.xml
new file mode 100755
index 0000000..5385c2e
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/values/dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+ 16dp
+ 64dp
+ 20sp
+ 48dp
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/values/font_certs.xml b/RecyclerViewGridLayout/app/src/main/res/values/font_certs.xml
new file mode 100755
index 0000000..98db6fb
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/values/font_certs.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ @array/com_google_android_gms_fonts_certs_dev
+ @array/com_google_android_gms_fonts_certs_prod
+
+
+
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+
+
+
+
+ MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
+
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/values/preloaded_fonts.xml b/RecyclerViewGridLayout/app/src/main/res/values/preloaded_fonts.xml
new file mode 100755
index 0000000..0a2c03c
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/values/preloaded_fonts.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ @font/roboto
+
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/values/strings.xml b/RecyclerViewGridLayout/app/src/main/res/values/strings.xml
new file mode 100755
index 0000000..aa58605
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/values/strings.xml
@@ -0,0 +1,60 @@
+
+
+
+
+
+ Track My Sleep Quality
+ SleepTrackerFragment
+ SleepQualityFragment
+
+
+ Start
+ Stop
+ Clear
+
+
+ Sleep Quality 0
+ Sleep Quality 1
+ Sleep Quality 2
+ Sleep Quality 3
+ Sleep Quality 4
+ Sleep Quality 5
+
+
+ HERE IS YOUR SLEEP DATA]]>
+ Start:]]>
+ End:]]>
+ Quality:]]>
+ Hours:Minutes:Seconds]]>
+
+
+ How was your sleep?
+ Very bad
+ Poor
+ So-so
+ OK
+ Pretty good
+ Excellent!
+
+
+ All your data is gone forever.
+
+ Sleep Results
+ %d minutes on %s
+ %d hours on %s
+ %d seconds on %s
+
diff --git a/RecyclerViewGridLayout/app/src/main/res/values/styles.xml b/RecyclerViewGridLayout/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..76369f2
--- /dev/null
+++ b/RecyclerViewGridLayout/app/src/main/res/values/styles.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewGridLayout/build.gradle b/RecyclerViewGridLayout/build.gradle
new file mode 100755
index 0000000..cd8f0e1
--- /dev/null
+++ b/RecyclerViewGridLayout/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2018, 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.3.10'
+ archLifecycleVersion = '1.1.1'
+ room_version = '2.0.0'
+ coroutine_version = '1.0.0'
+ gradleVersion = '3.4.0'
+ navigationVersion = '1.0.0-alpha07'
+ dataBindingCompilerVersion = gradleVersion // Always need to be the same.
+ }
+
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath "com.android.tools.build:gradle:$gradleVersion"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
+
+ // 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
+}
diff --git a/RecyclerViewGridLayout/gradle.properties b/RecyclerViewGridLayout/gradle.properties
new file mode 100755
index 0000000..9592636
--- /dev/null
+++ b/RecyclerViewGridLayout/gradle.properties
@@ -0,0 +1,15 @@
+# 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
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/RecyclerViewGridLayout/gradle/wrapper/gradle-wrapper.jar b/RecyclerViewGridLayout/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..f6b961f
Binary files /dev/null and b/RecyclerViewGridLayout/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/RecyclerViewGridLayout/gradle/wrapper/gradle-wrapper.properties b/RecyclerViewGridLayout/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..252a7cb
--- /dev/null
+++ b/RecyclerViewGridLayout/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue May 14 16:08:58 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/RecyclerViewGridLayout/gradlew b/RecyclerViewGridLayout/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/RecyclerViewGridLayout/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/RecyclerViewGridLayout/gradlew.bat b/RecyclerViewGridLayout/gradlew.bat
new file mode 100755
index 0000000..e95643d
--- /dev/null
+++ b/RecyclerViewGridLayout/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/RecyclerViewGridLayout/settings.gradle b/RecyclerViewGridLayout/settings.gradle
new file mode 100755
index 0000000..e7b4def
--- /dev/null
+++ b/RecyclerViewGridLayout/settings.gradle
@@ -0,0 +1 @@
+include ':app'
diff --git a/RecyclerViewHeaders/README.md b/RecyclerViewHeaders/README.md
new file mode 100755
index 0000000..3828699
--- /dev/null
+++ b/RecyclerViewHeaders/README.md
@@ -0,0 +1,56 @@
+TrackMySleepQuality with RecyclerView - Solution Code for 7.5
+=============================================================
+
+Solution code for Android Kotlin Fundamentals Codelab 7.5 Headers in RecyclerView
+
+Introduction
+------------
+
+TrackMySleepQuality is an app for recording sleep data for each night.
+You can record a start and stop time, assign a quality rating, and clear the database.
+
+Learn how to add more than one view holder and layout, such as for headers,
+to lists and grids in RecyclerView in your Android Kotlin app.
+
+Pre-requisites
+--------------
+
+You should be familiar with:
+
+* Building a basic user interface (UI) using an activity, fragments, and views.
+* Navigating between fragments, and using safeArgs to pass data between fragments.
+* Using view models, view model factories, transformations, and LiveData and their observers.
+* Creating a Room database, creating a DAO, and defining entities.
+* Using coroutines for database tasks and other long-running tasks.
+* How to implement a basic RecyclerView with an Adapter, ViewHolder, and item layout.
+* How to implement data binding for RecyclerView.
+* How to create and use binding adapters to transform data.
+* How to use GridLayoutManager.
+* How capture and handle clicks on items in a RecyclerView.
+
+
+Getting Started
+---------------
+
+1. Download and run the app.
+
+License
+-------
+
+Copyright 2019 Google, 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.
+
diff --git a/RecyclerViewHeaders/app/.gitignore b/RecyclerViewHeaders/app/.gitignore
new file mode 100755
index 0000000..796b96d
--- /dev/null
+++ b/RecyclerViewHeaders/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/RecyclerViewHeaders/app/build.gradle b/RecyclerViewHeaders/app/build.gradle
new file mode 100755
index 0000000..89f56de
--- /dev/null
+++ b/RecyclerViewHeaders/app/build.gradle
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019, 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: 'kotlin-kapt'
+apply plugin: 'androidx.navigation.safeargs'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.example.android.trackmysleepqualityrecyclerview"
+ minSdkVersion 19
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ vectorDrawables.useSupportLibrary = true
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ // Enables data binding.
+ dataBinding {
+ enabled = true
+ }
+
+}
+
+dependencies {
+
+ implementation fileTree(dir: 'libs', include: ['*.jar'])
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+
+ // Support libraries
+ implementation "androidx.appcompat:appcompat:1.0.0"
+ implementation "androidx.fragment:fragment:1.0.0"
+ implementation "androidx.constraintlayout:constraintlayout:2.0.0-alpha2"
+
+ // Android KTX
+ implementation 'androidx.core:core-ktx:1.0.1'
+
+ // Room and Lifecycle dependencies
+ implementation "androidx.room:room-runtime:$room_version"
+ implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+ kapt "androidx.room:room-compiler:$room_version"
+ implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
+
+ // Coroutines
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
+ implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutine_version"
+
+ // Navigation
+ implementation "android.arch.navigation:navigation-fragment-ktx:$navigationVersion"
+ implementation "android.arch.navigation:navigation-ui-ktx:$navigationVersion"
+
+ // Testing
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'androidx.test:runner:1.1.0-alpha4'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-alpha4'
+}
+
diff --git a/RecyclerViewHeaders/app/proguard-rules.pro b/RecyclerViewHeaders/app/proguard-rules.pro
new file mode 100755
index 0000000..f1b4245
--- /dev/null
+++ b/RecyclerViewHeaders/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/RecyclerViewHeaders/app/src/main/AndroidManifest.xml b/RecyclerViewHeaders/app/src/main/AndroidManifest.xml
new file mode 100755
index 0000000..58a0b98
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/AndroidManifest.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/ic_launcher_sleep_tracker-web.png b/RecyclerViewHeaders/app/src/main/ic_launcher_sleep_tracker-web.png
new file mode 100755
index 0000000..668bfae
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/ic_launcher_sleep_tracker-web.png differ
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
new file mode 100755
index 0000000..f06095d
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.os.Bundle
+import androidx.appcompat.app.AppCompatActivity
+
+
+class MainActivity : AppCompatActivity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ setContentView(R.layout.activity_main)
+ }
+}
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/Util.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
new file mode 100755
index 0000000..bf2f542
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/Util.kt
@@ -0,0 +1,156 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality
+
+import android.annotation.SuppressLint
+import android.content.res.Resources
+import android.os.Build
+import android.text.Html
+import android.text.Spanned
+import android.view.View
+import android.widget.TextView
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.trackmysleepquality.database.SleepNight
+import java.text.SimpleDateFormat
+import java.util.*
+import java.util.concurrent.TimeUnit
+
+/**
+ * These functions create a formatted string that can be set in a TextView.
+ */
+
+private val ONE_MINUTE_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.MINUTES)
+private val ONE_HOUR_MILLIS = TimeUnit.MILLISECONDS.convert(1, TimeUnit.HOURS)
+
+/**
+ * Convert a duration to a formatted string for display.
+ *
+ * Examples:
+ *
+ * 6 seconds on Wednesday
+ * 2 minutes on Monday
+ * 40 hours on Thursday
+ *
+ * @param startTimeMilli the start of the interval
+ * @param endTimeMilli the end of the interval
+ * @param res resources used to load formatted strings
+ */
+fun convertDurationToFormatted(startTimeMilli: Long, endTimeMilli: Long, res: Resources): String {
+ val durationMilli = endTimeMilli - startTimeMilli
+ val weekdayString = SimpleDateFormat("EEEE", Locale.getDefault()).format(startTimeMilli)
+ return when {
+ durationMilli < ONE_MINUTE_MILLIS -> {
+ val seconds = TimeUnit.SECONDS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.seconds_length, seconds, weekdayString)
+ }
+ durationMilli < ONE_HOUR_MILLIS -> {
+ val minutes = TimeUnit.MINUTES.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.minutes_length, minutes, weekdayString)
+ }
+ else -> {
+ val hours = TimeUnit.HOURS.convert(durationMilli, TimeUnit.MILLISECONDS)
+ res.getString(R.string.hours_length, hours, weekdayString)
+ }
+ }
+}
+
+/**
+ * Returns a string representing the numeric quality rating.
+ */
+fun convertNumericQualityToString(quality: Int, resources: Resources): String {
+ var qualityString = resources.getString(R.string.three_ok)
+ when (quality) {
+ -1 -> qualityString = "--"
+ 0 -> qualityString = resources.getString(R.string.zero_very_bad)
+ 1 -> qualityString = resources.getString(R.string.one_poor)
+ 2 -> qualityString = resources.getString(R.string.two_soso)
+ 4 -> qualityString = resources.getString(R.string.four_pretty_good)
+ 5 -> qualityString = resources.getString(R.string.five_excellent)
+ }
+ return qualityString
+}
+
+
+/**
+ * Take the Long milliseconds returned by the system and stored in Room,
+ * and convert it to a nicely formatted string for display.
+ *
+ * EEEE - Display the long letter version of the weekday
+ * MMM - Display the letter abbreviation of the nmotny
+ * dd-yyyy - day in month and full year numerically
+ * HH:mm - Hours and minutes in 24hr format
+ */
+@SuppressLint("SimpleDateFormat")
+fun convertLongToDateString(systemTime: Long): String {
+ return SimpleDateFormat("EEEE MMM-dd-yyyy' Time: 'HH:mm")
+ .format(systemTime).toString()
+}
+
+/**
+ * Takes a list of SleepNights and converts and formats it into one string for display.
+ *
+ * For display in a TextView, we have to supply one string, and styles are per TextView, not
+ * applicable per word. So, we build a formatted string using HTML. This is handy, but we will
+ * learn a better way of displaying this data in a future lesson.
+ *
+ * @param nights - List of all SleepNights in the database.
+ * @param resources - Resources object for all the resources defined for our app.
+ *
+ * @return Spanned - An interface for text that has formatting attached to it.
+ * See: https://developer.android.com/reference/android/text/Spanned
+ */
+fun formatNights(nights: List, resources: Resources): Spanned {
+ val sb = StringBuilder()
+ sb.apply {
+ append(resources.getString(R.string.title))
+ nights.forEach {
+ append(" ")
+ append(resources.getString(R.string.start_time))
+ append("\t${convertLongToDateString(it.startTimeMilli)} ")
+ if (it.endTimeMilli != it.startTimeMilli) {
+ append(resources.getString(R.string.end_time))
+ append("\t${convertLongToDateString(it.endTimeMilli)} ")
+ append(resources.getString(R.string.quality))
+ append("\t${convertNumericQualityToString(it.sleepQuality, resources)} ")
+ append(resources.getString(R.string.hours_slept))
+ // Hours
+ append("\t ${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60 / 60}:")
+ // Minutes
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000 / 60}:")
+ // Seconds
+ append("${it.endTimeMilli.minus(it.startTimeMilli) / 1000}
")
+ }
+ }
+ }
+ // fromHtml is deprecated for target API without a flag, but since our minSDK is 19, we
+ // can't use the newer version, which requires minSDK of 24
+ //https://developer.android.com/reference/android/text/Html#fromHtml(java.lang.String,%20int)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ return Html.fromHtml(sb.toString(), Html.FROM_HTML_MODE_LEGACY)
+ } else {
+ @Suppress("DEPRECATION")
+ return Html.fromHtml(sb.toString())
+ }
+}
+
+/**
+ * ViewHolder that holds a single [TextView].
+ *
+ * A ViewHolder holds a view for the [RecyclerView] as well as providing additional information
+ * to the RecyclerView such as where on the screen it was last drawn during scrolling.
+ */
+class TextItemViewHolder(val textView: TextView): RecyclerView.ViewHolder(textView)
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
new file mode 100755
index 0000000..483642b
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+
+/**
+ * A database that stores SleepNight information.
+ * And a global method to get access to the database.
+ *
+ * This pattern is pretty much the same for any database,
+ * so you can reuse it.
+ */
+@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
+abstract class SleepDatabase : RoomDatabase() {
+
+ /**
+ * Connects the database to the DAO.
+ */
+ abstract val sleepDatabaseDao: SleepDatabaseDao
+
+ /**
+ * Define a companion object, this allows us to add functions on the SleepDatabase class.
+ *
+ * For example, clients can call `SleepDatabase.getInstance(context)` to instantiate
+ * a new SleepDatabase.
+ */
+ companion object {
+ /**
+ * INSTANCE will keep a reference to any database returned via getInstance.
+ *
+ * This will help us avoid repeatedly initializing the database, which is expensive.
+ *
+ * The value of a volatile variable will never be cached, and all writes and
+ * reads will be done to and from the main memory. It means that changes made by one
+ * thread to shared data are visible to other threads.
+ */
+ @Volatile
+ private var INSTANCE: SleepDatabase? = null
+
+ /**
+ * Helper function to get the database.
+ *
+ * If a database has already been retrieved, the previous database will be returned.
+ * Otherwise, create a new database.
+ *
+ * This function is threadsafe, and callers should cache the result for multiple database
+ * calls to avoid overhead.
+ *
+ * This is an example of a simple Singleton pattern that takes another Singleton as an
+ * argument in Kotlin.
+ *
+ * To learn more about Singleton read the wikipedia article:
+ * https://en.wikipedia.org/wiki/Singleton_pattern
+ *
+ * @param context The application context Singleton, used to get access to the filesystem.
+ */
+ fun getInstance(context: Context): SleepDatabase {
+ // Multiple threads can ask for the database at the same time, ensure we only initialize
+ // it once by using synchronized. Only one thread may enter a synchronized block at a
+ // time.
+ synchronized(this) {
+
+ // Copy the current value of INSTANCE to a local variable so Kotlin can smart cast.
+ // Smart cast is only available to local variables.
+ var instance = INSTANCE
+
+ // If instance is `null` make a new database instance.
+ if (instance == null) {
+ instance = Room.databaseBuilder(
+ context.applicationContext,
+ SleepDatabase::class.java,
+ "sleep_history_database"
+ )
+ // Wipes and rebuilds instead of migrating if no Migration object.
+ // Migration is not part of this lesson. You can learn more about
+ // migration with Room in this blog post:
+ // https://medium.com/androiddevelopers/understanding-migrations-with-room-f01e04b07929
+ .fallbackToDestructiveMigration()
+ .build()
+ // Assign INSTANCE to the newly created database.
+ INSTANCE = instance
+ }
+
+ // Return instance; smart cast to be non-null.
+ return instance
+ }
+ }
+ }
+}
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
new file mode 100755
index 0000000..39c5444
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.lifecycle.LiveData
+import androidx.room.Dao
+import androidx.room.Insert
+import androidx.room.OnConflictStrategy
+import androidx.room.Query
+import androidx.room.Update
+
+
+/**
+ * Defines methods for using the SleepNight class with Room.
+ */
+@Dao
+interface SleepDatabaseDao {
+
+ @Insert
+ fun insert(night: SleepNight)
+
+ /**
+ * When updating a row with a value already set in a column,
+ * replaces the old value with the new one.
+ *
+ * @param night new value to write
+ */
+ @Update
+ fun update(night: SleepNight)
+
+ /**
+ * Selects and returns the row that matches the supplied start time, which is our key.
+ *
+ * @param key startTimeMilli to match
+ */
+ @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
+ fun get(key: Long): SleepNight
+
+ /**
+ * Deletes all values from the table.
+ *
+ * This does not delete the table, only its contents.
+ */
+ @Query("DELETE FROM daily_sleep_quality_table")
+ fun clear()
+
+ /**
+ * Selects and returns all rows in the table,
+ *
+ * sorted by start time in descending order.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC")
+ fun getAllNights(): LiveData>
+
+ /**
+ * Selects and returns the latest night.
+ */
+ @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
+ fun getTonight(): SleepNight?
+
+ /**
+ * Selects and returns the night with given nightId.
+ */
+ @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key")
+ fun getNightWithId(key: Long): LiveData
+}
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
new file mode 100755
index 0000000..b776812
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.database
+
+import androidx.room.ColumnInfo
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+
+/**
+ * Represents one night's sleep through start, end times, and the sleep quality.
+ */
+@Entity(tableName = "daily_sleep_quality_table")
+data class SleepNight(
+ @PrimaryKey(autoGenerate = true)
+ var nightId: Long = 0L,
+
+ @ColumnInfo(name = "start_time_milli")
+ val startTimeMilli: Long = System.currentTimeMillis(),
+
+ @ColumnInfo(name = "end_time_milli")
+ var endTimeMilli: Long = startTimeMilli,
+
+ @ColumnInfo(name = "quality_rating")
+ var sleepQuality: Int = -1)
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailFragment.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailFragment.kt
new file mode 100644
index 0000000..9f91ea3
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailFragment.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepdetail
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepDetailBinding
+
+
+/**
+ * A simple [Fragment] subclass.
+ * Activities that contain this fragment must implement the
+ * [SleepDetailFragment.OnFragmentInteractionListener] interface
+ * to handle interaction events.
+ * Use the [SleepDetailFragment.newInstance] factory method to
+ * create an instance of this fragment.
+ *
+ */
+class SleepDetailFragment : Fragment() {
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepDetailBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_detail, container, false)
+
+ val application = requireNotNull(this.activity).application
+ val arguments = SleepDetailFragmentArgs.fromBundle(arguments)
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepDetailViewModelFactory(arguments.sleepNightKey, dataSource)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepDetailViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepDetailViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepDetailViewModel = sleepDetailViewModel
+
+ binding.setLifecycleOwner(this)
+
+ // Add an Observer to the state variable for Navigating when a Quality icon is tapped.
+ sleepDetailViewModel.navigateToSleepTracker.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ this.findNavController().navigate(
+ SleepDetailFragmentDirections.actionSleepDetailFragmentToSleepTrackerFragment())
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepDetailViewModel.doneNavigating()
+ }
+ })
+
+ return binding.root
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModel.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModel.kt
new file mode 100644
index 0000000..f712758
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModel.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepdetail
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MediatorLiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import com.example.android.trackmysleepquality.database.SleepNight
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepQualityFragment.
+ *
+ * @param sleepNightKey The key of the current night we are working on.
+ */
+class SleepDetailViewModel(
+ private val sleepNightKey: Long = 0L,
+ dataSource: SleepDatabaseDao) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via its SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine setup variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private val viewModelJob = Job()
+
+ private val night: LiveData
+
+ fun getNight() = night
+
+ init {
+ night=database.getNightWithId(sleepNightKey)
+ }
+
+ /**
+ * Variable that tells the fragment whether it should navigate to [SleepTrackerFragment].
+ *
+ * This is `private` because we don't want to expose the ability to set [MutableLiveData] to
+ * the [Fragment]
+ */
+ private val _navigateToSleepTracker = MutableLiveData()
+
+ /**
+ * When true immediately navigate back to the [SleepTrackerFragment]
+ */
+ val navigateToSleepTracker: LiveData
+ get() = _navigateToSleepTracker
+
+ /**
+ * Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work.
+ *
+ * onCleared() gets called when the ViewModel is destroyed.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+ /**
+ * Call this immediately after navigating to [SleepTrackerFragment]
+ */
+ fun doneNavigating() {
+ _navigateToSleepTracker.value = null
+ }
+
+ fun onClose() {
+ _navigateToSleepTracker.value = true
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModelFactory.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModelFactory.kt
new file mode 100644
index 0000000..68e0dd8
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepdetail/SleepDetailViewModelFactory.kt
@@ -0,0 +1,38 @@
+package com.example.android.trackmysleepquality.sleepdetail
+
+/*
+ * Copyright 2019, 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.
+ */
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the key for the night and the SleepDatabaseDao to the ViewModel.
+ */
+class SleepDetailViewModelFactory(
+ private val sleepNightKey: Long,
+ private val dataSource: SleepDatabaseDao) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepDetailViewModel::class.java)) {
+ return SleepDetailViewModel(sleepNightKey, dataSource) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
new file mode 100755
index 0000000..db22082
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepQualityBinding
+
+/**
+ * Fragment that displays a list of clickable icons,
+ * each representing a sleep quality rating.
+ * Once the user taps an icon, the quality is set in the current sleepNight
+ * and the database is updated.
+ */
+class SleepQualityFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepQualityViewModel] to the
+ * [FragmentSleepQualityBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepQualityBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_quality, container, false)
+
+ val application = requireNotNull(this.activity).application
+ val arguments = SleepQualityFragmentArgs.fromBundle(arguments)
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepQualityViewModelFactory(arguments.sleepNightKey, dataSource)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepQualityViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepQualityViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepQualityViewModel = sleepQualityViewModel
+
+ // Add an Observer to the state variable for Navigating when a Quality icon is tapped.
+ sleepQualityViewModel.navigateToSleepTracker.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ this.findNavController().navigate(
+ SleepQualityFragmentDirections.actionSleepQualityFragmentToSleepTrackerFragment())
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepQualityViewModel.doneNavigating()
+ }
+ })
+
+ return binding.root
+ }
+}
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
new file mode 100755
index 0000000..52ce4d8
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleepquality
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepQualityFragment.
+ *
+ * @param sleepNightKey The key of the current night we are working on.
+ */
+class SleepQualityViewModel(
+ private val sleepNightKey: Long = 0L,
+ dataSource: SleepDatabaseDao) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via its SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine setup variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private val viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this scope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ /**
+ * Variable that tells the fragment whether it should navigate to [SleepTrackerFragment].
+ *
+ * This is `private` because we don't want to expose the ability to set [MutableLiveData] to
+ * the [Fragment]
+ */
+ private val _navigateToSleepTracker = MutableLiveData()
+
+ /**
+ * When true immediately navigate back to the [SleepTrackerFragment]
+ */
+ val navigateToSleepTracker: LiveData
+ get() = _navigateToSleepTracker
+
+ /**
+ * Cancels all coroutines when the ViewModel is cleared, to cleanup any pending work.
+ *
+ * onCleared() gets called when the ViewModel is destroyed.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+
+ /**
+ * Call this immediately after navigating to [SleepTrackerFragment]
+ */
+ fun doneNavigating() {
+ _navigateToSleepTracker.value = null
+ }
+
+ /**
+ * Sets the sleep quality and updates the database.
+ *
+ * Then navigates back to the SleepTrackerFragment.
+ */
+ fun onSetSleepQuality(quality: Int) {
+ uiScope.launch {
+ // IO is a thread pool for running operations that access the disk, such as
+ // our Room database.
+ withContext(Dispatchers.IO) {
+ val tonight = database.get(sleepNightKey)
+ tonight.sleepQuality = quality
+ database.update(tonight)
+ }
+
+ // Setting this state variable to true will alert the observer and trigger navigation.
+ _navigateToSleepTracker.value = true
+ }
+ }
+}
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
new file mode 100755
index 0000000..31baaee
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt
@@ -0,0 +1,38 @@
+package com.example.android.trackmysleepquality.sleepquality
+
+/*
+ * Copyright 2019, 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.
+ */
+
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the key for the night and the SleepDatabaseDao to the ViewModel.
+ */
+class SleepQualityViewModelFactory(
+ private val sleepNightKey: Long,
+ private val dataSource: SleepDatabaseDao) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepQualityViewModel::class.java)) {
+ return SleepQualityViewModel(sleepNightKey, dataSource) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt
new file mode 100644
index 0000000..b5089e0
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/BindingUtils.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.databinding.BindingAdapter
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.convertDurationToFormatted
+import com.example.android.trackmysleepquality.convertNumericQualityToString
+import com.example.android.trackmysleepquality.database.SleepNight
+
+
+@BindingAdapter("sleepDurationFormatted")
+fun TextView.setSleepDurationFormatted(item: SleepNight?) {
+ item?.let {
+ text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
+ }
+}
+
+
+@BindingAdapter("sleepQualityString")
+fun TextView.setSleepQualityString(item: SleepNight?) {
+ item?.let {
+ text = convertNumericQualityToString(item.sleepQuality, context.resources)
+ }
+}
+
+
+@BindingAdapter("sleepImage")
+fun ImageView.setSleepImage(item: SleepNight?) {
+ item?.let {
+ setImageResource(when (item.sleepQuality) {
+ 0 -> R.drawable.ic_sleep_0
+ 1 -> R.drawable.ic_sleep_1
+ 2 -> R.drawable.ic_sleep_2
+
+ 3 -> R.drawable.ic_sleep_3
+
+ 4 -> R.drawable.ic_sleep_4
+ 5 -> R.drawable.ic_sleep_5
+ else -> R.drawable.ic_sleep_active
+ })
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
new file mode 100644
index 0000000..37c77d5
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepNightAdapter.kt
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.annotation.SuppressLint
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepNight
+import com.example.android.trackmysleepquality.databinding.ListItemSleepNightBinding
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+private val ITEM_VIEW_TYPE_HEADER = 0
+private val ITEM_VIEW_TYPE_ITEM = 1
+
+class SleepNightAdapter(val clickListener: SleepNightListener):
+ ListAdapter(SleepNightDiffCallback()) {
+
+ private val adapterScope = CoroutineScope(Dispatchers.Default)
+
+ fun addHeaderAndSubmitList(list: List?) {
+ adapterScope.launch {
+ val items = when (list) {
+ null -> listOf(DataItem.Header)
+ else -> listOf(DataItem.Header) + list.map { DataItem.SleepNightItem(it) }
+ }
+ withContext(Dispatchers.Main) {
+ submitList(items)
+ }
+ }
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ when (holder) {
+ is ViewHolder -> {
+ val nightItem = getItem(position) as DataItem.SleepNightItem
+ holder.bind(nightItem.sleepNight, clickListener)
+ }
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
+ return when (viewType) {
+ ITEM_VIEW_TYPE_HEADER -> TextViewHolder.from(parent)
+ ITEM_VIEW_TYPE_ITEM -> ViewHolder.from(parent)
+ else -> throw ClassCastException("Unknown viewType ${viewType}")
+ }
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return when (getItem(position)) {
+ is DataItem.Header -> ITEM_VIEW_TYPE_HEADER
+ is DataItem.SleepNightItem -> ITEM_VIEW_TYPE_ITEM
+ }
+ }
+
+ class TextViewHolder(view: View): RecyclerView.ViewHolder(view) {
+ companion object {
+ fun from(parent: ViewGroup): TextViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ val view = layoutInflater.inflate(R.layout.header, parent, false)
+ return TextViewHolder(view)
+ }
+ }
+ }
+
+
+ class ViewHolder private constructor(val binding: ListItemSleepNightBinding) : RecyclerView.ViewHolder(binding.root){
+
+ fun bind(item: SleepNight, clickListener: SleepNightListener) {
+ binding.sleep = item
+ binding.clickListener = clickListener
+ binding.executePendingBindings()
+ }
+
+ companion object {
+ fun from(parent: ViewGroup): ViewHolder {
+ val layoutInflater = LayoutInflater.from(parent.context)
+ val binding = ListItemSleepNightBinding.inflate(layoutInflater, parent, false)
+ return ViewHolder(binding)
+ }
+ }
+ }
+}
+
+class SleepNightDiffCallback : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
+ return oldItem.id == newItem.id
+ }
+ @SuppressLint("DiffUtilEquals")
+ override fun areContentsTheSame(oldItem: DataItem, newItem: DataItem): Boolean {
+ return oldItem == newItem
+ }
+}
+
+
+class SleepNightListener(val clickListener: (sleepId: Long) -> Unit) {
+ fun onClick(night: SleepNight) = clickListener(night.nightId)
+}
+
+
+sealed class DataItem {
+ data class SleepNightItem(val sleepNight: SleepNight): DataItem() {
+ override val id = sleepNight.nightId
+ }
+
+ object Header: DataItem() {
+ override val id = Long.MIN_VALUE
+ }
+
+ abstract val id: Long
+}
+
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
new file mode 100755
index 0000000..bb47184
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.databinding.DataBindingUtil
+import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
+import androidx.lifecycle.ViewModelProviders
+import androidx.navigation.fragment.findNavController
+import androidx.recyclerview.widget.GridLayoutManager
+import com.example.android.trackmysleepquality.R
+import com.example.android.trackmysleepquality.database.SleepDatabase
+import com.example.android.trackmysleepquality.databinding.FragmentSleepTrackerBinding
+import com.google.android.material.snackbar.Snackbar
+
+/**
+ * A fragment with buttons to record start and end times for sleep, which are saved in
+ * a database. Cumulative data is displayed in a simple scrollable TextView.
+ * (Because we have not learned about RecyclerView yet.)
+ * The Clear button will clear all data from the database.
+ */
+class SleepTrackerFragment : Fragment() {
+
+ /**
+ * Called when the Fragment is ready to display content to the screen.
+ *
+ * This function uses DataBindingUtil to inflate R.layout.fragment_sleep_quality.
+ *
+ * It is also responsible for passing the [SleepTrackerViewModel] to the
+ * [FragmentSleepTrackerBinding] generated by DataBinding. This will allow DataBinding
+ * to use the [LiveData] on our ViewModel.
+ */
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+
+ // Get a reference to the binding object and inflate the fragment views.
+ val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
+ inflater, R.layout.fragment_sleep_tracker, container, false)
+
+ val application = requireNotNull(this.activity).application
+
+ // Create an instance of the ViewModel Factory.
+ val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
+ val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
+
+ // Get a reference to the ViewModel associated with this fragment.
+ val sleepTrackerViewModel =
+ ViewModelProviders.of(
+ this, viewModelFactory).get(SleepTrackerViewModel::class.java)
+
+ // To use the View Model with data binding, you have to explicitly
+ // give the binding object a reference to it.
+ binding.sleepTrackerViewModel = sleepTrackerViewModel
+
+ val adapter = SleepNightAdapter(SleepNightListener { nightId ->
+ //Toast.makeText(context, "${nightId}", Toast.LENGTH_LONG).show()
+ sleepTrackerViewModel.onSleepNightClicked(nightId)
+ })
+ binding.sleepList.adapter = adapter
+
+
+ sleepTrackerViewModel.nights.observe(viewLifecycleOwner, Observer {
+ it?.let {
+ adapter.addHeaderAndSubmitList(it)
+ }
+ })
+
+ // Specify the current activity as the lifecycle owner of the binding.
+ // This is necessary so that the binding can observe LiveData updates.
+ binding.setLifecycleOwner(this)
+
+ // Add an Observer on the state variable for showing a Snackbar message
+ // when the CLEAR button is pressed.
+ sleepTrackerViewModel.showSnackBarEvent.observe(this, Observer {
+ if (it == true) { // Observed state is true.
+ Snackbar.make(
+ activity!!.findViewById(android.R.id.content),
+ getString(R.string.cleared_message),
+ Snackbar.LENGTH_SHORT // How long to display the message.
+ ).show()
+ // Reset state to make sure the toast is only shown once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneShowingSnackbar()
+ }
+ })
+
+ // Add an Observer on the state variable for Navigating when STOP button is pressed.
+ sleepTrackerViewModel.navigateToSleepQuality.observe(this, Observer { night ->
+ night?.let {
+ // We need to get the navController from this, because button is not ready, and it
+ // just has to be a view. For some reason, this only matters if we hit stop again
+ // after using the back button, not if we hit stop and choose a quality.
+ // Also, in the Navigation Editor, for Quality -> Tracker, check "Inclusive" for
+ // popping the stack to get the correct behavior if we press stop multiple times
+ // followed by back.
+ // Also: https://stackoverflow.com/questions/28929637/difference-and-uses-of-oncreate-oncreateview-and-onactivitycreated-in-fra
+ this.findNavController().navigate(
+ SleepTrackerFragmentDirections
+ .actionSleepTrackerFragmentToSleepQualityFragment(night.nightId))
+ // Reset state to make sure we only navigate once, even if the device
+ // has a configuration change.
+ sleepTrackerViewModel.doneNavigating()
+ }
+ })
+
+ // Add an Observer on the state variable for Navigating when and item is clicked.
+ sleepTrackerViewModel.navigateToSleepDetail.observe(this, Observer { night ->
+ night?.let {
+
+ this.findNavController().navigate(
+ SleepTrackerFragmentDirections
+ .actionSleepTrackerFragmentToSleepDetailFragment(night))
+ sleepTrackerViewModel.onSleepDetailNavigated()
+ }
+ })
+
+ val manager = GridLayoutManager(activity, 3)
+ manager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
+ override fun getSpanSize(position: Int) = when (position) {
+ 0 -> 3
+ else -> 1
+ }
+ }
+
+
+ binding.sleepList.layoutManager = manager
+
+ return binding.root
+ }
+}
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
new file mode 100755
index 0000000..4505122
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2019, 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.example.android.trackmysleepquality.sleeptracker
+
+import android.app.Application
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.Transformations
+import androidx.lifecycle.ViewModel
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+import com.example.android.trackmysleepquality.database.SleepNight
+import com.example.android.trackmysleepquality.formatNights
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * ViewModel for SleepTrackerFragment.
+ */
+class SleepTrackerViewModel(
+ dataSource: SleepDatabaseDao,
+ application: Application) : ViewModel() {
+
+ /**
+ * Hold a reference to SleepDatabase via SleepDatabaseDao.
+ */
+ val database = dataSource
+
+ /** Coroutine variables */
+
+ /**
+ * viewModelJob allows us to cancel all coroutines started by this ViewModel.
+ */
+ private var viewModelJob = Job()
+
+ /**
+ * A [CoroutineScope] keeps track of all coroutines started by this ViewModel.
+ *
+ * Because we pass it [viewModelJob], any coroutine started in this uiScope can be cancelled
+ * by calling `viewModelJob.cancel()`
+ *
+ * By default, all coroutines started in uiScope will launch in [Dispatchers.Main] which is
+ * the main thread on Android. This is a sensible default because most coroutines started by
+ * a [ViewModel] update the UI after performing some processing.
+ */
+ private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
+
+ private var tonight = MutableLiveData()
+
+ val nights = database.getAllNights()
+
+ /**
+ * Converted nights to Spanned for displaying.
+ */
+ val nightsString = Transformations.map(nights) { nights ->
+ formatNights(nights, application.resources)
+ }
+
+ /**
+ * If tonight has not been set, then the START button should be visible.
+ */
+ val startButtonVisible = Transformations.map(tonight) {
+ null == it
+ }
+
+ /**
+ * If tonight has been set, then the STOP button should be visible.
+ */
+ val stopButtonVisible = Transformations.map(tonight) {
+ null != it
+ }
+
+ /**
+ * If there are any nights in the database, show the CLEAR button.
+ */
+ val clearButtonVisible = Transformations.map(nights) {
+ it?.isNotEmpty()
+ }
+
+ /**
+ * Request a toast by setting this value to true.
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private var _showSnackbarEvent = MutableLiveData()
+
+ /**
+ * If this is true, immediately `show()` a toast and call `doneShowingSnackbar()`.
+ */
+ val showSnackBarEvent: LiveData
+ get() = _showSnackbarEvent
+
+ /**
+ * Variable that tells the Fragment to navigate to a specific [SleepQualityFragment]
+ *
+ * This is private because we don't want to expose setting this value to the Fragment.
+ */
+ private val _navigateToSleepQuality = MutableLiveData()
+
+ /**
+ * If this is non-null, immediately navigate to [SleepQualityFragment] and call [doneNavigating]
+ */
+ val navigateToSleepQuality: LiveData
+ get() = _navigateToSleepQuality
+
+ /**
+ * Call this immediately after calling `show()` on a toast.
+ *
+ * It will clear the toast request, so if the user rotates their phone it won't show a duplicate
+ * toast.
+ */
+ fun doneShowingSnackbar() {
+ _showSnackbarEvent.value = null
+ }
+
+ /**
+ * Call this immediately after navigating to [SleepQualityFragment]
+ *
+ * It will clear the navigation request, so if the user rotates their phone it won't navigate
+ * twice.
+ */
+ fun doneNavigating() {
+ _navigateToSleepQuality.value = null
+ }
+
+ /**
+ * Navigation for the SleepDetails fragment.
+ */
+ private val _navigateToSleepDetail = MutableLiveData()
+ val navigateToSleepDetail
+ get() = _navigateToSleepDetail
+
+ fun onSleepNightClicked(id: Long) {
+ _navigateToSleepDetail.value = id
+ }
+
+ fun onSleepDetailNavigated() {
+ _navigateToSleepDetail.value = null
+ }
+
+ init {
+ initializeTonight()
+ }
+
+ private fun initializeTonight() {
+ uiScope.launch {
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Handling the case of the stopped app or forgotten recording,
+ * the start and end times will be the same.j
+ *
+ * If the start time and end time are not the same, then we do not have an unfinished
+ * recording.
+ */
+ private suspend fun getTonightFromDatabase(): SleepNight? {
+ return withContext(Dispatchers.IO) {
+ var night = database.getTonight()
+ if (night?.endTimeMilli != night?.startTimeMilli) {
+ night = null
+ }
+ night
+ }
+ }
+
+ private suspend fun insert(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.insert(night)
+ }
+ }
+
+ private suspend fun update(night: SleepNight) {
+ withContext(Dispatchers.IO) {
+ database.update(night)
+ }
+ }
+
+ private suspend fun clear() {
+ withContext(Dispatchers.IO) {
+ database.clear()
+ }
+ }
+
+ /**
+ * Executes when the START button is clicked.
+ */
+ fun onStart() {
+ uiScope.launch {
+ // Create a new night, which captures the current time,
+ // and insert it into the database.
+ val newNight = SleepNight()
+
+ insert(newNight)
+
+ tonight.value = getTonightFromDatabase()
+ }
+ }
+
+ /**
+ * Executes when the STOP button is clicked.
+ */
+ fun onStop() {
+ uiScope.launch {
+ // In Kotlin, the return@label syntax is used for specifying which function among
+ // several nested ones this statement returns from.
+ // In this case, we are specifying to return from launch().
+ val oldNight = tonight.value ?: return@launch
+
+ // Update the night in the database to add the end time.
+ oldNight.endTimeMilli = System.currentTimeMillis()
+
+ update(oldNight)
+
+ // Set state to navigate to the SleepQualityFragment.
+ _navigateToSleepQuality.value = oldNight
+ }
+ }
+
+ /**
+ * Executes when the CLEAR button is clicked.
+ */
+ fun onClear() {
+ uiScope.launch {
+ // Clear the database table.
+ clear()
+
+ // And clear tonight since it's no longer in the database
+ tonight.value = null
+
+ // Show a snackbar message, because it's friendly.
+ _showSnackbarEvent.value = true
+ }
+ }
+
+ /**
+ * Called when the ViewModel is dismantled.
+ * At this point, we want to cancel all coroutines;
+ * otherwise we end up with processes that have nowhere to return to
+ * using memory and resources.
+ */
+ override fun onCleared() {
+ super.onCleared()
+ viewModelJob.cancel()
+ }
+}
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
new file mode 100755
index 0000000..39a7046
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt
@@ -0,0 +1,39 @@
+package com.example.android.trackmysleepquality.sleeptracker
+
+/*
+ * Copyright 2019, 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.
+ */
+
+import android.app.Application
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.example.android.trackmysleepquality.database.SleepDatabaseDao
+
+/**
+ * This is pretty much boiler plate code for a ViewModel Factory.
+ *
+ * Provides the SleepDatabaseDao and context to the ViewModel.
+ */
+class SleepTrackerViewModelFactory(
+ private val dataSource: SleepDatabaseDao,
+ private val application: Application) : ViewModelProvider.Factory {
+ @Suppress("unchecked_cast")
+ override fun create(modelClass: Class): T {
+ if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
+ return SleepTrackerViewModel(dataSource, application) as T
+ }
+ throw IllegalArgumentException("Unknown ViewModel class")
+ }
+}
diff --git a/RecyclerViewHeaders/app/src/main/res/anim/slide_in_right.xml b/RecyclerViewHeaders/app/src/main/res/anim/slide_in_right.xml
new file mode 100755
index 0000000..49f7779
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/anim/slide_in_right.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml b/RecyclerViewHeaders/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
new file mode 100755
index 0000000..0b1f6bd
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml b/RecyclerViewHeaders/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_0.xml b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_0.xml
new file mode 100755
index 0000000..91c0b47
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_0.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_1.xml b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_1.xml
new file mode 100755
index 0000000..8cc34f7
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_1.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_2.xml b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_2.xml
new file mode 100755
index 0000000..0b7b818
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_2.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_3.xml b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_3.xml
new file mode 100755
index 0000000..677b9ae
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_3.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_4.xml b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_4.xml
new file mode 100755
index 0000000..39eab52
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_4.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_5.xml b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_5.xml
new file mode 100755
index 0000000..f23a2ae
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_5.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_active.xml b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_active.xml
new file mode 100755
index 0000000..a117d56
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/drawable/ic_sleep_active.xml
@@ -0,0 +1,77 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/font/roboto.xml b/RecyclerViewHeaders/app/src/main/res/font/roboto.xml
new file mode 100755
index 0000000..2641caf
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/font/roboto.xml
@@ -0,0 +1,7 @@
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/layout/activity_main.xml b/RecyclerViewHeaders/app/src/main/res/layout/activity_main.xml
new file mode 100755
index 0000000..a21679c
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/layout/fragment_sleep_detail.xml b/RecyclerViewHeaders/app/src/main/res/layout/fragment_sleep_detail.xml
new file mode 100644
index 0000000..336f0d0
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/layout/fragment_sleep_detail.xml
@@ -0,0 +1,83 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/res/layout/fragment_sleep_quality.xml b/RecyclerViewHeaders/app/src/main/res/layout/fragment_sleep_quality.xml
new file mode 100755
index 0000000..ab0ba0c
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/layout/fragment_sleep_quality.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/layout/fragment_sleep_tracker.xml b/RecyclerViewHeaders/app/src/main/res/layout/fragment_sleep_tracker.xml
new file mode 100755
index 0000000..6eeea8a
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/layout/fragment_sleep_tracker.xml
@@ -0,0 +1,98 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/layout/header.xml b/RecyclerViewHeaders/app/src/main/res/layout/header.xml
new file mode 100644
index 0000000..c55bde3
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/layout/header.xml
@@ -0,0 +1,29 @@
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/layout/list_item_sleep_night.xml b/RecyclerViewHeaders/app/src/main/res/layout/list_item_sleep_night.xml
new file mode 100755
index 0000000..6e2fb86
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/layout/list_item_sleep_night.xml
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/res/layout/list_item_sleep_night_linear.xml b/RecyclerViewHeaders/app/src/main/res/layout/list_item_sleep_night_linear.xml
new file mode 100755
index 0000000..43e89ce
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/layout/list_item_sleep_night_linear.xml
@@ -0,0 +1,70 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/res/layout/text_item_view.xml b/RecyclerViewHeaders/app/src/main/res/layout/text_item_view.xml
new file mode 100644
index 0000000..f603fac
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/layout/text_item_view.xml
@@ -0,0 +1,21 @@
+
+
+
\ No newline at end of file
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100755
index 0000000..8280307
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml b/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml b/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
new file mode 100755
index 0000000..3ebe7ae
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/mipmap-anydpi-v26/ic_launcher_sleep_tracker_round.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher.png b/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100755
index 0000000..898f3ed
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dffca36
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png b/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..e5e92ac
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..ae7a1a2
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-hdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher.png b/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100755
index 0000000..64ba76f
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100755
index 0000000..dae5e08
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png b/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1250b4c
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..5646308
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-mdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100755
index 0000000..e5ed465
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..14ed0af
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..fd07714
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..86c0074
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100755
index 0000000..b0907ca
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..d8ae031
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..ae4d8d3
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..37a4a99
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100755
index 0000000..2c18de9
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100755
index 0000000..beed3cd
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png
new file mode 100755
index 0000000..1fc200c
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png b/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png
new file mode 100755
index 0000000..1125b8c
Binary files /dev/null and b/RecyclerViewHeaders/app/src/main/res/mipmap-xxxhdpi/ic_launcher_sleep_tracker_round.png differ
diff --git a/RecyclerViewHeaders/app/src/main/res/navigation/navigation.xml b/RecyclerViewHeaders/app/src/main/res/navigation/navigation.xml
new file mode 100755
index 0000000..3d387dd
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/navigation/navigation.xml
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/values/colors.xml b/RecyclerViewHeaders/app/src/main/res/values/colors.xml
new file mode 100755
index 0000000..b57e355
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/values/colors.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ #6ab343
+ #388310
+ #6AB343
+
+ #388310
+ #f0f0f0
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/values/dimens.xml b/RecyclerViewHeaders/app/src/main/res/values/dimens.xml
new file mode 100755
index 0000000..5385c2e
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/values/dimens.xml
@@ -0,0 +1,22 @@
+
+
+
+ 16dp
+ 64dp
+ 20sp
+ 48dp
+
diff --git a/RecyclerViewHeaders/app/src/main/res/values/font_certs.xml b/RecyclerViewHeaders/app/src/main/res/values/font_certs.xml
new file mode 100755
index 0000000..98db6fb
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/values/font_certs.xml
@@ -0,0 +1,32 @@
+
+
+
+
+ @array/com_google_android_gms_fonts_certs_dev
+ @array/com_google_android_gms_fonts_certs_prod
+
+
+
+ MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
+
+
+
+
+ MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
+
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/values/preloaded_fonts.xml b/RecyclerViewHeaders/app/src/main/res/values/preloaded_fonts.xml
new file mode 100755
index 0000000..0a2c03c
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/values/preloaded_fonts.xml
@@ -0,0 +1,21 @@
+
+
+
+
+ @font/roboto
+
+
diff --git a/RecyclerViewHeaders/app/src/main/res/values/strings.xml b/RecyclerViewHeaders/app/src/main/res/values/strings.xml
new file mode 100755
index 0000000..223c807
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/values/strings.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+ Track My Sleep Quality
+ SleepTrackerFragment
+ SleepQualityFragment
+
+
+ Start
+ Stop
+ Clear
+
+
+ Sleep Quality 0
+ Sleep Quality 1
+ Sleep Quality 2
+ Sleep Quality 3
+ Sleep Quality 4
+ Sleep Quality 5
+
+
+ HERE IS YOUR SLEEP DATA]]>
+ Start:]]>
+ End:]]>
+ Quality:]]>
+ Hours:Minutes:Seconds]]>
+
+
+ How was your sleep?
+ Very bad
+ Poor
+ So-so
+ OK
+ Pretty good
+ Excellent!
+
+
+ All your data is gone forever.
+
+ Sleep Results
+ Close
+ %d minutes on %s
+ %d hours on %s
+ %d seconds on %s
+
diff --git a/RecyclerViewHeaders/app/src/main/res/values/styles.xml b/RecyclerViewHeaders/app/src/main/res/values/styles.xml
new file mode 100755
index 0000000..76369f2
--- /dev/null
+++ b/RecyclerViewHeaders/app/src/main/res/values/styles.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RecyclerViewHeaders/build.gradle b/RecyclerViewHeaders/build.gradle
new file mode 100755
index 0000000..c861b85
--- /dev/null
+++ b/RecyclerViewHeaders/build.gradle
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2019, 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.3.10'
+ archLifecycleVersion = '1.1.1'
+ room_version = '2.0.0'
+ coroutine_version = '1.0.0'
+ gradleVersion = '3.4.0'
+ navigationVersion = '1.0.0-alpha07'
+ dataBindingCompilerVersion = gradleVersion // Always need to be the same.
+ }
+
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath "com.android.tools.build:gradle:$gradleVersion"
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
+
+ // 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
+}
diff --git a/RecyclerViewHeaders/gradle.properties b/RecyclerViewHeaders/gradle.properties
new file mode 100755
index 0000000..9592636
--- /dev/null
+++ b/RecyclerViewHeaders/gradle.properties
@@ -0,0 +1,15 @@
+# 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
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/RecyclerViewHeaders/gradle/wrapper/gradle-wrapper.jar b/RecyclerViewHeaders/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000..f6b961f
Binary files /dev/null and b/RecyclerViewHeaders/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/RecyclerViewHeaders/gradle/wrapper/gradle-wrapper.properties b/RecyclerViewHeaders/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..cf2149f
--- /dev/null
+++ b/RecyclerViewHeaders/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Tue May 14 17:20:31 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip
diff --git a/RecyclerViewHeaders/gradlew b/RecyclerViewHeaders/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/RecyclerViewHeaders/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/RecyclerViewHeaders/gradlew.bat b/RecyclerViewHeaders/gradlew.bat
new file mode 100755
index 0000000..e95643d
--- /dev/null
+++ b/RecyclerViewHeaders/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/RecyclerViewHeaders/settings.gradle b/RecyclerViewHeaders/settings.gradle
new file mode 100755
index 0000000..e7b4def
--- /dev/null
+++ b/RecyclerViewHeaders/settings.gradle
@@ -0,0 +1 @@
+include ':app'