diff --git a/TrackMySleepQualityCoroutines/README.md b/TrackMySleepQualityCoroutines/README.md new file mode 100755 index 0000000..d974737 --- /dev/null +++ b/TrackMySleepQualityCoroutines/README.md @@ -0,0 +1,60 @@ +TrackMySleepQuality - Solution Code for 6.2 Coroutines codelab +============================================================== + +Solution code for Android Kotlin Fundamentals Codelab 6.2 Coroutines + +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. + +This app: + +* Extends the TrackMySleepQuality app to collect, store, and display data in and from the database. +* Uses coroutines to run long-running database operations in the background. +* Uses LiveData to trigger navigation and showing of a snackbar. +* Uses LiveData to enable and disable buttons. + + +Pre-requisites +-------------- + +You need to know: + +* Building a basic user interface (UI) for an Android app, + using an activity, fragments, and views. +* Navigating between fragments and using Safe Args (a Gradle plugin) + to pass data between fragments. +* View models, view-model factories, and LiveData and its observers. + These Architecture Components topics are covered in an earlier codelab in this course. +* A basic understanding of SQL databases and the SQLite language. +* How to create a Room database, create a DAO, and define entities, + from the previous Room codelab[LINK]. +* It is helpful if you are familiar with threading and multiprocessing concepts. + + +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. \ No newline at end of file diff --git a/TrackMySleepQualityCoroutines/app/.gitignore b/TrackMySleepQualityCoroutines/app/.gitignore new file mode 100755 index 0000000..796b96d --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/.gitignore @@ -0,0 +1 @@ +/build diff --git a/TrackMySleepQualityCoroutines/app/build.gradle b/TrackMySleepQualityCoroutines/app/build.gradle new file mode 100755 index 0000000..a1bb732 --- /dev/null +++ b/TrackMySleepQualityCoroutines/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.trackmysleepquality" + 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.2" + implementation "androidx.fragment:fragment:1.0.0" + implementation "androidx.constraintlayout:constraintlayout:2.0.0-alpha3" + + // 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.ext:junit:1.1.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' +} + diff --git a/TrackMySleepQualityCoroutines/app/proguard-rules.pro b/TrackMySleepQualityCoroutines/app/proguard-rules.pro new file mode 100755 index 0000000..f1b4245 --- /dev/null +++ b/TrackMySleepQualityCoroutines/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/TrackMySleepQualityCoroutines/app/src/androidTest/java/com/example/android/trackmysleepquality/SleepDatabaseTest.kt b/TrackMySleepQualityCoroutines/app/src/androidTest/java/com/example/android/trackmysleepquality/SleepDatabaseTest.kt new file mode 100755 index 0000000..44be8da --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/androidTest/java/com/example/android/trackmysleepquality/SleepDatabaseTest.kt @@ -0,0 +1,73 @@ +/* + * 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 androidx.room.Room +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.example.android.trackmysleepquality.database.SleepDatabase +import com.example.android.trackmysleepquality.database.SleepDatabaseDao +import com.example.android.trackmysleepquality.database.SleepNight +import org.junit.Assert.assertEquals +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import java.io.IOException + + +/** + * This is not meant to be a full set of tests. For simplicity, most of your samples do not + * include tests. However, when building the Room, it is helpful to make sure it works before + * adding the UI. + */ + +@RunWith(AndroidJUnit4::class) +class SleepDatabaseTest { + + private lateinit var sleepDao: SleepDatabaseDao + private lateinit var db: SleepDatabase + + @Before + fun createDb() { + val context = InstrumentationRegistry.getInstrumentation().targetContext + // Using an in-memory database because the information stored here disappears when the + // process is killed. + db = Room.inMemoryDatabaseBuilder(context, SleepDatabase::class.java) + // Allowing main thread queries, just for testing. + .allowMainThreadQueries() + .build() + sleepDao = db.sleepDatabaseDao + } + + @After + @Throws(IOException::class) + fun closeDb() { + db.close() + } + + @Test + @Throws(Exception::class) + fun insertAndGetNight() { + val night = SleepNight() + sleepDao.insert(night) + val tonight = sleepDao.getTonight() + assertEquals(tonight?.sleepQuality, -1) + } +} + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/AndroidManifest.xml b/TrackMySleepQualityCoroutines/app/src/main/AndroidManifest.xml new file mode 100755 index 0000000..58a0b98 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/AndroidManifest.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/ic_launcher_sleep_tracker-web.png b/TrackMySleepQualityCoroutines/app/src/main/ic_launcher_sleep_tracker-web.png new file mode 100755 index 0000000..668bfae Binary files /dev/null and b/TrackMySleepQualityCoroutines/app/src/main/ic_launcher_sleep_tracker-web.png differ diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt new file mode 100755 index 0000000..0494de5 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/MainActivity.kt @@ -0,0 +1,52 @@ +/* + * 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 + + +/** + * This is the toy app for lesson 6 of the + * Android App Development in Kotlin course on Udacity(https://www.udacity.com/course/???). + * + * The SleepQualityTracker app is a demo app that helps you collect information about your sleep. + * - Start time, end time, quality, and time slept + * + * This app demonstrates the following views and techniques: + * - Room database, DAO, and Coroutines + * + * It also uses and builds on the following techniques from previous lessons: + * - Transformation map + * - Data Binding in XML files + * - ViewModel Factory + * - Using Backing Properties to protect MutableLiveData + * - Observable state LiveData variables to trigger navigation + */ + +/** + * This main activity is just a container for our fragments, + * where the real action is. + */ +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContentView(R.layout.activity_main) + } +} diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/Util.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/Util.kt new file mode 100755 index 0000000..50f4f94 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/Util.kt @@ -0,0 +1,105 @@ +/* + * 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 androidx.core.text.HtmlCompat +import com.example.android.trackmysleepquality.database.SleepNight +import java.text.SimpleDateFormat + +/** + * These functions create a formatted string that can be set in a TextView. + */ + +/** + * 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}

") + } + } + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + return Html.fromHtml(sb.toString(), Html.FROM_HTML_MODE_LEGACY) + } else { + return HtmlCompat.fromHtml(sb.toString(), HtmlCompat.FROM_HTML_MODE_LEGACY) + } +} diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt new file mode 100755 index 0000000..28e627c --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabase.kt @@ -0,0 +1,52 @@ +/* + * 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 + +@Database(entities = [SleepNight::class], version = 1, exportSchema = false) +abstract class SleepDatabase : RoomDatabase() { + + abstract val sleepDatabaseDao: SleepDatabaseDao + + companion object { + + @Volatile + private var INSTANCE: SleepDatabase? = null + + fun getInstance(context: Context): SleepDatabase { + synchronized(this) { + var instance = INSTANCE + + if (instance == null) { + instance = Room.databaseBuilder( + context.applicationContext, + SleepDatabase::class.java, + "sleep_history_database" + ) + .fallbackToDestructiveMigration() + .build() + INSTANCE = instance + } + return instance + } + } + } +} \ No newline at end of file diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt new file mode 100755 index 0000000..6396001 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/database/SleepDatabaseDao.kt @@ -0,0 +1,45 @@ +/* + * 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.Query +import androidx.room.Update + +@Dao +interface SleepDatabaseDao { + + @Insert + fun insert(night: SleepNight) + + @Update + fun update(night: SleepNight) + + @Query("SELECT * from daily_sleep_quality_table WHERE nightId = :key") + fun get(key: Long): SleepNight? + + @Query("DELETE FROM daily_sleep_quality_table") + fun clear() + + @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1") + fun getTonight(): SleepNight? + + @Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC") + fun getAllNights(): LiveData> +} \ No newline at end of file diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt new file mode 100755 index 0000000..41bb20d --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/database/SleepNight.kt @@ -0,0 +1,36 @@ +/* + * 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 + +@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 +) \ No newline at end of file diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt new file mode 100755 index 0000000..9fb8a47 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityFragment.kt @@ -0,0 +1,52 @@ +/* + * 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 com.example.android.trackmysleepquality.R +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. + */ + 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 + + return binding.root + } +} diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt new file mode 100755 index 0000000..e664492 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModel.kt @@ -0,0 +1,17 @@ +/* + * 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 diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt new file mode 100755 index 0000000..e664492 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleepquality/SleepQualityViewModelFactory.kt @@ -0,0 +1,17 @@ +/* + * 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 diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt new file mode 100755 index 0000000..491ee6a --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerFragment.kt @@ -0,0 +1,66 @@ +/* + * 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.ViewModelProviders +import com.example.android.trackmysleepquality.R +import com.example.android.trackmysleepquality.database.SleepDatabase +import com.example.android.trackmysleepquality.databinding.FragmentSleepTrackerBinding + +/** + * 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.) + */ +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. + */ + 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 + + val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao + + val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application) + + val sleepTrackerViewModel = + ViewModelProviders.of( + this, viewModelFactory).get(SleepTrackerViewModel::class.java) + + binding.setLifecycleOwner(this) + + binding.sleepTrackerViewModel = sleepTrackerViewModel + + + return binding.root + } +} diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.kt new file mode 100755 index 0000000..8f17ebc --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModel.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.app.Application +import androidx.lifecycle.AndroidViewModel +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.Transformations +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( + val database: SleepDatabaseDao, + application: Application) : AndroidViewModel(application) { + + private var viewModelJob = Job() + + + private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob) + + private val nights = database.getAllNights() + + val nightsString = Transformations.map(nights) { nights -> + formatNights(nights, application.resources) + } + + private var tonight = MutableLiveData() + + init { + initializeTonight() + } + + private fun initializeTonight() { + uiScope.launch { + tonight.value = getTonightFromDatabase() + } + } + + private suspend fun getTonightFromDatabase(): SleepNight? { + + return withContext(Dispatchers.IO) { + + var night = database.getTonight() + if (night?.endTimeMilli != night?.startTimeMilli) { + night = null + } + night + } + } + + fun onStartTracking() { + uiScope.launch { + val newNight = SleepNight() + insert(newNight) + tonight.value = getTonightFromDatabase() + } + } + + private suspend fun insert(night: SleepNight) { + withContext(Dispatchers.IO) { + database.insert(night) + } + } + + fun onStopTracking() { + uiScope.launch { + val oldNight = tonight.value ?: return@launch + oldNight.endTimeMilli = System.currentTimeMillis() + update(oldNight) + } + } + + private suspend fun update(night: SleepNight) { + withContext(Dispatchers.IO) { + database.update(night) + } + } + + fun onClear() { + uiScope.launch { + clear() + tonight.value = null + } + } + + suspend fun clear() { + withContext(Dispatchers.IO) { + database.clear() + } + } + + override fun onCleared() { + super.onCleared() + viewModelJob.cancel() + } +} + diff --git a/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt b/TrackMySleepQualityCoroutines/app/src/main/java/com/example/android/trackmysleepquality/sleeptracker/SleepTrackerViewModelFactory.kt new file mode 100755 index 0000000..8a28f28 --- /dev/null +++ b/TrackMySleepQualityCoroutines/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/TrackMySleepQualityCoroutines/app/src/main/res/anim/slide_in_right.xml b/TrackMySleepQualityCoroutines/app/src/main/res/anim/slide_in_right.xml new file mode 100755 index 0000000..49f7779 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/anim/slide_in_right.xml @@ -0,0 +1,25 @@ + + + + + + \ No newline at end of file diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml new file mode 100755 index 0000000..0b1f6bd --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_launcher_sleep_tracker_background.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml new file mode 100755 index 0000000..a117d56 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_launcher_sleep_tracker_foreground.xml @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_0.xml b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_0.xml new file mode 100755 index 0000000..91c0b47 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_0.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_1.xml b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_1.xml new file mode 100755 index 0000000..8cc34f7 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_1.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_2.xml b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_2.xml new file mode 100755 index 0000000..0b7b818 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_2.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_3.xml b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_3.xml new file mode 100755 index 0000000..677b9ae --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_3.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_4.xml b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_4.xml new file mode 100755 index 0000000..39eab52 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_4.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_5.xml b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_5.xml new file mode 100755 index 0000000..f23a2ae --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/drawable/ic_sleep_5.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/font/roboto.xml b/TrackMySleepQualityCoroutines/app/src/main/res/font/roboto.xml new file mode 100755 index 0000000..2641caf --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/font/roboto.xml @@ -0,0 +1,7 @@ + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/layout/activity_main.xml b/TrackMySleepQualityCoroutines/app/src/main/res/layout/activity_main.xml new file mode 100755 index 0000000..a21679c --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,32 @@ + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/layout/fragment_sleep_quality.xml b/TrackMySleepQualityCoroutines/app/src/main/res/layout/fragment_sleep_quality.xml new file mode 100755 index 0000000..512a3f1 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/layout/fragment_sleep_quality.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TrackMySleepQualityCoroutines/app/src/main/res/layout/fragment_sleep_tracker.xml b/TrackMySleepQualityCoroutines/app/src/main/res/layout/fragment_sleep_tracker.xml new file mode 100755 index 0000000..4aeb5d6 --- /dev/null +++ b/TrackMySleepQualityCoroutines/app/src/main/res/layout/fragment_sleep_tracker.xml @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +