Initial commit of Android Kotlin Fundamentals Solution Apps.

Change-Id: I89c6d0f13f89a678a91b16694306579dbf031427
This commit is contained in:
Aleks Haecky 2019-03-14 14:29:45 -07:00
parent b023104c92
commit 39f76aacf8
207 changed files with 6560 additions and 0 deletions

View File

@ -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.

View File

@ -0,0 +1 @@
/build

View File

@ -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'
}

View File

@ -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

View File

@ -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)
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.trackmysleepquality">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher_sleep_tracker"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_sleep_tracker_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -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)
}
}

View File

@ -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<SleepNight>, resources: Resources): Spanned {
val sb = StringBuilder()
sb.apply {
append(resources.getString(R.string.title))
nights.forEach {
append("<br>")
append(resources.getString(R.string.start_time))
append("\t${convertLongToDateString(it.startTimeMilli)}<br>")
if (it.endTimeMilli != it.startTimeMilli) {
append(resources.getString(R.string.end_time))
append("\t${convertLongToDateString(it.endTimeMilli)}<br>")
append(resources.getString(R.string.quality))
append("\t${convertNumericQualityToString(it.sleepQuality, resources)}<br>")
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}<br><br>")
}
}
}
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)
}
}

View File

@ -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
}
}
}
}

View File

@ -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<List<SleepNight>>
}

View File

@ -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
)

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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<SleepNight?>()
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()
}
}

View File

@ -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 <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
return SleepTrackerViewModel(dataSource, application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="700"
android:fromXDelta="100%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%" />
</set>

View File

@ -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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M0,369.07h512v142.93h-512z"
android:fillColor="#e0e0e0"/>
<path
android:pathData="M228.55,271.42l15.7,-30.1L225,244.55l-1,-5.78 26.34,-4.41 1,6.22 -15.6,30.07L256,267.26l1,5.79 -27.42,4.59Z"
android:fillColor="#757575"/>
<path
android:pathData="M271.75,215.33l41.52,-29.2 -27.86,-9 2.68,-8.35 38,12.23 -2.89,9 -41.37,29.25 29.28,9.41L308.44,237l-39.58,-12.73Z"
android:fillColor="#bdbdbd"/>
<path
android:pathData="M350,187.18l39.94,-79.32L339.54,117l-2.75,-15.11 68.74,-12.52 3,16.23 -39.67,79.27 52.94,-9.64 2.75,15.1 -71.57,13Z"
android:fillColor="#e0e0e0"/>
<group>
<clip-path android:pathData="M584.94,-624.15h512v512h-512z M 0,0"/>
<path
android:pathData="M448.55,-187.1,709.34,73.69a8.72,8.72,0,0,0,12.32,0l325.45,-325.45a8.72,8.72,0,0,0,0,-12.32L778,-533.19a18.84,18.84,0,0,0,-26.65,0L442,-241.74l-2.36,5.16A44.05,44.05,0,0,0,448.55,-187.1Z"
android:fillColor="#43a047"/>
<path
android:pathData="M440.25,-208.98l275.52002,275.53l335.0,-335.0l-278.15002,-278.14996l-334.26,328.40997l1.8900146,9.210007z"
android:fillColor="#c0ca33"/>
<path
android:pathData="M544.72,-487.03h393.36v473.76h-393.36z"
android:fillColor="#e6ee9c"/>
<path
android:pathData="M548.74,-506.95H930.26a8.41,8.41,0,0,1,8.41,8.41V-37.68a8.41,8.41,0,0,1,-8.41,8.41H548.74a18.19,18.19,0,0,1,-18.19,-18.19V-488.75A18.19,18.19,0,0,1,548.74,-506.95Z"
android:fillColor="#6ab343"/>
</group>
<group>
<clip-path android:pathData="M0,-624.15h512v512h-512z M 0,0"/>
<path
android:pathData="M246.59,-272.23l39.17,-11.46a3.9,3.9,0,0,0,2.67,-4.84,3.9,3.9,0,0,0,-4.84,-2.63L244,-279.58a134.83,134.83,0,0,0,-30.74,-47.09,134.89,134.89,0,0,0,-47,-30.69l11.49,-39.52a3.86,3.86,0,0,0,-2.63,-4.83,3.85,3.85,0,0,0,-4.83,2.64l-11.42,39.16c-41.3,-13.2,-86.47,-4.85,-117.17,25.89l178.9,178.87c30.72,-30.71,39,-75.79,25.93,-117.08m-211,-54.15L-93.27,-197.48a29.94,29.94,0,0,0,0,42.38l14.43,14.44,-44,44a28.1,28.1,0,0,0,0,39.7,28.12,28.12,0,0,0,39.74,0l44,-44,26.81,26.8,-44,44a28.09,28.09,0,0,0,0,39.7,28.11,28.11,0,0,0,39.74,0l44,-44L41.91,-20A30,30,0,0,0,84.3,-20L213.16,-148.87Zm180.59,90.91L203.59,-248a1.7,1.7,0,0,1,0,-2.4l1.59,-1.6a1.71,1.71,0,0,1,2.41,0l12.58,12.59a1.7,1.7,0,0,1,0,2.4l-1.59,1.59A1.7,1.7,0,0,1,216.17,-235.47Zm-81.43,-81.42,-12.58,-12.59a1.7,1.7,0,0,1,0,-2.4l1.59,-1.59a1.7,1.7,0,0,1,2.41,0l12.58,12.58a1.68,1.68,0,0,1,0,2.4l-1.59,1.6A1.71,1.71,0,0,1,134.74,-316.89Zm-99.16,-9.49L-93.27,-197.48a29.94,29.94,0,0,0,0,42.38l14.43,14.44,-44,44a28.1,28.1,0,0,0,0,39.7,28.12,28.12,0,0,0,39.74,0l44,-44,26.81,26.8,-44,44a28.09,28.09,0,0,0,0,39.7,28.11,28.11,0,0,0,39.74,0l44,-44L41.91,-20A30,30,0,0,0,84.3,-20L213.16,-148.87Z"
android:fillColor="#6ab343"/>
</group>
</vector>

View File

@ -0,0 +1,77 @@
<!--
~ 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M35.58,297.76l6.18,-7.64l98.76,-8.5l100.14,101.33l-20,86.04l-7.5,6.29l-126.86,-7.63l-50.72,-169.89z"
android:fillColor="#fff"/>
<path
android:pathData="M389,302.51q-28.14,0 -47.68,19.54T321.8,369.73q0,28.15 19.55,48.08T389,437.74q28.14,0 48.08,-19.93T457,369.73q0,-28.14 -19.93,-47.68T389,302.51Z"
android:fillColor="#6ab343"/>
<path
android:pathData="M389.17,326.32a43.23,43.23 0,0 0,-43.55 43.55A42.84,42.84 0,0 0,358.28 401a41.6,41.6 0,0 0,30.89 12.92,44.09 44.09,0 0,0 44.05,-44.06A41.61,41.61 0,0 0,420.31 339,42.91 42.91,0 0,0 389.17,326.32Z"
android:fillColor="#f5f5f5"/>
<path
android:pathData="M389.4,363.79L389.4,340.35"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#6ab343"
android:strokeLineCap="round"/>
<path
android:pathData="M394.5,373.69L412.12,384.46"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#6ab343"
android:strokeLineCap="round"/>
<path
android:pathData="M389.39,363.79a6.25,6.25 0,0 0,-6.3 6.3,6.2 6.2,0 0,0 1.83,4.5 6,6 0,0 0,4.47 1.87,6.37 6.37,0 0,0 6.36,-6.37 6,6 0,0 0,-1.86 -4.47A6.21,6.21 0,0 0,389.39 363.79Z"
android:strokeWidth="7"
android:fillColor="#f5f5f5"
android:strokeColor="#6ab343"/>
<group>
<clip-path android:pathData="M584.94,0h512v512h-512z M 0,0"/>
<path
android:pathData="M448.55,437.05,709.34,697.84a8.72,8.72,0,0,0,12.32,0l325.45,-325.45a8.72,8.72,0,0,0,0,-12.32L778,91a18.84,18.84,0,0,0,-26.65,0L442,382.4l-2.36,5.16A44.07,44.07,0,0,0,448.55,437.05Z"
android:fillColor="#43a047"/>
<path
android:pathData="M440.25,415.17l275.52002,275.52l335.0,-334.99l-278.15002,-278.15002l-334.26,328.40002l1.8900146,9.220001z"
android:fillColor="#c0ca33"/>
<path
android:pathData="M544.72,137.12h393.36v473.76h-393.36z"
android:fillColor="#e6ee9c"/>
<path
android:pathData="M548.74,117.2H930.26a8.41,8.41,0,0,1,8.41,8.41V586.47a8.41,8.41,0,0,1,-8.41,8.41H548.74a18.19,18.19,0,0,1,-18.19,-18.19V135.4a18.19,18.19,0,0,1,18.19,-18.19Z"
android:fillColor="#6ab343"/>
<path
android:pathData="M601.64,-8.95h13.65v477.68h-13.65z"
android:fillColor="#43a047"/>
<path
android:pathData="M612.29,-1.71h6.82v477.68h-6.82z"
android:fillColor="#66bb6a"/>
</group>
<group>
<clip-path android:pathData="M0,0h512v512h-512z M 0,0"/>
<path
android:pathData="M246.59,351.91l39.17,-11.45a3.89,3.89,0,1,0,-2.17,-7.47L244,344.56a136.82,136.82,0,0,0,-77.69,-77.77l11.49,-39.53a3.89,3.89,0,1,0,-7.46,-2.19l-11.42,39.16c-41.3,-13.19,-86.47,-4.84,-117.17,25.89L220.66,469c30.72,-30.71,39,-75.78,25.93,-117.08m-211,-54.15L-93.27,426.67a29.94,29.94,0,0,0,0,42.38l14.43,14.43,-44,44a28.11,28.11,0,0,0,0,39.71,28.12,28.12,0,0,0,39.74,0l44,-44L-12.31,550l-44,44a28.1,28.1,0,0,0,0,39.71,28.1,28.1,0,0,0,39.74,0l44,-44,14.49,14.47a30,30,0,0,0,42.39,0l128.86,-128.9Zm180.59,90.92L203.59,376.1a1.71,1.71,0,0,1,0,-2.41l1.59,-1.59a1.71,1.71,0,0,1,2.41,0l12.58,12.58a1.71,1.71,0,0,1,0,2.41l-1.59,1.59A1.7,1.7,0,0,1,216.17,388.68Zm-81.43,-81.43,-12.58,-12.58a1.7,1.7,0,0,1,0,-2.4l1.59,-1.6a1.71,1.71,0,0,1,2.41,0l12.58,12.59a1.68,1.68,0,0,1,0,2.4l-1.59,1.59A1.7,1.7,0,0,1,134.74,307.25Zm-99.16,-9.49L-93.27,426.67a29.94,29.94,0,0,0,0,42.38l14.43,14.43,-44,44a28.11,28.11,0,0,0,0,39.71,28.12,28.12,0,0,0,39.74,0l44,-44L-12.31,550l-44,44a28.1,28.1,0,0,0,0,39.71,28.1,28.1,0,0,0,39.74,0l44,-44,14.49,14.47a30,30,0,0,0,42.39,0l128.86,-128.9Z"
android:fillColor="#6ab343"/>
</group>
</vector>

View File

@ -0,0 +1,28 @@
<!--
~ 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.
-->
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M81.158,261.465L145.387,272.79A5.49,5.49 55,0 1,149.84 279.15L149.832,279.199A5.49,5.49 55,0 1,143.472 283.652L79.243,272.327A5.49,5.49 55,0 1,74.789 265.967L74.798,265.918A5.49,5.49 55,0 1,81.158 261.465z"/>
<path android:fillColor="#fff" android:pathData="M214.909,272.293L278.825,261.206A5.461,5.461 125.159,0 1,285.138 265.652L285.147,265.701A5.461,5.461 125.159,0 1,280.7 272.015L216.784,283.102A5.461,5.461 125.159,0 1,210.471 278.655L210.462,278.606A5.461,5.461 125.159,0 1,214.909 272.293z"/>
<path android:fillColor="#fff" android:pathData="M87.58,410.05a9.55,9.55 0,0 1,-9.41 -11.11c7.36,-44.12 57.39,-78.27 103,-78.27s95.63,34.15 103,78.27a9.55,9.55 0,0 1,-9.41 11.11Z"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M377.25,147.51a45.14,45.14 0,0 1,-15.08 -19.2,68.28 68.28,0 0,1 -5.32,-27.53 68.23,68.23 0,0 1,5.32 -27.52,45.14 45.14,0 0,1 15.08,-19.2 40.24,40.24 0,0 1,45.3 0,45.12 45.12,0 0,1 15.07,19.2A68.23,68.23 0,0 1,443 100.78a68.28,68.28 0,0 1,-5.33 27.53,45.12 45.12,0 0,1 -15.07,19.2 40.29,40.29 0,0 1,-45.3 0ZM412.42,131.51a28.4,28.4 0,0 0,8.1 -12.6,55.08 55.08,0 0,0 2.78,-18.08q0,-15.59 -6.3,-25.42t-17.1,-9.83q-10.65,0 -16.95,9.9t-6.3,25.35a55.09,55.09 0,0 0,2.77 18.08,28.4 28.4,0 0,0 8.1,12.6 19.24,19.24 0,0 0,24.9 0Z"/>
</vector>

View File

@ -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.
-->
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M80.313,264.221L145.284,269.905A5.49,5.49 50,0 1,150.275 275.853L150.271,275.903A5.49,5.49 50,0 1,144.323 280.893L79.351,275.209A5.49,5.49 50,0 1,74.361 269.261L74.365,269.212A5.49,5.49 50,0 1,80.313 264.221z"/>
<path android:fillColor="#fff" android:pathData="M217.037,269.907L282.009,264.223A5.49,5.49 130,0 1,287.956 269.213L287.961,269.263A5.49,5.49 130,0 1,282.97 275.211L217.998,280.895A5.49,5.49 130,0 1,212.051 275.904L212.046,275.855A5.49,5.49 130,0 1,217.037 269.907z"/>
<path android:fillColor="#00000000"
android:pathData="M92.91,395.3c19.86,-26.3 54.92,-43.94 87.69,-43.94 33.08,0 68.5,18 88.26,44.69"
android:strokeColor="#fff" android:strokeLineCap="round" android:strokeWidth="20"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M400.67,75.13 L385.22,86.38l-9.9,-15.15 30.15,-21.75h14.85v102.6H400.67Z"/>
</vector>

View File

@ -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.
-->
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M79.71,267.04L144.93,267.04A5.49,5.49 0,0 1,150.42 272.53L150.42,272.58A5.49,5.49 0,0 1,144.93 278.07L79.71,278.07A5.49,5.49 0,0 1,74.22 272.58L74.22,272.53A5.49,5.49 0,0 1,79.71 267.04z"/>
<path android:fillColor="#fff" android:pathData="M217.39,267.04L282.61,267.04A5.49,5.49 0,0 1,288.1 272.53L288.1,272.58A5.49,5.49 0,0 1,282.61 278.07L217.39,278.07A5.49,5.49 0,0 1,211.9 272.58L211.9,272.53A5.49,5.49 0,0 1,217.39 267.04z"/>
<path android:fillColor="#00000000"
android:pathData="M268.86,383.11L92.91,382.36"
android:strokeColor="#fff" android:strokeLineCap="round" android:strokeWidth="20"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M366.07,134.23l19.13,-19.12q14.18,-14.17 18.22,-18.53a48.84,48.84 0,0 0,7.43 -9.75,18.89 18.89,0 0,0 2,-8.85 11.59,11.59 0,0 0,-3.9 -8.85q-3.9,-3.6 -10.35,-3.6a14.44,14.44 0,0 0,-10.2 3.6,19.9 19.9,0 0,0 -5.55,9l-17.7,-7.35a31.77,31.77 0,0 1,6.08 -11.32,33.4 33.4,0 0,1 11.47,-8.93 36.54,36.54 0,0 1,16.2 -3.45,38.19 38.19,0 0,1 17.85,4 29.55,29.55 0,0 1,11.85 10.8,28.48 28.48,0 0,1 4.2,15.22q0,16.65 -16.2,32.55 -6.6,6.47 -24.45,24.3l0.45,0.9h41.55v17.25h-68.1Z"/>
</vector>

View File

@ -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.
-->
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M377,147.58q-10,-6.89 -13.73,-20.1l18.6,-7.35q1.8,7.37 6.45,11.33a17.37,17.37 0,0 0,11.7 4,17 17,0 0,0 11.18,-3.9 12.88,12.88 0,0 0,-0.3 -20.17q-5,-3.83 -13.58,-3.83h-8.85V89.68h8.1a18.54,18.54 0,0 0,11.18 -3.3c3.05,-2.19 4.57,-5.4 4.57,-9.6a10.72,10.72 0,0 0,-3.75 -8.4,14 14,0 0,0 -9.6,-3.3 14.49,14.49 0,0 0,-9.9 3.3,17.57 17.57,0 0,0 -5.25,8.25l-17.85,-7.35a33.92,33.92 0,0 1,11.63 -15.6q8.47,-6.59 21.52,-6.6a39.56,39.56 0,0 1,17.18 3.6A28.64,28.64 0,0 1,428 60.51a24.51,24.51 0,0 1,4.2 14,25.17 25.17,0 0,1 -3.82,14.1 23.8,23.8 0,0 1,-9.23 8.55v1.2a27.18,27.18 0,0 1,12.15 9.45,26 26,0 0,1 4.65,15.6 28.87,28.87 0,0 1,-4.5 15.9,30.82 30.82,0 0,1 -12.67,11.1 41.9,41.9 0,0 1,-18.83 4A39.46,39.46 0,0 1,377 147.58Z"/>
<path android:fillColor="#fff" android:pathData="M79.71,267.04L144.93,267.04A5.49,5.49 0,0 1,150.42 272.53L150.42,272.58A5.49,5.49 0,0 1,144.93 278.07L79.71,278.07A5.49,5.49 0,0 1,74.22 272.58L74.22,272.53A5.49,5.49 0,0 1,79.71 267.04z"/>
<path android:fillColor="#fff" android:pathData="M217.39,267.04L282.61,267.04A5.49,5.49 0,0 1,288.1 272.53L288.1,272.58A5.49,5.49 0,0 1,282.61 278.07L217.39,278.07A5.49,5.49 0,0 1,211.9 272.58L211.9,272.53A5.49,5.49 0,0 1,217.39 267.04z"/>
<path android:fillColor="#00000000"
android:pathData="M268.86,377.57c-19.87,8.69 -54.93,14.52 -87.7,14.52 -33.08,0 -68.49,-5.94 -88.25,-14.77"
android:strokeColor="#fff" android:strokeLineCap="round" android:strokeWidth="20"/>
</vector>

View File

@ -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.
-->
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M79.351,269.907L144.323,264.222A5.49,5.49 130,0 1,150.27 269.213L150.275,269.263A5.49,5.49 130,0 1,145.284 275.21L80.312,280.895A5.49,5.49 130,0 1,74.364 275.904L74.36,275.854A5.49,5.49 130,0 1,79.351 269.907z"/>
<path android:fillColor="#fff" android:pathData="M218.066,265.855L283.286,271.725A5.512,5.512 50.143,0 1,288.282 277.709L288.278,277.759A5.512,5.512 50.143,0 1,282.294 282.755L217.074,276.885A5.512,5.512 50.143,0 1,212.078 270.901L212.082,270.851A5.512,5.512 50.143,0 1,218.066 265.855z"/>
<path android:fillColor="#00000000"
android:pathData="M268.86,363.11c-19.87,26.3 -54.93,43.94 -87.7,43.94 -33.08,0 -68.49,-18 -88.25,-44.69"
android:strokeColor="#fff" android:strokeLineCap="round" android:strokeWidth="20"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M402.17,133.18L354.92,133.18v-15.9l45.6,-67.8h21.3v65.4h12.75v18.3L421.82,133.18v18.9L402.17,152.08ZM402.17,114.88L402.17,78.73L401,78.73l-24.3,36.15Z"/>
</vector>

View File

@ -0,0 +1,28 @@
<!--
~ 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.
-->
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M79.25,272.792L143.479,261.466A5.49,5.49 125,0 1,149.839 265.919L149.848,265.969A5.49,5.49 125,0 1,145.395 272.329L81.165,283.654A5.49,5.49 125,0 1,74.805 279.201L74.797,279.151A5.49,5.49 125,0 1,79.25 272.792z"/>
<path android:fillColor="#fff" android:pathData="M218.846,261.462L283.076,272.788A5.49,5.49 55,0 1,287.529 279.148L287.52,279.197A5.49,5.49 55,0 1,281.16 283.65L216.931,272.325A5.49,5.49 55,0 1,212.478 265.965L212.486,265.916A5.49,5.49 55,0 1,218.846 261.462z"/>
<path android:fillColor="#fff" android:pathData="M274.74,337.67a9.55,9.55 0,0 1,9.41 11.11c-7.36,44.12 -57.39,78.27 -103,78.27s-95.63,-34.15 -103,-78.27a9.55,9.55 0,0 1,9.41 -11.11Z"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M383.77,151.41a35.84,35.84 0,0 1,-13.12 -9.3,33.67 33.67,0 0,1 -7.73,-15.38l17.7,-6.9a23.14,23.14 0,0 0,6.6 12.08,17 17,0 0,0 12,4.42 16.24,16.24 0,0 0,12 -4.8,16.43 16.43,0 0,0 4.8,-12.15 16.66,16.66 0,0 0,-4.72 -12.07,16.1 16.1,0 0,0 -12.08,-4.88 17.71,17.71 0,0 0,-8.17 1.88,19.05 19.05,0 0,0 -6.23,5l-19.05,-8.55 5.85,-51.3h57.9V66.73H388l-3.75,24 1.2,0.3a27.32,27.32 0,0 1,17.85 -6.15,31.18 31.18,0 0,1 16,4.35 32.65,32.65 0,0 1,11.92 12.23,35.49 35.49,0 0,1 4.5,17.92A35.22,35.22 0,0 1,431 137.53,32.81 32.81,0 0,1 418.12,150a38.51,38.51 0,0 1,-18.75 4.5A40.73,40.73 0,0 1,383.77 151.41Z"/>
</vector>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
app:fontProviderAuthority="com.google.android.gms.fonts"
app:fontProviderPackage="com.google.android.gms"
app:fontProviderQuery="Roboto"
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<!-- The merge tag can be used to eliminate redundant layouts when
including layouts, and it's a good idea to use it.
See: https://developer.android.com/training/improving-layouts/reusing-layouts -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation" />
</merge>

View File

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<!-- Wrapping the layout into /layout to make it available with data binding. -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<!-- Data to make available to the XML via data binding. In this case,
the whole ViewModel, so that we can access the LiveData,
click handlers, and state variables. -->
<data>
</data>
<!-- Start of the visible fragment layout using ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".sleepquality.SleepQualityFragment">
<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin"
android:layout_marginTop="@dimen/margin"
android:layout_marginBottom="@dimen/triple_margin"
android:fontFamily="@font/roboto"
android:text="@string/how_was_hour_sleep"
android:textSize="@dimen/title_text_size"
app:layout_constraintBottom_toTopOf="@+id/quality_zero_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/quality_zero_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginStart="@dimen/margin"
android:layout_marginTop="@dimen/margin"
android:contentDescription="@string/quality_0"
app:layout_constraintEnd_toStartOf="@+id/quality_one_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_text"
app:srcCompat="@drawable/ic_sleep_0" />
<ImageView
android:id="@+id/quality_one_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:contentDescription="@string/quality_1"
app:layout_constraintBottom_toBottomOf="@+id/quality_zero_image"
app:layout_constraintEnd_toStartOf="@+id/quality_two_image"
app:layout_constraintStart_toEndOf="@+id/quality_zero_image"
app:srcCompat="@drawable/ic_sleep_1" />
<ImageView
android:id="@+id/quality_two_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginEnd="@dimen/margin"
android:contentDescription="@string/quality_2"
app:layout_constraintBottom_toBottomOf="@+id/quality_one_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_one_image"
app:srcCompat="@drawable/ic_sleep_2" />
<ImageView
android:id="@+id/quality_three_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginStart="@dimen/margin"
android:layout_marginTop="@dimen/margin"
android:layout_marginBottom="@dimen/margin"
android:contentDescription="@string/quality_3"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/quality_four_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/quality_zero_image"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/ic_sleep_3" />
<ImageView
android:id="@+id/quality_four_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:contentDescription="@string/quality_4"
app:layout_constraintBottom_toBottomOf="@+id/quality_three_image"
app:layout_constraintEnd_toStartOf="@+id/quality_five_image"
app:layout_constraintStart_toEndOf="@+id/quality_three_image"
app:layout_constraintTop_toTopOf="@+id/quality_three_image"
app:srcCompat="@drawable/ic_sleep_4" />
<ImageView
android:id="@+id/quality_five_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginEnd="@dimen/margin"
android:contentDescription="@string/quality_5"
app:layout_constraintBottom_toBottomOf="@+id/quality_four_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_four_image"
app:layout_constraintTop_toTopOf="@+id/quality_four_image"
app:srcCompat="@drawable/ic_sleep_5" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<!-- Wrapping the layout into /layout to make it available with data binding. -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<!-- Data to make available to the XML via data binding. In this case,
the whole ViewModel, so that we can access the LiveData,
click handlers, and state variables. -->
<data>
<variable
name="sleepTrackerViewModel"
type="com.example.android.trackmysleepquality.sleeptracker.SleepTrackerViewModel" />
</data>
<!-- Start of the visible fragment layout using ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".sleeptracker.SleepTrackerFragment">
<!-- Simplest way of displaying scrollable text and data. There is a
better and more efficient way to do this, and you will learn about
RecyclerView in a later lesson. -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/clear_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stop_button">
<!-- In the TextView, we can access the nightsString LiveData,
which keeps it displayed and updated in the TextView
whenever it changes. -->
<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin"
android:layout_marginTop="@dimen/margin"
android:layout_marginEnd="@dimen/margin"
android:text="@{sleepTrackerViewModel.nightsString}" />
</ScrollView>
<!-- With data binding and LiveData, we can track the buttons' visibility states
from the ViewModel. The click handler is in the ViewModel as well, and
you can set it for the Views using this lambda pattern. -->
<Button
android:id="@+id/start_button"
style="@style/SleepButtons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin"
android:text="@string/start"
app:layout_constraintBaseline_toBaselineOf="@id/stop_button"
app:layout_constraintEnd_toStartOf="@+id/stop_button"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
android:onClick="@{() -> sleepTrackerViewModel.onStartTracking()}"/>
<Button
android:id="@+id/stop_button"
style="@style/SleepButtons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin"
android:layout_marginEnd="@dimen/margin"
android:text="@string/stop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/start_button"
app:layout_constraintTop_toTopOf="parent"
android:onClick="@{() -> sleepTrackerViewModel.onStopTracking()}"/>
<Button
android:id="@+id/clear_button"
style="@style/SleepButtons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin"
android:layout_marginEnd="@dimen/margin"
android:layout_marginBottom="@dimen/margin"
android:text="@string/clear"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:onClick="@{() -> sleepTrackerViewModel.onClear()}"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_sleep_tracker_background" />
<foreground android:drawable="@drawable/ic_launcher_sleep_tracker_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_sleep_tracker_background" />
<foreground android:drawable="@drawable/ic_launcher_sleep_tracker_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_sleep_tracker_background"/>
<foreground android:drawable="@drawable/ic_launcher_sleep_tracker_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_sleep_tracker_background"/>
<foreground android:drawable="@drawable/ic_launcher_sleep_tracker_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<!-- Note that the animations may show red in Android Studio;
however, they are present and working perfectly fine. -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation"
app:startDestination="@id/sleep_tracker_fragment">
<fragment
android:id="@+id/sleep_tracker_fragment"
android:name="com.example.android.trackmysleepquality.sleeptracker.SleepTrackerFragment"
android:label="@string/sleep_tracker_fragment"
tools:layout="@layout/fragment_sleep_tracker">
<action
android:id="@+id/action_sleepTrackerFragment_to_sleepQualityFragment"
app:destination="@id/sleep_quality_fragment"
app:enterAnim="@anim/slide_in_right"
app:popEnterAnim="@anim/slide_in_right" />
</fragment>
<fragment
android:id="@+id/sleep_quality_fragment"
android:name="com.example.android.trackmysleepquality.sleepquality.SleepQualityFragment"
android:label="@string/sleep_quality_fragment"
tools:layout="@layout/fragment_sleep_quality">
<argument
android:name="sleepNightKey"
app:argType="long" />
<action
android:id="@+id/action_sleepQualityFragment_to_sleepTrackerFragment"
app:destination="@id/sleep_tracker_fragment"
app:launchSingleTop="false"
app:popUpTo="@+id/sleep_tracker_fragment"
app:popUpToInclusive="true" />
</fragment>
</navigation>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<resources>
<color name="colorPrimary">#6ab343</color>
<color name="colorPrimaryDark">#388310</color>
<color name="colorAccent">#6ab343</color>
<color name="green_color">#388310</color>
<color name="white_text_color">#f0f0f0</color>
</resources>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<resources>
<dimen name="margin">16dp</dimen>
<dimen name="icon_size">64dp</dimen>
<dimen name="title_text_size">20sp</dimen>
<dimen name="triple_margin">48dp</dimen>
</resources>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<resources>
<array name="com_google_android_gms_fonts_certs">
<item>@array/com_google_android_gms_fonts_certs_dev</item>
<item>@array/com_google_android_gms_fonts_certs_prod</item>
</array>
<string-array name="com_google_android_gms_fonts_certs_dev">
<item>
MIIEqDCCA5CgAwIBAgIJANWFuGx90071MA0GCSqGSIb3DQEBBAUAMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAeFw0wODA0MTUyMzM2NTZaFw0zNTA5MDEyMzM2NTZaMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBANbOLggKv+IxTdGNs8/TGFy0PTP6DHThvbbR24kT9ixcOd9W+EaBPWW+wPPKQmsHxajtWjmQwWfna8mZuSeJS48LIgAZlKkpFeVyxW0qMBujb8X8ETrWy550NaFtI6t9+u7hZeTfHwqNvacKhp1RbE6dBRGWynwMVX8XW8N1+UjFaq6GCJukT4qmpN2afb8sCjUigq0GuMwYXrFVee74bQgLHWGJwPmvmLHC69EH6kWr22ijx4OKXlSIx2xT1AsSHee70w5iDBiK4aph27yH3TxkXy9V89TDdexAcKk/cVHYNnDBapcavl7y0RiQ4biu8ymM8Ga/nmzhRKya6G0cGw8CAQOjgfwwgfkwHQYDVR0OBBYEFI0cxb6VTEM8YYY6FbBMvAPyT+CyMIHJBgNVHSMEgcEwgb6AFI0cxb6VTEM8YYY6FbBMvAPyT+CyoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJANWFuGx90071MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADggEBABnTDPEF+3iSP0wNfdIjIz1AlnrPzgAIHVvXxunW7SBrDhEglQZBbKJEk5kT0mtKoOD1JMrSu1xuTKEBahWRbqHsXclaXjoBADb0kkjVEJu/Lh5hgYZnOjvlba8Ld7HCKePCVePoTJBdI4fvugnL8TsgK05aIskyY0hKI9L8KfqfGTl1lzOv2KoWD0KWwtAWPoGChZxmQ+nBli+gwYMzM1vAkP+aayLe0a1EQimlOalO762r0GXO0ks+UeXde2Z4e+8S/pf7pITEI/tP+MxJTALw9QUWEv9lKTk+jkbqxbsh8nfBUapfKqYn0eidpwq2AzVp3juYl7//fKnaPhJD9gs=
</item>
</string-array>
<string-array name="com_google_android_gms_fonts_certs_prod">
<item>
MIIEQzCCAyugAwIBAgIJAMLgh0ZkSjCNMA0GCSqGSIb3DQEBBAUAMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDAeFw0wODA4MjEyMzEzMzRaFw0zNjAxMDcyMzEzMzRaMHQxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtHb29nbGUgSW5jLjEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDCCASAwDQYJKoZIhvcNAQEBBQADggENADCCAQgCggEBAKtWLgDYO6IIrgqWbxJOKdoR8qtW0I9Y4sypEwPpt1TTcvZApxsdyxMJZ2JORland2qSGT2y5b+3JKkedxiLDmpHpDsz2WCbdxgxRczfey5YZnTJ4VZbH0xqWVW/8lGmPav5xVwnIiJS6HXk+BVKZF+JcWjAsb/GEuq/eFdpuzSqeYTcfi6idkyugwfYwXFU1+5fZKUaRKYCwkkFQVfcAs1fXA5V+++FGfvjJ/CxURaSxaBvGdGDhfXE28LWuT9ozCl5xw4Yq5OGazvV24mZVSoOO0yZ31j7kYvtwYK6NeADwbSxDdJEqO4k//0zOHKrUiGYXtqw/A0LFFtqoZKFjnkCAQOjgdkwgdYwHQYDVR0OBBYEFMd9jMIhF1Ylmn/Tgt9r45jk14alMIGmBgNVHSMEgZ4wgZuAFMd9jMIhF1Ylmn/Tgt9r45jk14aloXikdjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLR29vZ2xlIEluYy4xEDAOBgNVBAsTB0FuZHJvaWQxEDAOBgNVBAMTB0FuZHJvaWSCCQDC4IdGZEowjTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBAUAA4IBAQBt0lLO74UwLDYKqs6Tm8/yzKkEu116FmH4rkaymUIE0P9KaMftGlMexFlaYjzmB2OxZyl6euNXEsQH8gjwyxCUKRJNexBiGcCEyj6z+a1fuHHvkiaai+KL8W1EyNmgjmyy8AW7P+LLlkR+ho5zEHatRbM/YAnqGcFh5iZBqpknHf1SKMXFh4dd239FJ1jWYfbMDMy3NS5CTMQ2XFI1MvcyUTdZPErjQfTbQe3aDQsQcafEQPD+nqActifKZ0Np0IS9L9kR/wbNvyz6ENwPiTrjV2KRkEjH78ZMcUQXg0L3BYHJ3lc69Vs5Ddf9uUGGMYldX3WfMBEmh/9iFBDAaTCK
</item>
</string-array>
</resources>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<resources>
<array name="preloaded_fonts" translatable="false">
<item>@font/roboto</item>
</array>
</resources>

View File

@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<resources>
<!-- App title and fragment titles -->
<string name="app_name">Track My Sleep Quality</string>
<string name="sleep_tracker_fragment">SleepTrackerFragment</string>
<string name="sleep_quality_fragment">SleepQualityFragment</string>
<!-- Button labels -->
<string name="start">Start</string>
<string name="stop">Stop</string>
<string name="clear">Clear</string>
<!-- contentDescription strings for quality icons -->
<string name="quality_0">Sleep Quality 0</string>
<string name="quality_1">Sleep Quality 1</string>
<string name="quality_2">Sleep Quality 2</string>
<string name="quality_3">Sleep Quality 3</string>
<string name="quality_4">Sleep Quality 4</string>
<string name="quality_5">Sleep Quality 5</string>
<!-- Output to TextView styled with a little HTML -->
<string name="title"><![CDATA[<h3>HERE IS YOUR SLEEP DATA</h3>]]></string>
<string name="start_time"><![CDATA[<b>Start:</b>]]></string>
<string name="end_time"><![CDATA[<b>End:</b>]]></string>
<string name="quality"><![CDATA[<b>Quality:</b>]]></string>
<string name="hours_slept"><![CDATA[<b>Hours:Minutes:Seconds</b>]]></string>
<!-- Sleep quality strings -->
<string name="how_was_hour_sleep">How was your sleep?</string>
<string name="zero_very_bad">Very bad</string>
<string name="one_poor">Poor</string>
<string name="two_soso">So-so</string>
<string name="three_ok">OK</string>
<string name="four_pretty_good">Pretty good</string>
<string name="five_excellent">Excellent!</string>
<!-- Toast to play after Clear button has been pressed -->
<string name="cleared_message">All your data is gone forever.</string>
</resources>

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<!-- Basic styling for the buttons. -->
<style name="SleepButtons" parent="Widget.AppCompat.Button.Colored">
<item name="android:textColor">@color/white_text_color</item>
<item name="colorAccent">@color/green_color</item>
<item name="colorControlHighlight">@color/green_color</item>
<item name="android:textStyle">bold</item>
</style>
</resources>

View File

@ -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.11'
archLifecycleVersion = '1.1.1'
room_version = '2.0.0'
coroutine_version = '1.0.0'
gradleVersion = '3.3.0'
navigationVersion = '1.0.0-alpha08'
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
}

View File

@ -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

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Tue Oct 02 13:39:27 PDT 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip

172
TrackMySleepQualityCoroutines/gradlew vendored Executable file
View File

@ -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" "$@"

84
TrackMySleepQualityCoroutines/gradlew.bat vendored Executable file
View File

@ -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

View File

@ -0,0 +1 @@
include ':app'

View File

@ -0,0 +1,54 @@
TrackMySleepQuality - Final Code for 6.3 Recording Quality & Button States
============================================================================
Solution code for Android Kotlin Fundamentals Codelab 6.3 Recording Quality & Button States
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, working from this starter app, you:
* Extended the TrackMySleepQuality app to collect a quality rating, add it to the database and display the result.
* Usedd LiveData to trigger showing of a snackbar.
* Use LiveData to enable and disable buttons.
Pre-requisites
--------------
You should be familiar with:
* Navigating between fragments, and using safeArgs to pass data between fragments.
* View models, view model factories, Transformations, and LiveData and their observers
from the preceding Architecture Components codelab[LINK].
* How to create a Room database, create a DAO, and define entities.
* How to use coroutines for database interactions 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.

1
TrackMySleepQualityFinal/app/.gitignore vendored Executable file
View File

@ -0,0 +1 @@
/build

View File

@ -0,0 +1,79 @@
/*
* 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:1.1.3"
// Android KTX
implementation 'androidx.core:core-ktx:1.0.1'
// Room and Lifecycle dependencies
implementation "androidx.room:room-runtime:$room_version"
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'
}

View File

@ -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

View File

@ -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
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)
}
}

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.trackmysleepquality">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher_sleep_tracker"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_sleep_tracker_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

View File

@ -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)
}
}

View File

@ -0,0 +1,106 @@
/*
* 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<SleepNight>, resources: Resources): Spanned {
val sb = StringBuilder()
sb.apply {
append(resources.getString(R.string.title))
nights.forEach {
append("<br>")
append(resources.getString(R.string.start_time))
append("\t${convertLongToDateString(it.startTimeMilli)}<br>")
if (it.endTimeMilli != it.startTimeMilli) {
append(resources.getString(R.string.end_time))
append("\t${convertLongToDateString(it.endTimeMilli)}<br>")
append(resources.getString(R.string.quality))
append("\t${convertNumericQualityToString(it.sleepQuality, resources)}<br>")
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}<br><br>")
}
}
}
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)
}
}

View File

@ -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
}
}
}
}

View File

@ -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.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<List<SleepNight>>
/**
* Selects and returns the latest night.
*/
@Query("SELECT * FROM daily_sleep_quality_table ORDER BY nightId DESC LIMIT 1")
fun getTonight(): SleepNight?
}

View File

@ -0,0 +1,37 @@
/*
* 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
)

View File

@ -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
}
}

View File

@ -0,0 +1,108 @@
/*
* 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,
val database: SleepDatabaseDao) : ViewModel() {
/** 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<Boolean?>()
/**
* When true immediately navigate back to the [SleepTrackerFragment]
*/
val navigateToSleepTracker: LiveData<Boolean?>
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) ?: return@withContext
tonight.sleepQuality = quality
database.update(tonight)
}
// Setting this state variable to true will alert the observer and trigger navigation.
_navigateToSleepTracker.value = true
}
}
}

View File

@ -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 <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(SleepQualityViewModel::class.java)) {
return SleepQualityViewModel(sleepNightKey, dataSource) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

View File

@ -0,0 +1,111 @@
/*
* 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
// 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 snackbar 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
}
}

View File

@ -0,0 +1,246 @@
/*
* 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.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(
val database: SleepDatabaseDao,
application: Application) : AndroidViewModel(application) {
/** 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<SleepNight?>()
private 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<Boolean>()
/**
* If this is true, immediately `show()` a toast and call `doneShowingSnackbar()`.
*/
val showSnackBarEvent: LiveData<Boolean>
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<SleepNight>()
/**
* If this is non-null, immediately navigate to [SleepQualityFragment] and call [doneNavigating]
*/
val navigateToSleepQuality: LiveData<SleepNight>
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 = false
}
/**
* 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 onStartTracking() {
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 onStopTracking() {
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(),
// not the lambda.
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()
}
}

View File

@ -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 <T : ViewModel?> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
return SleepTrackerViewModel(dataSource, application) as T
}
throw IllegalArgumentException("Unknown ViewModel class")
}
}

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="700"
android:fromXDelta="100%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%" />
</set>

View File

@ -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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M0,369.07h512v142.93h-512z"
android:fillColor="#e0e0e0"/>
<path
android:pathData="M228.55,271.42l15.7,-30.1L225,244.55l-1,-5.78 26.34,-4.41 1,6.22 -15.6,30.07L256,267.26l1,5.79 -27.42,4.59Z"
android:fillColor="#757575"/>
<path
android:pathData="M271.75,215.33l41.52,-29.2 -27.86,-9 2.68,-8.35 38,12.23 -2.89,9 -41.37,29.25 29.28,9.41L308.44,237l-39.58,-12.73Z"
android:fillColor="#bdbdbd"/>
<path
android:pathData="M350,187.18l39.94,-79.32L339.54,117l-2.75,-15.11 68.74,-12.52 3,16.23 -39.67,79.27 52.94,-9.64 2.75,15.1 -71.57,13Z"
android:fillColor="#e0e0e0"/>
<group>
<clip-path android:pathData="M584.94,-624.15h512v512h-512z M 0,0"/>
<path
android:pathData="M448.55,-187.1,709.34,73.69a8.72,8.72,0,0,0,12.32,0l325.45,-325.45a8.72,8.72,0,0,0,0,-12.32L778,-533.19a18.84,18.84,0,0,0,-26.65,0L442,-241.74l-2.36,5.16A44.05,44.05,0,0,0,448.55,-187.1Z"
android:fillColor="#43a047"/>
<path
android:pathData="M440.25,-208.98l275.52002,275.53l335.0,-335.0l-278.15002,-278.14996l-334.26,328.40997l1.8900146,9.210007z"
android:fillColor="#c0ca33"/>
<path
android:pathData="M544.72,-487.03h393.36v473.76h-393.36z"
android:fillColor="#e6ee9c"/>
<path
android:pathData="M548.74,-506.95H930.26a8.41,8.41,0,0,1,8.41,8.41V-37.68a8.41,8.41,0,0,1,-8.41,8.41H548.74a18.19,18.19,0,0,1,-18.19,-18.19V-488.75A18.19,18.19,0,0,1,548.74,-506.95Z"
android:fillColor="#6ab343"/>
</group>
<group>
<clip-path android:pathData="M0,-624.15h512v512h-512z M 0,0"/>
<path
android:pathData="M246.59,-272.23l39.17,-11.46a3.9,3.9,0,0,0,2.67,-4.84,3.9,3.9,0,0,0,-4.84,-2.63L244,-279.58a134.83,134.83,0,0,0,-30.74,-47.09,134.89,134.89,0,0,0,-47,-30.69l11.49,-39.52a3.86,3.86,0,0,0,-2.63,-4.83,3.85,3.85,0,0,0,-4.83,2.64l-11.42,39.16c-41.3,-13.2,-86.47,-4.85,-117.17,25.89l178.9,178.87c30.72,-30.71,39,-75.79,25.93,-117.08m-211,-54.15L-93.27,-197.48a29.94,29.94,0,0,0,0,42.38l14.43,14.44,-44,44a28.1,28.1,0,0,0,0,39.7,28.12,28.12,0,0,0,39.74,0l44,-44,26.81,26.8,-44,44a28.09,28.09,0,0,0,0,39.7,28.11,28.11,0,0,0,39.74,0l44,-44L41.91,-20A30,30,0,0,0,84.3,-20L213.16,-148.87Zm180.59,90.91L203.59,-248a1.7,1.7,0,0,1,0,-2.4l1.59,-1.6a1.71,1.71,0,0,1,2.41,0l12.58,12.59a1.7,1.7,0,0,1,0,2.4l-1.59,1.59A1.7,1.7,0,0,1,216.17,-235.47Zm-81.43,-81.42,-12.58,-12.59a1.7,1.7,0,0,1,0,-2.4l1.59,-1.59a1.7,1.7,0,0,1,2.41,0l12.58,12.58a1.68,1.68,0,0,1,0,2.4l-1.59,1.6A1.71,1.71,0,0,1,134.74,-316.89Zm-99.16,-9.49L-93.27,-197.48a29.94,29.94,0,0,0,0,42.38l14.43,14.44,-44,44a28.1,28.1,0,0,0,0,39.7,28.12,28.12,0,0,0,39.74,0l44,-44,26.81,26.8,-44,44a28.09,28.09,0,0,0,0,39.7,28.11,28.11,0,0,0,39.74,0l44,-44L41.91,-20A30,30,0,0,0,84.3,-20L213.16,-148.87Z"
android:fillColor="#6ab343"/>
</group>
</vector>

View File

@ -0,0 +1,77 @@
<!--
~ 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="512"
android:viewportHeight="512">
<path
android:pathData="M35.58,297.76l6.18,-7.64l98.76,-8.5l100.14,101.33l-20,86.04l-7.5,6.29l-126.86,-7.63l-50.72,-169.89z"
android:fillColor="#fff"/>
<path
android:pathData="M389,302.51q-28.14,0 -47.68,19.54T321.8,369.73q0,28.15 19.55,48.08T389,437.74q28.14,0 48.08,-19.93T457,369.73q0,-28.14 -19.93,-47.68T389,302.51Z"
android:fillColor="#6ab343"/>
<path
android:pathData="M389.17,326.32a43.23,43.23 0,0 0,-43.55 43.55A42.84,42.84 0,0 0,358.28 401a41.6,41.6 0,0 0,30.89 12.92,44.09 44.09,0 0,0 44.05,-44.06A41.61,41.61 0,0 0,420.31 339,42.91 42.91,0 0,0 389.17,326.32Z"
android:fillColor="#f5f5f5"/>
<path
android:pathData="M389.4,363.79L389.4,340.35"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#6ab343"
android:strokeLineCap="round"/>
<path
android:pathData="M394.5,373.69L412.12,384.46"
android:strokeLineJoin="round"
android:strokeWidth="10"
android:fillColor="#00000000"
android:strokeColor="#6ab343"
android:strokeLineCap="round"/>
<path
android:pathData="M389.39,363.79a6.25,6.25 0,0 0,-6.3 6.3,6.2 6.2,0 0,0 1.83,4.5 6,6 0,0 0,4.47 1.87,6.37 6.37,0 0,0 6.36,-6.37 6,6 0,0 0,-1.86 -4.47A6.21,6.21 0,0 0,389.39 363.79Z"
android:strokeWidth="7"
android:fillColor="#f5f5f5"
android:strokeColor="#6ab343"/>
<group>
<clip-path android:pathData="M584.94,0h512v512h-512z M 0,0"/>
<path
android:pathData="M448.55,437.05,709.34,697.84a8.72,8.72,0,0,0,12.32,0l325.45,-325.45a8.72,8.72,0,0,0,0,-12.32L778,91a18.84,18.84,0,0,0,-26.65,0L442,382.4l-2.36,5.16A44.07,44.07,0,0,0,448.55,437.05Z"
android:fillColor="#43a047"/>
<path
android:pathData="M440.25,415.17l275.52002,275.52l335.0,-334.99l-278.15002,-278.15002l-334.26,328.40002l1.8900146,9.220001z"
android:fillColor="#c0ca33"/>
<path
android:pathData="M544.72,137.12h393.36v473.76h-393.36z"
android:fillColor="#e6ee9c"/>
<path
android:pathData="M548.74,117.2H930.26a8.41,8.41,0,0,1,8.41,8.41V586.47a8.41,8.41,0,0,1,-8.41,8.41H548.74a18.19,18.19,0,0,1,-18.19,-18.19V135.4a18.19,18.19,0,0,1,18.19,-18.19Z"
android:fillColor="#6ab343"/>
<path
android:pathData="M601.64,-8.95h13.65v477.68h-13.65z"
android:fillColor="#43a047"/>
<path
android:pathData="M612.29,-1.71h6.82v477.68h-6.82z"
android:fillColor="#66bb6a"/>
</group>
<group>
<clip-path android:pathData="M0,0h512v512h-512z M 0,0"/>
<path
android:pathData="M246.59,351.91l39.17,-11.45a3.89,3.89,0,1,0,-2.17,-7.47L244,344.56a136.82,136.82,0,0,0,-77.69,-77.77l11.49,-39.53a3.89,3.89,0,1,0,-7.46,-2.19l-11.42,39.16c-41.3,-13.19,-86.47,-4.84,-117.17,25.89L220.66,469c30.72,-30.71,39,-75.78,25.93,-117.08m-211,-54.15L-93.27,426.67a29.94,29.94,0,0,0,0,42.38l14.43,14.43,-44,44a28.11,28.11,0,0,0,0,39.71,28.12,28.12,0,0,0,39.74,0l44,-44L-12.31,550l-44,44a28.1,28.1,0,0,0,0,39.71,28.1,28.1,0,0,0,39.74,0l44,-44,14.49,14.47a30,30,0,0,0,42.39,0l128.86,-128.9Zm180.59,90.92L203.59,376.1a1.71,1.71,0,0,1,0,-2.41l1.59,-1.59a1.71,1.71,0,0,1,2.41,0l12.58,12.58a1.71,1.71,0,0,1,0,2.41l-1.59,1.59A1.7,1.7,0,0,1,216.17,388.68Zm-81.43,-81.43,-12.58,-12.58a1.7,1.7,0,0,1,0,-2.4l1.59,-1.6a1.71,1.71,0,0,1,2.41,0l12.58,12.59a1.68,1.68,0,0,1,0,2.4l-1.59,1.59A1.7,1.7,0,0,1,134.74,307.25Zm-99.16,-9.49L-93.27,426.67a29.94,29.94,0,0,0,0,42.38l14.43,14.43,-44,44a28.11,28.11,0,0,0,0,39.71,28.12,28.12,0,0,0,39.74,0l44,-44L-12.31,550l-44,44a28.1,28.1,0,0,0,0,39.71,28.1,28.1,0,0,0,39.74,0l44,-44,14.49,14.47a30,30,0,0,0,42.39,0l128.86,-128.9Z"
android:fillColor="#6ab343"/>
</group>
</vector>

View File

@ -0,0 +1,12 @@
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M81.158,261.465L145.387,272.79A5.49,5.49 55,0 1,149.84 279.15L149.832,279.199A5.49,5.49 55,0 1,143.472 283.652L79.243,272.327A5.49,5.49 55,0 1,74.789 265.967L74.798,265.918A5.49,5.49 55,0 1,81.158 261.465z"/>
<path android:fillColor="#fff" android:pathData="M214.909,272.293L278.825,261.206A5.461,5.461 125.159,0 1,285.138 265.652L285.147,265.701A5.461,5.461 125.159,0 1,280.7 272.015L216.784,283.102A5.461,5.461 125.159,0 1,210.471 278.655L210.462,278.606A5.461,5.461 125.159,0 1,214.909 272.293z"/>
<path android:fillColor="#fff" android:pathData="M87.58,410.05a9.55,9.55 0,0 1,-9.41 -11.11c7.36,-44.12 57.39,-78.27 103,-78.27s95.63,34.15 103,78.27a9.55,9.55 0,0 1,-9.41 11.11Z"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M377.25,147.51a45.14,45.14 0,0 1,-15.08 -19.2,68.28 68.28,0 0,1 -5.32,-27.53 68.23,68.23 0,0 1,5.32 -27.52,45.14 45.14,0 0,1 15.08,-19.2 40.24,40.24 0,0 1,45.3 0,45.12 45.12,0 0,1 15.07,19.2A68.23,68.23 0,0 1,443 100.78a68.28,68.28 0,0 1,-5.33 27.53,45.12 45.12,0 0,1 -15.07,19.2 40.29,40.29 0,0 1,-45.3 0ZM412.42,131.51a28.4,28.4 0,0 0,8.1 -12.6,55.08 55.08,0 0,0 2.78,-18.08q0,-15.59 -6.3,-25.42t-17.1,-9.83q-10.65,0 -16.95,9.9t-6.3,25.35a55.09,55.09 0,0 0,2.77 18.08,28.4 28.4,0 0,0 8.1,12.6 19.24,19.24 0,0 0,24.9 0Z"/>
</vector>

View File

@ -0,0 +1,14 @@
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M80.313,264.221L145.284,269.905A5.49,5.49 50,0 1,150.275 275.853L150.271,275.903A5.49,5.49 50,0 1,144.323 280.893L79.351,275.209A5.49,5.49 50,0 1,74.361 269.261L74.365,269.212A5.49,5.49 50,0 1,80.313 264.221z"/>
<path android:fillColor="#fff" android:pathData="M217.037,269.907L282.009,264.223A5.49,5.49 130,0 1,287.956 269.213L287.961,269.263A5.49,5.49 130,0 1,282.97 275.211L217.998,280.895A5.49,5.49 130,0 1,212.051 275.904L212.046,275.855A5.49,5.49 130,0 1,217.037 269.907z"/>
<path android:fillColor="#00000000"
android:pathData="M92.91,395.3c19.86,-26.3 54.92,-43.94 87.69,-43.94 33.08,0 68.5,18 88.26,44.69"
android:strokeColor="#fff" android:strokeLineCap="round" android:strokeWidth="20"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M400.67,75.13 L385.22,86.38l-9.9,-15.15 30.15,-21.75h14.85v102.6H400.67Z"/>
</vector>

View File

@ -0,0 +1,14 @@
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M79.71,267.04L144.93,267.04A5.49,5.49 0,0 1,150.42 272.53L150.42,272.58A5.49,5.49 0,0 1,144.93 278.07L79.71,278.07A5.49,5.49 0,0 1,74.22 272.58L74.22,272.53A5.49,5.49 0,0 1,79.71 267.04z"/>
<path android:fillColor="#fff" android:pathData="M217.39,267.04L282.61,267.04A5.49,5.49 0,0 1,288.1 272.53L288.1,272.58A5.49,5.49 0,0 1,282.61 278.07L217.39,278.07A5.49,5.49 0,0 1,211.9 272.58L211.9,272.53A5.49,5.49 0,0 1,217.39 267.04z"/>
<path android:fillColor="#00000000"
android:pathData="M268.86,383.11L92.91,382.36"
android:strokeColor="#fff" android:strokeLineCap="round" android:strokeWidth="20"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M366.07,134.23l19.13,-19.12q14.18,-14.17 18.22,-18.53a48.84,48.84 0,0 0,7.43 -9.75,18.89 18.89,0 0,0 2,-8.85 11.59,11.59 0,0 0,-3.9 -8.85q-3.9,-3.6 -10.35,-3.6a14.44,14.44 0,0 0,-10.2 3.6,19.9 19.9,0 0,0 -5.55,9l-17.7,-7.35a31.77,31.77 0,0 1,6.08 -11.32,33.4 33.4,0 0,1 11.47,-8.93 36.54,36.54 0,0 1,16.2 -3.45,38.19 38.19,0 0,1 17.85,4 29.55,29.55 0,0 1,11.85 10.8,28.48 28.48,0 0,1 4.2,15.22q0,16.65 -16.2,32.55 -6.6,6.47 -24.45,24.3l0.45,0.9h41.55v17.25h-68.1Z"/>
</vector>

View File

@ -0,0 +1,14 @@
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M377,147.58q-10,-6.89 -13.73,-20.1l18.6,-7.35q1.8,7.37 6.45,11.33a17.37,17.37 0,0 0,11.7 4,17 17,0 0,0 11.18,-3.9 12.88,12.88 0,0 0,-0.3 -20.17q-5,-3.83 -13.58,-3.83h-8.85V89.68h8.1a18.54,18.54 0,0 0,11.18 -3.3c3.05,-2.19 4.57,-5.4 4.57,-9.6a10.72,10.72 0,0 0,-3.75 -8.4,14 14,0 0,0 -9.6,-3.3 14.49,14.49 0,0 0,-9.9 3.3,17.57 17.57,0 0,0 -5.25,8.25l-17.85,-7.35a33.92,33.92 0,0 1,11.63 -15.6q8.47,-6.59 21.52,-6.6a39.56,39.56 0,0 1,17.18 3.6A28.64,28.64 0,0 1,428 60.51a24.51,24.51 0,0 1,4.2 14,25.17 25.17,0 0,1 -3.82,14.1 23.8,23.8 0,0 1,-9.23 8.55v1.2a27.18,27.18 0,0 1,12.15 9.45,26 26,0 0,1 4.65,15.6 28.87,28.87 0,0 1,-4.5 15.9,30.82 30.82,0 0,1 -12.67,11.1 41.9,41.9 0,0 1,-18.83 4A39.46,39.46 0,0 1,377 147.58Z"/>
<path android:fillColor="#fff" android:pathData="M79.71,267.04L144.93,267.04A5.49,5.49 0,0 1,150.42 272.53L150.42,272.58A5.49,5.49 0,0 1,144.93 278.07L79.71,278.07A5.49,5.49 0,0 1,74.22 272.58L74.22,272.53A5.49,5.49 0,0 1,79.71 267.04z"/>
<path android:fillColor="#fff" android:pathData="M217.39,267.04L282.61,267.04A5.49,5.49 0,0 1,288.1 272.53L288.1,272.58A5.49,5.49 0,0 1,282.61 278.07L217.39,278.07A5.49,5.49 0,0 1,211.9 272.58L211.9,272.53A5.49,5.49 0,0 1,217.39 267.04z"/>
<path android:fillColor="#00000000"
android:pathData="M268.86,377.57c-19.87,8.69 -54.93,14.52 -87.7,14.52 -33.08,0 -68.49,-5.94 -88.25,-14.77"
android:strokeColor="#fff" android:strokeLineCap="round" android:strokeWidth="20"/>
</vector>

View File

@ -0,0 +1,14 @@
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M79.351,269.907L144.323,264.222A5.49,5.49 130,0 1,150.27 269.213L150.275,269.263A5.49,5.49 130,0 1,145.284 275.21L80.312,280.895A5.49,5.49 130,0 1,74.364 275.904L74.36,275.854A5.49,5.49 130,0 1,79.351 269.907z"/>
<path android:fillColor="#fff" android:pathData="M218.066,265.855L283.286,271.725A5.512,5.512 50.143,0 1,288.282 277.709L288.278,277.759A5.512,5.512 50.143,0 1,282.294 282.755L217.074,276.885A5.512,5.512 50.143,0 1,212.078 270.901L212.082,270.851A5.512,5.512 50.143,0 1,218.066 265.855z"/>
<path android:fillColor="#00000000"
android:pathData="M268.86,363.11c-19.87,26.3 -54.93,43.94 -87.7,43.94 -33.08,0 -68.49,-18 -88.25,-44.69"
android:strokeColor="#fff" android:strokeLineCap="round" android:strokeWidth="20"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M402.17,133.18L354.92,133.18v-15.9l45.6,-67.8h21.3v65.4h12.75v18.3L421.82,133.18v18.9L402.17,152.08ZM402.17,114.88L402.17,78.73L401,78.73l-24.3,36.15Z"/>
</vector>

View File

@ -0,0 +1,12 @@
<vector android:height="24dp" android:viewportHeight="500"
android:viewportWidth="500" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#6ab343" android:pathData="M181.16,316.18m-181.7,0a181.7,181.7 0,1 1,363.4 0a181.7,181.7 0,1 1,-363.4 0"/>
<path android:fillColor="#fff" android:pathData="M79.25,272.792L143.479,261.466A5.49,5.49 125,0 1,149.839 265.919L149.848,265.969A5.49,5.49 125,0 1,145.395 272.329L81.165,283.654A5.49,5.49 125,0 1,74.805 279.201L74.797,279.151A5.49,5.49 125,0 1,79.25 272.792z"/>
<path android:fillColor="#fff" android:pathData="M218.846,261.462L283.076,272.788A5.49,5.49 55,0 1,287.529 279.148L287.52,279.197A5.49,5.49 55,0 1,281.16 283.65L216.931,272.325A5.49,5.49 55,0 1,212.478 265.965L212.486,265.916A5.49,5.49 55,0 1,218.846 261.462z"/>
<path android:fillColor="#fff" android:pathData="M274.74,337.67a9.55,9.55 0,0 1,9.41 11.11c-7.36,44.12 -57.39,78.27 -103,78.27s-95.63,-34.15 -103,-78.27a9.55,9.55 0,0 1,9.41 -11.11Z"/>
<path android:fillColor="#eee" android:pathData="M398.31,101.69m-101.69,0a101.69,101.69 0,1 1,203.38 0a101.69,101.69 0,1 1,-203.38 0"/>
<path android:fillColor="#eee" android:pathData="M267.86,57.09m-24.76,0a24.76,24.76 0,1 1,49.52 0a24.76,24.76 0,1 1,-49.52 0"/>
<path android:fillColor="#eee" android:pathData="M224.93,85.36m-16.33,0a16.33,16.33 0,1 1,32.66 0a16.33,16.33 0,1 1,-32.66 0"/>
<path android:fillColor="#eee" android:pathData="M217.77,120.19m-9.17,0a9.17,9.17 0,1 1,18.34 0a9.17,9.17 0,1 1,-18.34 0"/>
<path android:fillColor="#6ab343" android:pathData="M383.77,151.41a35.84,35.84 0,0 1,-13.12 -9.3,33.67 33.67,0 0,1 -7.73,-15.38l17.7,-6.9a23.14,23.14 0,0 0,6.6 12.08,17 17,0 0,0 12,4.42 16.24,16.24 0,0 0,12 -4.8,16.43 16.43,0 0,0 4.8,-12.15 16.66,16.66 0,0 0,-4.72 -12.07,16.1 16.1,0 0,0 -12.08,-4.88 17.71,17.71 0,0 0,-8.17 1.88,19.05 19.05,0 0,0 -6.23,5l-19.05,-8.55 5.85,-51.3h57.9V66.73H388l-3.75,24 1.2,0.3a27.32,27.32 0,0 1,17.85 -6.15,31.18 31.18,0 0,1 16,4.35 32.65,32.65 0,0 1,11.92 12.23,35.49 35.49,0 0,1 4.5,17.92A35.22,35.22 0,0 1,431 137.53,32.81 32.81,0 0,1 418.12,150a38.51,38.51 0,0 1,-18.75 4.5A40.73,40.73 0,0 1,383.77 151.41Z"/>
</vector>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<font-family xmlns:app="http://schemas.android.com/apk/res-auto"
app:fontProviderAuthority="com.google.android.gms.fonts"
app:fontProviderPackage="com.google.android.gms"
app:fontProviderQuery="Roboto"
app:fontProviderCerts="@array/com_google_android_gms_fonts_certs">
</font-family>

View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<!-- The merge tag can be used to eliminate redundant layouts when
including layouts, and it's a good idea to use it.
See: https://developer.android.com/training/improving-layouts/reusing-layouts -->
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/navigation" />
</merge>

View File

@ -0,0 +1,130 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<!-- Wrapping the layout into /layout to make it available with data binding. -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<!-- Data to make available to the XML via data binding. In this case,
the whole ViewModel, so that we can access the LiveData,
click handlers, and state variables. -->
<data>
<variable
name="sleepQualityViewModel"
type="com.example.android.trackmysleepquality.sleepquality.SleepQualityViewModel" />
</data>
<!-- Start of the visible fragment layout using ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".sleepquality.SleepQualityFragment">
<TextView
android:id="@+id/title_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin"
android:layout_marginTop="@dimen/margin"
android:layout_marginBottom="@dimen/triple_margin"
android:fontFamily="@font/roboto"
android:text="@string/how_was_hour_sleep"
android:textSize="@dimen/title_text_size"
app:layout_constraintBottom_toTopOf="@+id/quality_zero_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/quality_zero_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginStart="@dimen/margin"
android:layout_marginTop="@dimen/margin"
android:contentDescription="@string/quality_0"
android:onClick="@{() -> sleepQualityViewModel.onSetSleepQuality(0)}"
app:layout_constraintEnd_toStartOf="@+id/quality_one_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_text"
app:srcCompat="@drawable/ic_sleep_0" />
<ImageView
android:id="@+id/quality_one_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:contentDescription="@string/quality_1"
android:onClick="@{() -> sleepQualityViewModel.onSetSleepQuality(1)}"
app:layout_constraintBottom_toBottomOf="@+id/quality_zero_image"
app:layout_constraintEnd_toStartOf="@+id/quality_two_image"
app:layout_constraintStart_toEndOf="@+id/quality_zero_image"
app:srcCompat="@drawable/ic_sleep_1" />
<ImageView
android:id="@+id/quality_two_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginEnd="@dimen/margin"
android:contentDescription="@string/quality_2"
android:onClick="@{() -> sleepQualityViewModel.onSetSleepQuality(2)}"
app:layout_constraintBottom_toBottomOf="@+id/quality_one_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_one_image"
app:srcCompat="@drawable/ic_sleep_2" />
<ImageView
android:id="@+id/quality_three_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginStart="@dimen/margin"
android:layout_marginTop="@dimen/margin"
android:layout_marginBottom="@dimen/margin"
android:contentDescription="@string/quality_3"
android:onClick="@{() -> sleepQualityViewModel.onSetSleepQuality(3)}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/quality_four_image"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/quality_zero_image"
app:layout_constraintVertical_bias="0.0"
app:srcCompat="@drawable/ic_sleep_3" />
<ImageView
android:id="@+id/quality_four_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:contentDescription="@string/quality_4"
android:onClick="@{() -> sleepQualityViewModel.onSetSleepQuality(4)}"
app:layout_constraintBottom_toBottomOf="@+id/quality_three_image"
app:layout_constraintEnd_toStartOf="@+id/quality_five_image"
app:layout_constraintStart_toEndOf="@+id/quality_three_image"
app:layout_constraintTop_toTopOf="@+id/quality_three_image"
app:srcCompat="@drawable/ic_sleep_4" />
<ImageView
android:id="@+id/quality_five_image"
android:layout_width="@dimen/icon_size"
android:layout_height="@dimen/icon_size"
android:layout_marginEnd="@dimen/margin"
android:contentDescription="@string/quality_5"
android:onClick="@{() -> sleepQualityViewModel.onSetSleepQuality(5)}"
app:layout_constraintBottom_toBottomOf="@+id/quality_four_image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/quality_four_image"
app:layout_constraintTop_toTopOf="@+id/quality_four_image"
app:srcCompat="@drawable/ic_sleep_5" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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.
-->
<!-- Wrapping the layout into /layout to make it available with data binding. -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<!-- Data to make available to the XML via data binding. In this case,
the whole ViewModel, so that we can access the LiveData,
click handlers, and state variables. -->
<data>
<variable
name="sleepTrackerViewModel"
type="com.example.android.trackmysleepquality.sleeptracker.SleepTrackerViewModel" />
</data>
<!-- Start of the visible fragment layout using ConstraintLayout -->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".sleeptracker.SleepTrackerFragment">
<!-- Simplest way of displaying scrollable text and data. There is a
better and more efficient way to do this, and you will learn about
RecyclerView in a later lesson. -->
<ScrollView
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/clear_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stop_button">
<!-- In the TextView, we can access the nightsString LiveData,
which keeps it displayed and updated in the TextView
whenever it changes. -->
<TextView
android:id="@+id/textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin"
android:layout_marginTop="@dimen/margin"
android:layout_marginEnd="@dimen/margin"
android:text="@{sleepTrackerViewModel.nightsString}" />
</ScrollView>
<!-- With data binding and LiveData, we can track the buttons' visibility states
from the ViewModel. The click handler is in the ViewModel as well, and
you can set it for the Views using this lambda pattern. -->
<Button
android:id="@+id/start_button"
style="@style/SleepButtons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin"
android:enabled="@{sleepTrackerViewModel.startButtonVisible}"
android:onClick="@{() -> sleepTrackerViewModel.onStartTracking()}"
android:text="@string/start"
app:layout_constraintBaseline_toBaselineOf="@id/stop_button"
app:layout_constraintEnd_toStartOf="@+id/stop_button"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="@+id/stop_button"
style="@style/SleepButtons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin"
android:layout_marginEnd="@dimen/margin"
android:enabled="@{sleepTrackerViewModel.stopButtonVisible}"
android:onClick="@{() -> sleepTrackerViewModel.onStopTracking()}"
android:text="@string/stop"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/start_button"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/clear_button"
style="@style/SleepButtons"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/margin"
android:layout_marginEnd="@dimen/margin"
android:layout_marginBottom="@dimen/margin"
android:enabled="@{sleepTrackerViewModel.clearButtonVisible}"
android:onClick="@{() -> sleepTrackerViewModel.onClear()}"
android:text="@string/clear"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Some files were not shown because too many files have changed in this diff Show More