diff --git a/.github/ci-gradle.properties b/.github/ci-gradle.properties
index 2f601e2d50..5dd327d5c8 100644
--- a/.github/ci-gradle.properties
+++ b/.github/ci-gradle.properties
@@ -1,23 +1,23 @@
-/*
- * Copyright 2019 Google, Inc.
- *
- * 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.
- */
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
 
- org.gradle.daemon=false
- org.gradle.parallel=true
- org.gradle.jvmargs=-Xmx5120m
- org.gradle.workers.max=2
+org.gradle.daemon=false
+org.gradle.parallel=true
+org.gradle.jvmargs=-Xmx5120m
+org.gradle.workers.max=2
 
- kotlin.incremental=false
- kotlin.compiler.execution.strategy=in-process
+kotlin.incremental=false
+kotlin.compiler.execution.strategy=in-process
diff --git a/Crane/README.md b/Crane/README.md
index 81a7af7dc1..8525d6afc8 100644
--- a/Crane/README.md
+++ b/Crane/README.md
@@ -32,6 +32,38 @@ implemented using a different Activity will be displayed. In there, you can see
 embedded in Compose and Compose buttons updating the Android View. Notice how you can also
 interact with the `MapView` seamlessly.
 
+## Hilt
+
+Crane uses [Hilt][hilt] to manage its dependencies. The Hilt's ViewModel extension (with the
+`@ViewModelInject` annotation) works perfectly with Compose's ViewModel integration (`viewModel()`
+composable function) as you can see in the following snippet of code. `viewModel()` will
+automatically use the factory that Hilt creates for the ViewModel:
+
+```
+class MainViewModel @ViewModelInject constructor(
+    private val destinationsRepository: DestinationsRepository,
+    @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
+    datesRepository: DatesRepository
+) : ViewModel() { ... }
+
+@Composable
+fun CraneHomeContent(...) {
+    val viewModel: MainViewModel = viewModel()
+    ...
+}
+```
+
+Disclaimer: Passing dependencies to a ViewModel which are not available at compile time (which is
+sometimes called _assisted injection_) doesn't work as you might expect using `viewModel()`.
+Compose's ViewModel integration cannot currently scope a ViewModel to a given composable. Instead
+it is always scoped to the host Activity or Fragment. This means that calling `viewModel()` with
+different factories in the same host Activity/Fragment don't have the desired effect; the _first_
+factory will be always used.
+
+This is the case of the [DetailsViewModel](detailsViewModel), which takes the name of
+a `City` as a parameter to load the required information for the screen. However, the above isn't a
+problem in this sample, since `DetailsScreen` is always used in it's own newly launched Activity.
+
 ## Google Maps SDK
 
 To get the MapView working, you need to get an API key as
@@ -79,6 +111,8 @@ limitations under the License.
 [details]: app/src/main/java/androidx/compose/samples/crane/details/DetailsActivity.kt
 [data]: app/src/main/java/androidx/compose/samples/crane/data/CraneData.kt
 [mainViewModel]: app/src/main/java/androidx/compose/samples/crane/home/MainViewModel.kt
+[detailsViewModel]: app/src/main/java/androidx/compose/samples/crane/details/DetailsViewModel.kt
 [homeTest]: app/src/androidTest/java/androidx/compose/samples/crane/home/HomeTest.kt
 [detailsTest]: app/src/androidTest/java/androidx/compose/samples/crane/details/DetailsActivityTest.kt
 [coil-accompanist]: https://github.com/chrisbanes/accompanist
+[hilt]: https://d.android.com/hilt
diff --git a/Crane/app/build.gradle b/Crane/app/build.gradle
index a2d1d99f64..31f398c4c2 100644
--- a/Crane/app/build.gradle
+++ b/Crane/app/build.gradle
@@ -19,6 +19,8 @@ import com.example.crane.buildsrc.Libs
 plugins {
     id 'com.android.application'
     id 'kotlin-android'
+    id 'kotlin-kapt'
+    id 'dagger.hilt.android.plugin'
 }
 
 // Reads the Google maps key that is used in the AndroidManifest
@@ -36,7 +38,13 @@ android {
         versionCode 1
         versionName "1.0"
         vectorDrawables.useSupportLibrary = true
-        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        testInstrumentationRunner "androidx.compose.samples.crane.CustomTestRunner"
+
+        javaCompileOptions {
+            annotationProcessorOptions {
+                arguments["dagger.hilt.disableModulesHaveInstallInCheck"] = "true"
+            }
+        }
 
         manifestPlaceholders = [ googleMapsKey : properties.getProperty("google.maps.key", "") ]
     }
@@ -81,14 +89,22 @@ android {
         resValues false
         shaders false
     }
+
     composeOptions {
         kotlinCompilerVersion Libs.Kotlin.version
         kotlinCompilerExtensionVersion Libs.AndroidX.Compose.version
     }
+
+    packagingOptions {
+        exclude "META-INF/licenses/**"
+        exclude "META-INF/AL2.0"
+        exclude "META-INF/LGPL2.1"
+    }
 }
 
 dependencies {
     implementation Libs.Kotlin.stdlib
+    implementation Libs.Kotlin.Coroutines.android
     implementation Libs.googleMaps
 
     implementation Libs.AndroidX.Compose.runtime
@@ -98,12 +114,27 @@ dependencies {
     implementation Libs.AndroidX.Compose.layout
     implementation Libs.AndroidX.Compose.animation
     implementation Libs.AndroidX.UI.tooling
-
     implementation Libs.Accompanist.coil
 
+    implementation Libs.AndroidX.Lifecycle.viewModelKtx
+    implementation Libs.Hilt.android
+    implementation Libs.Hilt.AndroidX.viewModel
+    compileOnly Libs.AssistedInjection.dagger
+    kapt Libs.Hilt.compiler
+    kapt Libs.Hilt.AndroidX.compiler
+    kapt Libs.AssistedInjection.processor
+
+    androidTestImplementation Libs.JUnit.junit
     androidTestImplementation Libs.AndroidX.Test.runner
     androidTestImplementation Libs.AndroidX.Test.espressoCore
     androidTestImplementation Libs.AndroidX.Test.rules
     androidTestImplementation Libs.AndroidX.Test.Ext.junit
+    androidTestImplementation Libs.Kotlin.Coroutines.test
     androidTestImplementation Libs.AndroidX.Compose.uiTest
+    androidTestImplementation Libs.Hilt.android
+    androidTestImplementation Libs.Hilt.AndroidX.viewModel
+    androidTestImplementation Libs.Hilt.testing
+    kaptAndroidTest Libs.Hilt.compiler
+    kaptAndroidTest Libs.Hilt.AndroidX.compiler
+    kaptAndroidTest Libs.AssistedInjection.processor
 }
diff --git a/Crane/app/src/androidTest/java/androidx/compose/samples/crane/CustomTestRunner.kt b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/CustomTestRunner.kt
new file mode 100644
index 0000000000..d81394f073
--- /dev/null
+++ b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/CustomTestRunner.kt
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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 androidx.compose.samples.crane
+
+import android.app.Application
+import android.content.Context
+import androidx.test.runner.AndroidJUnitRunner
+import dagger.hilt.android.testing.HiltTestApplication
+
+class CustomTestRunner : AndroidJUnitRunner() {
+    override fun newApplication(cl: ClassLoader?, name: String?, context: Context?): Application {
+        return super.newApplication(cl, HiltTestApplication::class.java.name, context)
+    }
+}
diff --git a/Crane/app/src/androidTest/java/androidx/compose/samples/crane/calendar/CalendarTest.kt b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/calendar/CalendarTest.kt
index e45d6cb306..27105bac9c 100644
--- a/Crane/app/src/androidTest/java/androidx/compose/samples/crane/calendar/CalendarTest.kt
+++ b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/calendar/CalendarTest.kt
@@ -17,66 +17,61 @@
 package androidx.compose.samples.crane.calendar
 
 import androidx.compose.material.Surface
-import androidx.compose.samples.crane.base.ServiceLocator
-import androidx.compose.samples.crane.calendar.model.CalendarDay
-import androidx.compose.samples.crane.calendar.model.CalendarMonth
-import androidx.compose.samples.crane.calendar.model.DaySelected
 import androidx.compose.samples.crane.calendar.model.DaySelectedStatus
 import androidx.compose.samples.crane.calendar.model.DaySelectedStatus.FirstDay
 import androidx.compose.samples.crane.calendar.model.DaySelectedStatus.FirstLastDay
 import androidx.compose.samples.crane.calendar.model.DaySelectedStatus.LastDay
 import androidx.compose.samples.crane.calendar.model.DaySelectedStatus.NoSelected
 import androidx.compose.samples.crane.calendar.model.DaySelectedStatus.Selected
+import androidx.compose.samples.crane.data.DatesRepository
+import androidx.compose.samples.crane.di.DispatchersModule
 import androidx.compose.samples.crane.ui.CraneTheme
 import androidx.ui.test.ComposeTestRule
 import androidx.ui.test.SemanticsMatcher
 import androidx.ui.test.assertLabelEquals
-import androidx.ui.test.createComposeRule
+import androidx.ui.test.createAndroidComposeRule
 import androidx.ui.test.onNodeWithLabel
 import androidx.ui.test.performClick
 import androidx.ui.test.performScrollTo
-import org.junit.After
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.UninstallModules
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
+import javax.inject.Inject
 
+@UninstallModules(DispatchersModule::class)
+@HiltAndroidTest
 class CalendarTest {
 
-    @get:Rule
-    val composeTestRule = createComposeRule(disableTransitions = true)
-
-    var dateSelected = ""
-    private val onDayClicked: (CalendarDay, CalendarMonth) -> Unit = { day, month ->
-        dateSelected = "${month.name} ${day.value}"
-        ServiceLocator.datesSelected.daySelected(
-            DaySelected(
-                day = day.value.toInt(),
-                month = month
-            )
-        )
-    }
+    @get:Rule(order = 0)
+    var hiltRule = HiltAndroidRule(this)
+
+    @get:Rule(order = 1)
+    val composeTestRule = createAndroidComposeRule<CalendarActivity>()
+
+    @Inject
+    lateinit var datesRepository: DatesRepository
 
     @Before
     fun setUp() {
+        hiltRule.inject()
+
         composeTestRule.setContent {
             CraneTheme {
                 Surface {
-                    Calendar(onDayClicked)
+                    CalendarScreen(onBackPressed = {})
                 }
             }
         }
     }
 
-    @After
-    fun tearDown() {
-        ServiceLocator.datesSelected.clearDates()
-    }
-
     @Test
     fun scrollsToTheBottom() {
         composeTestRule.onNodeWithLabel("January 1").assertExists()
         composeTestRule.onNodeWithLabel("December 31").performScrollTo().performClick()
-        assert(dateSelected == "December 31")
+        assert(datesRepository.datesSelected.toString() == "Dec 31")
     }
 
     @Test
diff --git a/Crane/app/src/androidTest/java/androidx/compose/samples/crane/details/DetailsActivityTest.kt b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/details/DetailsActivityTest.kt
index dc3de7710c..d61dc5db38 100644
--- a/Crane/app/src/androidTest/java/androidx/compose/samples/crane/details/DetailsActivityTest.kt
+++ b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/details/DetailsActivityTest.kt
@@ -17,8 +17,10 @@
 package androidx.compose.samples.crane.details
 
 import androidx.compose.samples.crane.R
+import androidx.compose.samples.crane.data.DestinationsRepository
 import androidx.compose.samples.crane.data.ExploreModel
 import androidx.compose.samples.crane.data.MADRID
+import androidx.compose.samples.crane.di.DispatchersModule
 import androidx.test.espresso.Espresso.onView
 import androidx.test.espresso.assertion.ViewAssertions.matches
 import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
@@ -31,16 +33,30 @@ import androidx.ui.test.onNodeWithText
 import com.google.android.libraries.maps.MapView
 import com.google.android.libraries.maps.model.CameraPosition
 import com.google.android.libraries.maps.model.LatLng
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.UninstallModules
+import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 import java.util.concurrent.CountDownLatch
+import javax.inject.Inject
 
+@UninstallModules(DispatchersModule::class)
+@HiltAndroidTest
 class DetailsActivityTest {
 
-    private val expectedDescription = "description"
-    private val testExploreModel = ExploreModel(MADRID, expectedDescription, "imageUrl")
+    @Inject
+    lateinit var destinationsRepository: DestinationsRepository
+    lateinit var cityDetails: ExploreModel
 
-    @get:Rule
+    private val city = MADRID
+    private val testExploreModel = ExploreModel(city, "description", "imageUrl")
+
+    @get:Rule(order = 0)
+    var hiltRule = HiltAndroidRule(this)
+
+    @get:Rule(order = 1)
     val composeTestRule = AndroidComposeTestRule(
         ActivityScenarioRule<DetailsActivity>(
             createDetailsActivityIntent(
@@ -50,10 +66,16 @@ class DetailsActivityTest {
         )
     )
 
+    @Before
+    fun setUp() {
+        hiltRule.inject()
+        cityDetails = destinationsRepository.getDestination(MADRID.name)!!
+    }
+
     @Test
     fun mapView_cameraPositioned() {
-        composeTestRule.onNodeWithText(MADRID.nameToDisplay).assertIsDisplayed()
-        composeTestRule.onNodeWithText(expectedDescription).assertIsDisplayed()
+        composeTestRule.onNodeWithText(cityDetails.city.nameToDisplay).assertIsDisplayed()
+        composeTestRule.onNodeWithText(cityDetails.description).assertIsDisplayed()
         onView(withId(R.id.map)).check(matches(isDisplayed()))
 
         var cameraPosition: CameraPosition? = null
diff --git a/Crane/app/src/androidTest/java/androidx/compose/samples/crane/di/TestDispatchersModule.kt b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/di/TestDispatchersModule.kt
new file mode 100644
index 0000000000..69ab93f999
--- /dev/null
+++ b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/di/TestDispatchersModule.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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.
+ */
+
+@file:Suppress("DEPRECATION")
+
+package androidx.compose.samples.crane.di
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ApplicationComponent
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@Module
+@InstallIn(ApplicationComponent::class)
+class TestDispatchersModule {
+
+    @Provides
+    @DefaultDispatcher
+    fun provideDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Unconfined
+}
diff --git a/Crane/app/src/androidTest/java/androidx/compose/samples/crane/home/HomeTest.kt b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/home/HomeTest.kt
index e5ceec480f..97095b1e06 100644
--- a/Crane/app/src/androidTest/java/androidx/compose/samples/crane/home/HomeTest.kt
+++ b/Crane/app/src/androidTest/java/androidx/compose/samples/crane/home/HomeTest.kt
@@ -17,18 +17,27 @@
 package androidx.compose.samples.crane.home
 
 import androidx.compose.material.Surface
+import androidx.compose.samples.crane.di.DispatchersModule
 import androidx.compose.samples.crane.ui.CraneTheme
-import androidx.ui.test.createComposeRule
+import androidx.ui.test.createAndroidComposeRule
 import androidx.ui.test.onNodeWithText
 import androidx.ui.test.performClick
+import dagger.hilt.android.testing.HiltAndroidRule
+import dagger.hilt.android.testing.HiltAndroidTest
+import dagger.hilt.android.testing.UninstallModules
 import org.junit.Before
 import org.junit.Rule
 import org.junit.Test
 
+@UninstallModules(DispatchersModule::class)
+@HiltAndroidTest
 class HomeTest {
 
-    @get:Rule
-    val composeTestRule = createComposeRule(disableTransitions = true)
+    @get:Rule(order = 0)
+    var hiltRule = HiltAndroidRule(this)
+
+    @get:Rule(order = 1)
+    val composeTestRule = createAndroidComposeRule<MainActivity>()
 
     @Before
     fun setUp() {
diff --git a/Crane/app/src/main/AndroidManifest.xml b/Crane/app/src/main/AndroidManifest.xml
index 141d1c4998..300a8a0503 100644
--- a/Crane/app/src/main/AndroidManifest.xml
+++ b/Crane/app/src/main/AndroidManifest.xml
@@ -21,6 +21,7 @@
     <uses-permission android:name="android.permission.INTERNET" />
 
     <application
+        android:name=".CraneApplication"
         android:allowBackup="true"
         android:icon="@mipmap/ic_launcher"
         android:label="@string/app_name"
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/base/ServiceLocator.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/CraneApplication.kt
similarity index 76%
rename from Crane/app/src/main/java/androidx/compose/samples/crane/base/ServiceLocator.kt
rename to Crane/app/src/main/java/androidx/compose/samples/crane/CraneApplication.kt
index 2dfcf8a247..8cb75e6a5e 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/base/ServiceLocator.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/CraneApplication.kt
@@ -14,10 +14,10 @@
  * limitations under the License.
  */
 
-package androidx.compose.samples.crane.base
+package androidx.compose.samples.crane
 
-import androidx.compose.samples.crane.calendar.model.DatesSelectedState
+import android.app.Application
+import dagger.hilt.android.HiltAndroidApp
 
-object ServiceLocator {
-    val datesSelected = DatesSelectedState()
-}
+@HiltAndroidApp
+class CraneApplication : Application()
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/base/BaseUserInput.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/base/BaseUserInput.kt
index f6e39f8555..3e38c665f6 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/base/BaseUserInput.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/base/BaseUserInput.kt
@@ -20,13 +20,13 @@ import androidx.annotation.DrawableRes
 import androidx.compose.foundation.AmbientContentColor
 import androidx.compose.foundation.BaseTextField
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.preferredWidth
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Surface
 import androidx.compose.runtime.Composable
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/base/Result.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/base/Result.kt
new file mode 100644
index 0000000000..5dad2368e7
--- /dev/null
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/base/Result.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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 androidx.compose.samples.crane.base
+
+/**
+ * A generic class that holds a value.
+ * @param <T>
+ */
+sealed class Result<out R> {
+    data class Success<out T>(val data: T) : Result<T>()
+    data class Error(val exception: Exception) : Result<Nothing>()
+}
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/Calendar.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/Calendar.kt
index b785cbe137..af3590d99c 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/Calendar.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/Calendar.kt
@@ -36,11 +36,12 @@ import androidx.compose.material.Colors
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Surface
 import androidx.compose.runtime.Composable
-import androidx.compose.samples.crane.calendar.data.year2020
 import androidx.compose.samples.crane.calendar.model.CalendarDay
 import androidx.compose.samples.crane.calendar.model.CalendarMonth
 import androidx.compose.samples.crane.calendar.model.DayOfWeek
 import androidx.compose.samples.crane.calendar.model.DaySelectedStatus
+import androidx.compose.samples.crane.data.CalendarYear
+import androidx.compose.samples.crane.data.DatesLocalDataSource
 import androidx.compose.samples.crane.ui.CraneTheme
 import androidx.compose.samples.crane.util.Circle
 import androidx.compose.samples.crane.util.SemiRect
@@ -58,12 +59,13 @@ typealias CalendarWeek = List<CalendarDay>
 
 @Composable
 fun Calendar(
+    calendarYear: CalendarYear,
     onDayClicked: (CalendarDay, CalendarMonth) -> Unit,
     modifier: Modifier = Modifier
 ) {
     ScrollableColumn(modifier = modifier) {
         Spacer(Modifier.preferredHeight(32.dp))
-        for (month in year2020) {
+        for (month in calendarYear) {
             Month(month = month, onDayClicked = onDayClicked)
             Spacer(Modifier.preferredHeight(32.dp))
         }
@@ -280,6 +282,6 @@ var SemanticsPropertyReceiver.dayStatusProperty by DayStatusKey
 @Composable
 fun DayPreview() {
     CraneTheme {
-        Calendar(onDayClicked = { _, _ -> })
+        Calendar(DatesLocalDataSource().year2020, onDayClicked = { _, _ -> })
     }
 }
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/CalendarActivity.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/CalendarActivity.kt
index bdfb166bb7..1ac1d93dde 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/CalendarActivity.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/CalendarActivity.kt
@@ -30,18 +30,21 @@ import androidx.compose.material.TopAppBar
 import androidx.compose.runtime.Composable
 import androidx.compose.samples.crane.R
 import androidx.compose.samples.crane.base.CraneScaffold
-import androidx.compose.samples.crane.base.ServiceLocator
 import androidx.compose.samples.crane.calendar.model.CalendarDay
 import androidx.compose.samples.crane.calendar.model.CalendarMonth
 import androidx.compose.samples.crane.calendar.model.DaySelected
+import androidx.compose.samples.crane.data.CalendarYear
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.res.vectorResource
+import androidx.compose.ui.viewinterop.viewModel
+import dagger.hilt.android.AndroidEntryPoint
 
 fun launchCalendarActivity(context: Context) {
     val intent = Intent(context, CalendarActivity::class.java)
     context.startActivity(intent)
 }
 
+@AndroidEntryPoint
 class CalendarActivity : ComponentActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
@@ -57,27 +60,37 @@ class CalendarActivity : ComponentActivity() {
     }
 }
 
-// Extracted out to a separate variable. If this lambda is used as a trailing lambda in the
-// Calendar function, it recomposes the whole Calendar view when clicked on it.
-private val onDayClicked: (CalendarDay, CalendarMonth) -> Unit = { calendarDay, calendarMonth ->
-    ServiceLocator.datesSelected.daySelected(
-        DaySelected(
-            day = calendarDay.value.toInt(),
-            month = calendarMonth
-        )
+@Composable
+fun CalendarScreen(onBackPressed: () -> Unit) {
+    val calendarViewModel: CalendarViewModel = viewModel()
+    val calendarYear = calendarViewModel.calendarYear
+
+    CalendarContent(
+        selectedDates = calendarViewModel.datesSelected.toString(),
+        calendarYear = calendarYear,
+        onDayClicked = { calendarDay, calendarMonth ->
+            calendarViewModel.onDaySelected(
+                DaySelected(calendarDay.value.toInt(), calendarMonth, calendarYear)
+            )
+        },
+        onBackPressed = onBackPressed
     )
 }
 
 @Composable
-fun CalendarScreen(onBackPressed: () -> Unit) {
+private fun CalendarContent(
+    selectedDates: String,
+    calendarYear: CalendarYear,
+    onDayClicked: (CalendarDay, CalendarMonth) -> Unit,
+    onBackPressed: () -> Unit
+) {
     CraneScaffold {
         Column {
-            val selectedDatesText = ServiceLocator.datesSelected.toString()
             TopAppBar(
                 title = {
                     Text(
-                        text = if (selectedDatesText.isEmpty()) "Select Dates"
-                        else selectedDatesText
+                        text = if (selectedDates.isEmpty()) "Select Dates"
+                        else selectedDates
                     )
                 },
                 navigationIcon = {
@@ -88,7 +101,7 @@ fun CalendarScreen(onBackPressed: () -> Unit) {
                 backgroundColor = MaterialTheme.colors.primaryVariant
             )
             Surface {
-                Calendar(onDayClicked = onDayClicked)
+                Calendar(calendarYear, onDayClicked)
             }
         }
     }
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/CalendarViewModel.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/CalendarViewModel.kt
new file mode 100644
index 0000000000..1cee9585b6
--- /dev/null
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/CalendarViewModel.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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 androidx.compose.samples.crane.calendar
+
+import androidx.compose.samples.crane.calendar.model.DaySelected
+import androidx.compose.samples.crane.data.DatesRepository
+import androidx.hilt.lifecycle.ViewModelInject
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.launch
+
+class CalendarViewModel @ViewModelInject constructor(
+    private val datesRepository: DatesRepository
+) : ViewModel() {
+
+    val datesSelected = datesRepository.datesSelected
+    val calendarYear = datesRepository.calendarYear
+
+    fun onDaySelected(daySelected: DaySelected) {
+        viewModelScope.launch {
+            datesRepository.onDaySelected(daySelected)
+        }
+    }
+}
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/data/CalendarData.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/data/CalendarData.kt
deleted file mode 100644
index 3152d07d8b..0000000000
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/data/CalendarData.kt
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     https://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 androidx.compose.samples.crane.calendar.data
-
-import androidx.compose.samples.crane.calendar.model.CalendarMonth
-import androidx.compose.samples.crane.calendar.model.DayOfWeek
-
-val january2020 = CalendarMonth(
-    name = "January",
-    year = "2020",
-    numDays = 31,
-    monthNumber = 1,
-    startDayOfWeek = DayOfWeek.Wednesday
-)
-val february2020 = CalendarMonth(
-    name = "February",
-    year = "2020",
-    numDays = 29,
-    monthNumber = 2,
-    startDayOfWeek = DayOfWeek.Saturday
-)
-val march2020 = CalendarMonth(
-    name = "March",
-    year = "2020",
-    numDays = 31,
-    monthNumber = 3,
-    startDayOfWeek = DayOfWeek.Sunday
-)
-val april2020 = CalendarMonth(
-    name = "April",
-    year = "2020",
-    numDays = 30,
-    monthNumber = 4,
-    startDayOfWeek = DayOfWeek.Wednesday
-)
-val may2020 = CalendarMonth(
-    name = "May",
-    year = "2020",
-    numDays = 31,
-    monthNumber = 5,
-    startDayOfWeek = DayOfWeek.Friday
-)
-val june2020 = CalendarMonth(
-    name = "June",
-    year = "2020",
-    numDays = 30,
-    monthNumber = 6,
-    startDayOfWeek = DayOfWeek.Monday
-)
-val july2020 = CalendarMonth(
-    name = "July",
-    year = "2020",
-    numDays = 31,
-    monthNumber = 7,
-    startDayOfWeek = DayOfWeek.Wednesday
-)
-val august2020 = CalendarMonth(
-    name = "August",
-    year = "2020",
-    numDays = 31,
-    monthNumber = 8,
-    startDayOfWeek = DayOfWeek.Saturday
-)
-val september2020 = CalendarMonth(
-    name = "September",
-    year = "2020",
-    numDays = 30,
-    monthNumber = 9,
-    startDayOfWeek = DayOfWeek.Tuesday
-)
-val october2020 = CalendarMonth(
-    name = "October",
-    year = "2020",
-    numDays = 31,
-    monthNumber = 10,
-    startDayOfWeek = DayOfWeek.Thursday
-)
-val november2020 = CalendarMonth(
-    name = "November",
-    year = "2020",
-    numDays = 30,
-    monthNumber = 11,
-    startDayOfWeek = DayOfWeek.Sunday
-)
-val december2020 = CalendarMonth(
-    name = "December",
-    year = "2020",
-    numDays = 31,
-    monthNumber = 12,
-    startDayOfWeek = DayOfWeek.Tuesday
-)
-
-val year2020 = listOf(
-    january2020,
-    february2020,
-    march2020,
-    april2020,
-    may2020,
-    june2020,
-    july2020,
-    august2020,
-    september2020,
-    october2020,
-    november2020,
-    december2020
-)
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/model/DatesSelectedState.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/model/DatesSelectedState.kt
index b27e45e3be..8d4cfc64dd 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/model/DatesSelectedState.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/model/DatesSelectedState.kt
@@ -20,9 +20,9 @@ import androidx.annotation.VisibleForTesting
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.setValue
-import androidx.compose.samples.crane.calendar.data.year2020
+import androidx.compose.samples.crane.data.CalendarYear
 
-class DatesSelectedState {
+class DatesSelectedState(private val year: CalendarYear) {
     private var from by mutableStateOf(DaySelectedEmpty)
     private var to by mutableStateOf(DaySelectedEmpty)
 
@@ -40,8 +40,6 @@ class DatesSelectedState {
             setDates(newDate, DaySelectedEmpty)
         } else if (from != DaySelectedEmpty && to != DaySelectedEmpty) {
             clearDates()
-            from = DaySelectedEmpty
-            to = DaySelectedEmpty
             daySelected(newDate = newDate)
         } else if (from == DaySelectedEmpty) {
             if (newDate < to) setDates(newDate, to)
@@ -75,7 +73,7 @@ class DatesSelectedState {
             from.month.getDay(from.month.numDays).status = DaySelectedStatus.LastDay
             // Fill in-between months
             for (i in (from.month.monthNumber + 1) until to.month.monthNumber) {
-                val month = year2020[i - 1]
+                val month = year[i - 1]
                 month.getDay(1).status = DaySelectedStatus.FirstDay
                 for (j in 2 until month.numDays) {
                     month.getDay(j).status = DaySelectedStatus.Selected
@@ -92,7 +90,7 @@ class DatesSelectedState {
 
     @VisibleForTesting
     fun clearDates() {
-        if (from != DaySelectedEmpty && to != DaySelectedEmpty) {
+        if (from != DaySelectedEmpty || to != DaySelectedEmpty) {
             // Unselect dates from the same month
             if (from.month == to.month) {
                 for (i in from.day..to.day)
@@ -104,7 +102,7 @@ class DatesSelectedState {
                 }
                 // Fill in-between months
                 for (i in (from.month.monthNumber + 1) until to.month.monthNumber) {
-                    val month = year2020[i - 1]
+                    val month = year[i - 1]
                     for (j in 1..month.numDays) {
                         month.getDay(j).status = DaySelectedStatus.NoSelected
                     }
@@ -115,5 +113,9 @@ class DatesSelectedState {
                 }
             }
         }
+        from.calendarDay.value.status = DaySelectedStatus.NoSelected
+        from = DaySelectedEmpty
+        to.calendarDay.value.status = DaySelectedStatus.NoSelected
+        to = DaySelectedEmpty
     }
 }
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/model/DaySelected.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/model/DaySelected.kt
index 85077cbde4..4dfd0a287c 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/model/DaySelected.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/calendar/model/DaySelected.kt
@@ -16,10 +16,9 @@
 
 package androidx.compose.samples.crane.calendar.model
 
-import androidx.compose.samples.crane.calendar.data.january2020
-import androidx.compose.samples.crane.calendar.data.year2020
+import androidx.compose.samples.crane.data.CalendarYear
 
-data class DaySelected(val day: Int, val month: CalendarMonth) {
+data class DaySelected(val day: Int, val month: CalendarMonth, val year: CalendarYear) {
     val calendarDay = lazy {
         month.getDay(day)
     }
@@ -31,8 +30,8 @@ data class DaySelected(val day: Int, val month: CalendarMonth) {
     operator fun compareTo(other: DaySelected): Int {
         if (day == other.day && month == other.month) return 0
         if (month == other.month) return day.compareTo(other.day)
-        return (year2020.indexOf(month)).compareTo(
-            year2020.indexOf(other.month)
+        return (year.indexOf(month)).compareTo(
+            year.indexOf(other.month)
         )
     }
 }
@@ -40,4 +39,4 @@ data class DaySelected(val day: Int, val month: CalendarMonth) {
 /**
  * Represents an empty value for [DaySelected]
  */
-val DaySelectedEmpty = DaySelected(-1, january2020)
+val DaySelectedEmpty = DaySelected(-1, CalendarMonth("", "", 0, 0, DayOfWeek.Sunday), emptyList())
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/data/CraneData.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/data/CraneData.kt
deleted file mode 100644
index 27ba5b9135..0000000000
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/data/CraneData.kt
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     https://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 androidx.compose.samples.crane.data
-
-private const val DEFAULT_IMAGE_WIDTH = "250"
-
-val craneRestaurants = listOf(
-    ExploreModel(
-        city = NAPLES,
-        description = "1286 Restaurants",
-        imageUrl = "https://images.unsplash.com/photo-1534308983496-4fabb1a015ee?ixlib=rb-1.2.1&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = DALLAS,
-        description = "2241 Restaurants",
-        imageUrl = "https://images.unsplash.com/photo-1495749388945-9d6e4e5b67b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = CORDOBA,
-        description = "876 Restaurants",
-        imageUrl = "https://images.unsplash.com/photo-1562625964-ffe9b2f617fc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=250&q=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = MADRID,
-        description = "5610 Restaurants",
-        imageUrl = "https://images.unsplash.com/photo-1515443961218-a51367888e4b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = MALDIVAS,
-        description = "1286 Restaurants",
-        imageUrl = "https://images.unsplash.com/flagged/photo-1556202256-af2687079e51?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = ASPEN,
-        description = "2241 Restaurants",
-        imageUrl = "https://images.unsplash.com/photo-1542384557-0824d90731ee?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = BALI,
-        description = "876 Restaurants",
-        imageUrl = "https://images.unsplash.com/photo-1567337710282-00832b415979?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    )
-)
-
-val craneHotels = listOf(
-    ExploreModel(
-        city = MALDIVAS,
-        description = "1286 Available Properties",
-        imageUrl = "https://images.unsplash.com/photo-1520250497591-112f2f40a3f4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = ASPEN,
-        description = "2241 Available Properties",
-        imageUrl = "https://images.unsplash.com/photo-1445019980597-93fa8acb246c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = BALI,
-        description = "876 Available Properties",
-        imageUrl = "https://images.unsplash.com/photo-1570213489059-0aac6626cade?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = BIGSUR,
-        description = "5610 Available Properties",
-        imageUrl = "https://images.unsplash.com/photo-1561409037-c7be81613c1f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = NAPLES,
-        description = "1286 Available Properties",
-        imageUrl = "https://images.unsplash.com/photo-1455587734955-081b22074882?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = DALLAS,
-        description = "2241 Available Properties",
-        imageUrl = "https://images.unsplash.com/46/sh3y2u5PSaKq8c4LxB3B_submission-photo-4.jpg?ixlib=rb-1.2.1&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = CORDOBA,
-        description = "876 Available Properties",
-        imageUrl = "https://images.unsplash.com/photo-1570214476695-19bd467e6f7a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    )
-)
-
-val craneDestinations = listOf(
-    ExploreModel(
-        city = KHUMBUVALLEY,
-        description = "Nonstop - 5h 16m+",
-        imageUrl = "https://images.unsplash.com/photo-1544735716-392fe2489ffa?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = MADRID,
-        description = "Nonstop - 2h 12m+",
-        imageUrl = "https://images.unsplash.com/photo-1539037116277-4db20889f2d4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = BALI,
-        description = "Nonstop - 6h 20m+",
-        imageUrl = "https://images.unsplash.com/photo-1518548419970-58e3b4079ab2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = ROME,
-        description = "Nonstop - 2h 38m+",
-        imageUrl = "https://images.unsplash.com/photo-1515542622106-78bda8ba0e5b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = GRANADA,
-        description = "Nonstop - 2h 12m+",
-        imageUrl = "https://images.unsplash.com/photo-1534423839368-1796a4dd1845?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = MALDIVAS,
-        description = "Nonstop - 9h 24m+",
-        imageUrl = "https://images.unsplash.com/photo-1544550581-5f7ceaf7f992?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = WASHINGTONDC,
-        description = "Nonstop - 7h 30m+",
-        imageUrl = "https://images.unsplash.com/photo-1557160854-e1e89fdd3286?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = BARCELONA,
-        description = "Nonstop - 2h 12m+",
-        imageUrl = "https://images.unsplash.com/photo-1562883676-8c7feb83f09b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    ),
-    ExploreModel(
-        city = CRETE,
-        description = "Nonstop - 1h 50m+",
-        imageUrl = "https://images.unsplash.com/photo-1486575008575-27670acb58db?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
-    )
-)
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/data/DatesLocalDataSource.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/data/DatesLocalDataSource.kt
new file mode 100644
index 0000000000..d33dd24959
--- /dev/null
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/data/DatesLocalDataSource.kt
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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 androidx.compose.samples.crane.data
+
+import androidx.compose.samples.crane.calendar.model.CalendarDay
+import androidx.compose.samples.crane.calendar.model.CalendarMonth
+import androidx.compose.samples.crane.calendar.model.DayOfWeek
+import javax.inject.Inject
+import javax.inject.Singleton
+
+typealias CalendarYear = List<CalendarMonth>
+
+/**
+ * Annotated with Singleton because [CalendarDay] contains mutable state.
+ */
+@Singleton
+class DatesLocalDataSource @Inject constructor() {
+
+    private val january2020 = CalendarMonth(
+        name = "January",
+        year = "2020",
+        numDays = 31,
+        monthNumber = 1,
+        startDayOfWeek = DayOfWeek.Wednesday
+    )
+    private val february2020 = CalendarMonth(
+        name = "February",
+        year = "2020",
+        numDays = 29,
+        monthNumber = 2,
+        startDayOfWeek = DayOfWeek.Saturday
+    )
+    private val march2020 = CalendarMonth(
+        name = "March",
+        year = "2020",
+        numDays = 31,
+        monthNumber = 3,
+        startDayOfWeek = DayOfWeek.Sunday
+    )
+    private val april2020 = CalendarMonth(
+        name = "April",
+        year = "2020",
+        numDays = 30,
+        monthNumber = 4,
+        startDayOfWeek = DayOfWeek.Wednesday
+    )
+    private val may2020 = CalendarMonth(
+        name = "May",
+        year = "2020",
+        numDays = 31,
+        monthNumber = 5,
+        startDayOfWeek = DayOfWeek.Friday
+    )
+    private val june2020 = CalendarMonth(
+        name = "June",
+        year = "2020",
+        numDays = 30,
+        monthNumber = 6,
+        startDayOfWeek = DayOfWeek.Monday
+    )
+    private val july2020 = CalendarMonth(
+        name = "July",
+        year = "2020",
+        numDays = 31,
+        monthNumber = 7,
+        startDayOfWeek = DayOfWeek.Wednesday
+    )
+    private val august2020 = CalendarMonth(
+        name = "August",
+        year = "2020",
+        numDays = 31,
+        monthNumber = 8,
+        startDayOfWeek = DayOfWeek.Saturday
+    )
+    private val september2020 = CalendarMonth(
+        name = "September",
+        year = "2020",
+        numDays = 30,
+        monthNumber = 9,
+        startDayOfWeek = DayOfWeek.Tuesday
+    )
+    private val october2020 = CalendarMonth(
+        name = "October",
+        year = "2020",
+        numDays = 31,
+        monthNumber = 10,
+        startDayOfWeek = DayOfWeek.Thursday
+    )
+    private val november2020 = CalendarMonth(
+        name = "November",
+        year = "2020",
+        numDays = 30,
+        monthNumber = 11,
+        startDayOfWeek = DayOfWeek.Sunday
+    )
+    private val december2020 = CalendarMonth(
+        name = "December",
+        year = "2020",
+        numDays = 31,
+        monthNumber = 12,
+        startDayOfWeek = DayOfWeek.Tuesday
+    )
+
+    val year2020: CalendarYear = listOf(
+        january2020,
+        february2020,
+        march2020,
+        april2020,
+        may2020,
+        june2020,
+        july2020,
+        august2020,
+        september2020,
+        october2020,
+        november2020,
+        december2020
+    )
+}
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/data/DatesRepository.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/data/DatesRepository.kt
new file mode 100644
index 0000000000..7b0dc0e7c5
--- /dev/null
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/data/DatesRepository.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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 androidx.compose.samples.crane.data
+
+import androidx.compose.samples.crane.calendar.model.DatesSelectedState
+import androidx.compose.samples.crane.calendar.model.DaySelected
+import androidx.compose.samples.crane.di.DefaultDispatcher
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+import javax.inject.Inject
+import javax.inject.Singleton
+
+/**
+ * Annotated with Singleton because [DatesSelectedState] contains mutable state.
+ */
+@Singleton
+class DatesRepository @Inject constructor(
+    datesLocalDataSource: DatesLocalDataSource,
+    @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
+
+) {
+    val calendarYear = datesLocalDataSource.year2020
+    val datesSelected = DatesSelectedState(datesLocalDataSource.year2020)
+
+    suspend fun onDaySelected(daySelected: DaySelected) = withContext(defaultDispatcher) {
+        datesSelected.daySelected(daySelected)
+    }
+}
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/data/DestinationsLocalDataSource.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/data/DestinationsLocalDataSource.kt
new file mode 100644
index 0000000000..bb5d179182
--- /dev/null
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/data/DestinationsLocalDataSource.kt
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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 androidx.compose.samples.crane.data
+
+import javax.inject.Inject
+import javax.inject.Singleton
+
+private const val DEFAULT_IMAGE_WIDTH = "250"
+
+/**
+ * Annotated with Singleton as the class created a lot of objects.
+ */
+@Singleton
+class DestinationsLocalDataSource @Inject constructor() {
+
+    val craneRestaurants = listOf(
+        ExploreModel(
+            city = NAPLES,
+            description = "1286 Restaurants",
+            imageUrl = "https://images.unsplash.com/photo-1534308983496-4fabb1a015ee?ixlib=rb-1.2.1&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = DALLAS,
+            description = "2241 Restaurants",
+            imageUrl = "https://images.unsplash.com/photo-1495749388945-9d6e4e5b67b1?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = CORDOBA,
+            description = "876 Restaurants",
+            imageUrl = "https://images.unsplash.com/photo-1562625964-ffe9b2f617fc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=250&q=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = MADRID,
+            description = "5610 Restaurants",
+            imageUrl = "https://images.unsplash.com/photo-1515443961218-a51367888e4b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = MALDIVAS,
+            description = "1286 Restaurants",
+            imageUrl = "https://images.unsplash.com/flagged/photo-1556202256-af2687079e51?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = ASPEN,
+            description = "2241 Restaurants",
+            imageUrl = "https://images.unsplash.com/photo-1542384557-0824d90731ee?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = BALI,
+            description = "876 Restaurants",
+            imageUrl = "https://images.unsplash.com/photo-1567337710282-00832b415979?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        )
+    )
+
+    val craneHotels = listOf(
+        ExploreModel(
+            city = MALDIVAS,
+            description = "1286 Available Properties",
+            imageUrl = "https://images.unsplash.com/photo-1520250497591-112f2f40a3f4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = ASPEN,
+            description = "2241 Available Properties",
+            imageUrl = "https://images.unsplash.com/photo-1445019980597-93fa8acb246c?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = BALI,
+            description = "876 Available Properties",
+            imageUrl = "https://images.unsplash.com/photo-1570213489059-0aac6626cade?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = BIGSUR,
+            description = "5610 Available Properties",
+            imageUrl = "https://images.unsplash.com/photo-1561409037-c7be81613c1f?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = NAPLES,
+            description = "1286 Available Properties",
+            imageUrl = "https://images.unsplash.com/photo-1455587734955-081b22074882?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = DALLAS,
+            description = "2241 Available Properties",
+            imageUrl = "https://images.unsplash.com/46/sh3y2u5PSaKq8c4LxB3B_submission-photo-4.jpg?ixlib=rb-1.2.1&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = CORDOBA,
+            description = "876 Available Properties",
+            imageUrl = "https://images.unsplash.com/photo-1570214476695-19bd467e6f7a?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        )
+    )
+
+    val craneDestinations = listOf(
+        ExploreModel(
+            city = KHUMBUVALLEY,
+            description = "Nonstop - 5h 16m+",
+            imageUrl = "https://images.unsplash.com/photo-1544735716-392fe2489ffa?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = MADRID,
+            description = "Nonstop - 2h 12m+",
+            imageUrl = "https://images.unsplash.com/photo-1539037116277-4db20889f2d4?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = BALI,
+            description = "Nonstop - 6h 20m+",
+            imageUrl = "https://images.unsplash.com/photo-1518548419970-58e3b4079ab2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = ROME,
+            description = "Nonstop - 2h 38m+",
+            imageUrl = "https://images.unsplash.com/photo-1515542622106-78bda8ba0e5b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = GRANADA,
+            description = "Nonstop - 2h 12m+",
+            imageUrl = "https://images.unsplash.com/photo-1534423839368-1796a4dd1845?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = MALDIVAS,
+            description = "Nonstop - 9h 24m+",
+            imageUrl = "https://images.unsplash.com/photo-1544550581-5f7ceaf7f992?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = WASHINGTONDC,
+            description = "Nonstop - 7h 30m+",
+            imageUrl = "https://images.unsplash.com/photo-1557160854-e1e89fdd3286?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = BARCELONA,
+            description = "Nonstop - 2h 12m+",
+            imageUrl = "https://images.unsplash.com/photo-1562883676-8c7feb83f09b?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        ),
+        ExploreModel(
+            city = CRETE,
+            description = "Nonstop - 1h 50m+",
+            imageUrl = "https://images.unsplash.com/photo-1486575008575-27670acb58db?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=$DEFAULT_IMAGE_WIDTH"
+        )
+    )
+}
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/data/DestinationsRepository.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/data/DestinationsRepository.kt
new file mode 100644
index 0000000000..df419033a4
--- /dev/null
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/data/DestinationsRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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 androidx.compose.samples.crane.data
+
+import javax.inject.Inject
+
+class DestinationsRepository @Inject constructor(
+    private val destinationsLocalDataSource: DestinationsLocalDataSource
+) {
+    val destinations: List<ExploreModel> = destinationsLocalDataSource.craneDestinations
+    val hotels: List<ExploreModel> = destinationsLocalDataSource.craneHotels
+    val restaurants: List<ExploreModel> = destinationsLocalDataSource.craneRestaurants
+
+    fun getDestination(cityName: String): ExploreModel? {
+        return destinationsLocalDataSource.craneDestinations.firstOrNull {
+            it.city.name == cityName
+        }
+    }
+}
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/details/DetailsActivity.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/details/DetailsActivity.kt
index 4715a32dc5..6aaead5a75 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/details/DetailsActivity.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/details/DetailsActivity.kt
@@ -30,12 +30,15 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.material.Button
+import androidx.compose.material.ButtonConstants
 import androidx.compose.material.MaterialTheme
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
 import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
 import androidx.compose.samples.crane.base.CraneScaffold
+import androidx.compose.samples.crane.base.Result
 import androidx.compose.samples.crane.data.ExploreModel
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
@@ -46,11 +49,11 @@ import com.google.android.libraries.maps.CameraUpdateFactory
 import com.google.android.libraries.maps.MapView
 import com.google.android.libraries.maps.model.LatLng
 import com.google.android.libraries.maps.model.MarkerOptions
+import dagger.hilt.android.AndroidEntryPoint
+import java.lang.IllegalStateException
+import javax.inject.Inject
 
-private const val DETAILS_NAME = "DETAILS_NAME"
-private const val DETAILS_DESCRIPTION = "DETAILS_DESCRIPTION"
-private const val DETAILS_LATITUDE = "DETAILS_LATITUDE"
-private const val DETAILS_LONGITUDE = "DETAILS_LONGITUDE"
+private const val KEY_ARG_DETAILS_CITY_NAME = "KEY_ARG_DETAILS_CITY_NAME"
 
 fun launchDetailsActivity(context: Context, item: ExploreModel) {
     context.startActivity(createDetailsActivityIntent(context, item))
@@ -59,56 +62,72 @@ fun launchDetailsActivity(context: Context, item: ExploreModel) {
 @VisibleForTesting
 fun createDetailsActivityIntent(context: Context, item: ExploreModel): Intent {
     val intent = Intent(context, DetailsActivity::class.java)
-    intent.putExtra(DETAILS_NAME, item.city.nameToDisplay)
-    intent.putExtra(DETAILS_DESCRIPTION, item.description)
-    intent.putExtra(DETAILS_LATITUDE, item.city.latitude)
-    intent.putExtra(DETAILS_LONGITUDE, item.city.longitude)
+    intent.putExtra(KEY_ARG_DETAILS_CITY_NAME, item.city.name)
     return intent
 }
 
 data class DetailsActivityArg(
-    val name: String,
-    val description: String,
-    val latitude: String,
-    val longitude: String
+    val cityName: String
 )
 
+@AndroidEntryPoint
 class DetailsActivity : ComponentActivity() {
 
+    @Inject
+    lateinit var viewModelFactory: DetailsViewModel.AssistedFactory
+
     override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
-
-        val args = DetailsActivityArg(
-            name = intent.getStringExtra(DETAILS_NAME)!!,
-            description = intent.getStringExtra(DETAILS_DESCRIPTION)!!,
-            latitude = intent.getStringExtra(DETAILS_LATITUDE)!!,
-            longitude = intent.getStringExtra(DETAILS_LONGITUDE)!!
-        )
+        val args = getDetailsArgs(intent)
 
         setContent {
             CraneScaffold {
-                DetailsScreen(args = args)
+                DetailsScreen(args, viewModelFactory, onErrorLoading = { finish() })
             }
         }
     }
+
+    private fun getDetailsArgs(intent: Intent): DetailsActivityArg {
+        val cityArg = intent.getStringExtra(KEY_ARG_DETAILS_CITY_NAME)
+        if (cityArg.isNullOrEmpty()) {
+            throw IllegalStateException("DETAILS_CITY_NAME arg cannot be null or empty")
+        }
+        return DetailsActivityArg(cityArg)
+    }
+}
+
+@Composable
+fun DetailsScreen(
+    args: DetailsActivityArg,
+    viewModelFactory: DetailsViewModel.AssistedFactory,
+    onErrorLoading: () -> Unit
+) {
+    val viewModel: DetailsViewModel = viewModelFactory.create(args.cityName)
+
+    val cityDetailsResult = remember(viewModel) { viewModel.cityDetails }
+    if (cityDetailsResult is Result.Success<ExploreModel>) {
+        DetailsContent(cityDetailsResult.data)
+    } else {
+        onErrorLoading()
+    }
 }
 
 @Composable
-fun DetailsScreen(args: DetailsActivityArg) {
+fun DetailsContent(exploreModel: ExploreModel) {
     Column(verticalArrangement = Arrangement.Center) {
         Spacer(Modifier.preferredHeight(32.dp))
         Text(
             modifier = Modifier.align(Alignment.CenterHorizontally),
-            text = args.name,
+            text = exploreModel.city.nameToDisplay,
             style = MaterialTheme.typography.h4
         )
         Text(
             modifier = Modifier.align(Alignment.CenterHorizontally),
-            text = args.description,
+            text = exploreModel.description,
             style = MaterialTheme.typography.h6
         )
         Spacer(Modifier.preferredHeight(16.dp))
-        CityMapView(args.latitude, args.longitude)
+        CityMapView(exploreModel.city.latitude, exploreModel.city.longitude)
     }
 }
 
@@ -163,8 +182,10 @@ private fun ZoomControls(
 private fun ZoomButton(text: String, onClick: () -> Unit) {
     Button(
         modifier = Modifier.padding(8.dp),
-        backgroundColor = MaterialTheme.colors.onPrimary,
-        contentColor = MaterialTheme.colors.primary,
+        colors = ButtonConstants.defaultButtonColors(
+            backgroundColor = MaterialTheme.colors.onPrimary,
+            contentColor = MaterialTheme.colors.primary
+        ),
         onClick = onClick
     ) {
         Text(text = text, style = MaterialTheme.typography.h5)
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/details/DetailsViewModel.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/details/DetailsViewModel.kt
new file mode 100644
index 0000000000..c97ffe1407
--- /dev/null
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/details/DetailsViewModel.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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 androidx.compose.samples.crane.details
+
+import androidx.compose.samples.crane.base.Result
+import androidx.compose.samples.crane.data.DestinationsRepository
+import androidx.compose.samples.crane.data.ExploreModel
+import androidx.lifecycle.ViewModel
+import androidx.lifecycle.ViewModelProvider
+import com.squareup.inject.assisted.Assisted
+import com.squareup.inject.assisted.AssistedInject
+import com.squareup.inject.assisted.dagger2.AssistedModule
+import dagger.Module
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ActivityRetainedComponent
+import java.lang.IllegalArgumentException
+
+class DetailsViewModel @AssistedInject constructor(
+    private val destinationsRepository: DestinationsRepository,
+    @Assisted private val cityName: String
+) : ViewModel() {
+
+    val cityDetails: Result<ExploreModel>
+        get() {
+            val destination = destinationsRepository.getDestination(cityName)
+            return if (destination != null) {
+                Result.Success(destination)
+            } else {
+                Result.Error(IllegalArgumentException("City doesn't exist"))
+            }
+        }
+
+    @AssistedInject.Factory
+    interface AssistedFactory {
+        fun create(cityName: String): DetailsViewModel
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    companion object {
+        fun provideFactory(
+            assistedFactory: AssistedFactory,
+            cityName: String
+        ): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
+            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
+                return assistedFactory.create(cityName) as T
+            }
+        }
+    }
+}
+
+@InstallIn(ActivityRetainedComponent::class)
+@AssistedModule
+@Module(includes = [AssistedInject_AssistedInjectModule::class])
+interface AssistedInjectModule
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/di/DispatchersModule.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/di/DispatchersModule.kt
new file mode 100644
index 0000000000..19ff0c9f6b
--- /dev/null
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/di/DispatchersModule.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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 androidx.compose.samples.crane.di
+
+import dagger.Module
+import dagger.Provides
+import dagger.hilt.InstallIn
+import dagger.hilt.android.components.ApplicationComponent
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.Dispatchers
+import javax.inject.Qualifier
+
+@Module
+@InstallIn(ApplicationComponent::class)
+class DispatchersModule {
+
+    @Provides
+    @DefaultDispatcher
+    fun provideDefaultDispatcher(): CoroutineDispatcher = Dispatchers.Default
+}
+
+@Retention(AnnotationRetention.BINARY)
+@Qualifier
+annotation class DefaultDispatcher
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/home/CraneHome.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/home/CraneHome.kt
index 510c72769f..570e36cc2c 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/home/CraneHome.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/home/CraneHome.kt
@@ -154,8 +154,13 @@ private fun SearchContent(
     onDateSelectionClicked: () -> Unit,
     onExploreItemClicked: OnExploreItemClicked
 ) {
+    // Reading datesSelected State from here instead of passing the String from the ViewModel
+    // to cause a recomposition when the dates change.
+    val datesSelected = viewModel.datesSelected.toString()
+
     when (tabSelected) {
         CraneScreen.Fly -> FlySearchContent(
+            datesSelected,
             searchUpdates = FlySearchContentUpdates(
                 onPeopleChanged = onPeopleChanged,
                 onToDestinationChanged = { viewModel.toDestinationChanged(it) },
@@ -164,6 +169,7 @@ private fun SearchContent(
             )
         )
         CraneScreen.Sleep -> SleepSearchContent(
+            datesSelected,
             sleepUpdates = SleepSearchContentUpdates(
                 onPeopleChanged = onPeopleChanged,
                 onDateSelectionClicked = onDateSelectionClicked,
@@ -171,6 +177,7 @@ private fun SearchContent(
             )
         )
         CraneScreen.Eat -> EatSearchContent(
+            datesSelected,
             eatUpdates = EatSearchContentUpdates(
                 onPeopleChanged = onPeopleChanged,
                 onDateSelectionClicked = onDateSelectionClicked,
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/home/HomeFeatures.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/home/HomeFeatures.kt
index 58b3051203..64a6f46479 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/home/HomeFeatures.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/home/HomeFeatures.kt
@@ -27,7 +27,7 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
 
 @Composable
-fun FlySearchContent(searchUpdates: FlySearchContentUpdates) {
+fun FlySearchContent(datesSelected: String, searchUpdates: FlySearchContentUpdates) {
     CraneSearch {
         PeopleUserInput(
             titleSuffix = ", Economy",
@@ -38,27 +38,27 @@ fun FlySearchContent(searchUpdates: FlySearchContentUpdates) {
         Spacer(Modifier.preferredHeight(8.dp))
         ToDestinationUserInput(onToDestinationChanged = searchUpdates.onToDestinationChanged)
         Spacer(Modifier.preferredHeight(8.dp))
-        DatesUserInput(onDateSelectionClicked = searchUpdates.onDateSelectionClicked)
+        DatesUserInput(datesSelected, onDateSelectionClicked = searchUpdates.onDateSelectionClicked)
     }
 }
 
 @Composable
-fun SleepSearchContent(sleepUpdates: SleepSearchContentUpdates) {
+fun SleepSearchContent(datesSelected: String, sleepUpdates: SleepSearchContentUpdates) {
     CraneSearch {
         PeopleUserInput(onPeopleChanged = { sleepUpdates.onPeopleChanged })
         Spacer(Modifier.preferredHeight(8.dp))
-        DatesUserInput(onDateSelectionClicked = sleepUpdates.onDateSelectionClicked)
+        DatesUserInput(datesSelected, onDateSelectionClicked = sleepUpdates.onDateSelectionClicked)
         Spacer(Modifier.preferredHeight(8.dp))
         SimpleUserInput(caption = "Select Location", vectorImageId = R.drawable.ic_hotel)
     }
 }
 
 @Composable
-fun EatSearchContent(eatUpdates: EatSearchContentUpdates) {
+fun EatSearchContent(datesSelected: String, eatUpdates: EatSearchContentUpdates) {
     CraneSearch {
         PeopleUserInput(onPeopleChanged = { eatUpdates.onPeopleChanged })
         Spacer(Modifier.preferredHeight(8.dp))
-        DatesUserInput(onDateSelectionClicked = eatUpdates.onDateSelectionClicked)
+        DatesUserInput(datesSelected, onDateSelectionClicked = eatUpdates.onDateSelectionClicked)
         Spacer(Modifier.preferredHeight(8.dp))
         SimpleUserInput(caption = "Select Time", vectorImageId = R.drawable.ic_time)
         Spacer(Modifier.preferredHeight(8.dp))
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/home/MainActivity.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/home/MainActivity.kt
index a8cf1717de..b22e9eda1a 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/home/MainActivity.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/home/MainActivity.kt
@@ -42,7 +42,9 @@ import androidx.compose.ui.draw.drawOpacity
 import androidx.compose.ui.platform.setContent
 import androidx.compose.ui.unit.Dp
 import androidx.compose.ui.unit.dp
+import dagger.hilt.android.AndroidEntryPoint
 
+@AndroidEntryPoint
 class MainActivity : ComponentActivity() {
 
     override fun onCreate(savedInstanceState: Bundle?) {
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/home/MainViewModel.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/home/MainViewModel.kt
index c325409e1a..c9f6399e89 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/home/MainViewModel.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/home/MainViewModel.kt
@@ -16,42 +16,62 @@
 
 package androidx.compose.samples.crane.home
 
+import androidx.compose.samples.crane.calendar.model.DatesSelectedState
+import androidx.compose.samples.crane.data.DatesRepository
+import androidx.compose.samples.crane.data.DestinationsRepository
 import androidx.compose.samples.crane.data.ExploreModel
-import androidx.compose.samples.crane.data.craneDestinations
-import androidx.compose.samples.crane.data.craneHotels
-import androidx.compose.samples.crane.data.craneRestaurants
+import androidx.compose.samples.crane.di.DefaultDispatcher
+import androidx.hilt.lifecycle.ViewModelInject
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
+import androidx.lifecycle.viewModelScope
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
 import kotlin.random.Random
 
 const val MAX_PEOPLE = 4
 
-class MainViewModel : ViewModel() {
+class MainViewModel @ViewModelInject constructor(
+    private val destinationsRepository: DestinationsRepository,
+    @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
+    datesRepository: DatesRepository
+) : ViewModel() {
 
-    val hotels = craneHotels
-    val restaurants = craneRestaurants
+    val hotels: List<ExploreModel> = destinationsRepository.hotels
+    val restaurants: List<ExploreModel> = destinationsRepository.restaurants
+    val datesSelected: DatesSelectedState = datesRepository.datesSelected
 
     private val _suggestedDestinations = MutableLiveData<List<ExploreModel>>()
     val suggestedDestinations: LiveData<List<ExploreModel>>
         get() = _suggestedDestinations
 
     init {
-        _suggestedDestinations.value = craneDestinations
+        _suggestedDestinations.value = destinationsRepository.destinations
     }
 
     fun updatePeople(people: Int) {
-        if (people > MAX_PEOPLE) {
-            _suggestedDestinations.value = emptyList()
-        } else {
-            // Making Random more random
-            _suggestedDestinations.value =
-                craneDestinations.shuffled(Random(people * (1..100).shuffled().first()))
+        viewModelScope.launch {
+            if (people > MAX_PEOPLE) {
+                _suggestedDestinations.value = emptyList()
+            } else {
+                val newDestinations = withContext(defaultDispatcher) {
+                    destinationsRepository.destinations
+                        .shuffled(Random(people * (1..100).shuffled().first()))
+                }
+                _suggestedDestinations.value = newDestinations
+            }
         }
     }
 
     fun toDestinationChanged(newDestination: String) {
-        _suggestedDestinations.value =
-            craneDestinations.filter { it.city.nameToDisplay.contains(newDestination) }
+        viewModelScope.launch {
+            val newDestinations = withContext(defaultDispatcher) {
+                destinationsRepository.destinations
+                    .filter { it.city.nameToDisplay.contains(newDestination) }
+            }
+            _suggestedDestinations.value = newDestinations
+        }
     }
 }
diff --git a/Crane/app/src/main/java/androidx/compose/samples/crane/home/SearchUserInput.kt b/Crane/app/src/main/java/androidx/compose/samples/crane/home/SearchUserInput.kt
index 09694334dd..46cffbf6b9 100644
--- a/Crane/app/src/main/java/androidx/compose/samples/crane/home/SearchUserInput.kt
+++ b/Crane/app/src/main/java/androidx/compose/samples/crane/home/SearchUserInput.kt
@@ -32,7 +32,6 @@ import androidx.compose.runtime.setValue
 import androidx.compose.samples.crane.R
 import androidx.compose.samples.crane.base.CraneEditableUserInput
 import androidx.compose.samples.crane.base.CraneUserInput
-import androidx.compose.samples.crane.base.ServiceLocator
 import androidx.compose.samples.crane.home.PeopleUserInputAnimationState.Invalid
 import androidx.compose.samples.crane.home.PeopleUserInputAnimationState.Valid
 import androidx.compose.samples.crane.ui.CraneTheme
@@ -114,12 +113,11 @@ fun ToDestinationUserInput(onToDestinationChanged: (String) -> Unit) {
 }
 
 @Composable
-fun DatesUserInput(onDateSelectionClicked: () -> Unit) {
-    val datesSelectedText = ServiceLocator.datesSelected.toString()
+fun DatesUserInput(datesSelected: String, onDateSelectionClicked: () -> Unit) {
     CraneUserInput(
         modifier = Modifier.clickable(onClick = onDateSelectionClicked),
-        caption = if (datesSelectedText.isEmpty()) "Select Dates" else null,
-        text = datesSelectedText,
+        caption = if (datesSelected.isEmpty()) "Select Dates" else null,
+        text = datesSelected,
         vectorImageId = R.drawable.ic_calendar
     )
 }
diff --git a/Crane/build.gradle b/Crane/build.gradle
index 1f6412ac10..96fe956cf1 100644
--- a/Crane/build.gradle
+++ b/Crane/build.gradle
@@ -26,11 +26,12 @@ buildscript {
     dependencies {
         classpath Libs.androidGradlePlugin
         classpath Libs.Kotlin.gradlePlugin
+        classpath Libs.Hilt.gradlePlugin
     }
 }
 
 plugins {
-    id 'com.diffplug.spotless' version '5.1.1'
+    id 'com.diffplug.spotless' version '5.7.0'
 }
 
 subprojects {
diff --git a/Crane/buildSrc/src/main/java/com/example/crane/buildsrc/Dependencies.kt b/Crane/buildSrc/src/main/java/com/example/crane/buildsrc/Dependencies.kt
index b533be766f..b48e95e7c2 100644
--- a/Crane/buildSrc/src/main/java/com/example/crane/buildsrc/Dependencies.kt
+++ b/Crane/buildSrc/src/main/java/com/example/crane/buildsrc/Dependencies.kt
@@ -21,12 +21,12 @@ object Versions {
 }
 
 object Libs {
-    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha13"
+    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha15"
     const val ktLint = "com.pinterest:ktlint:${Versions.ktLint}"
     const val googleMaps = "com.google.android.libraries.maps:maps:3.1.0-beta"
 
     object Accompanist {
-        private const val version = "0.3.1"
+        private const val version = "0.3.2"
         const val coil = "dev.chrisbanes.accompanist:accompanist-coil:$version"
     }
 
@@ -35,12 +35,18 @@ object Libs {
         const val stdlib = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
         const val gradlePlugin = "org.jetbrains.kotlin:kotlin-gradle-plugin:$version"
         const val extensions = "org.jetbrains.kotlin:kotlin-android-extensions:$version"
+
+        object Coroutines {
+            private const val version = "1.4.0-M1"
+            const val android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version"
+            const val test = "org.jetbrains.kotlinx:kotlinx-coroutines-test:$version"
+        }
     }
 
     object AndroidX {
         object Compose {
             const val snapshot = ""
-            const val version = "1.0.0-alpha05"
+            const val version = "1.0.0-alpha06"
 
             const val runtime = "androidx.compose.runtime:runtime:$version"
             const val runtimeLivedata = "androidx.compose.runtime:runtime-livedata:$version"
@@ -51,6 +57,11 @@ object Libs {
             const val uiTest = "androidx.ui:ui-test:$version"
         }
 
+        object Lifecycle {
+            private const val version = "2.3.0-beta01"
+            const val viewModelKtx = "androidx.lifecycle:lifecycle-viewmodel-ktx:$version"
+        }
+
         object UI {
             const val tooling = "androidx.ui:ui-tooling:${Compose.version}"
         }
@@ -66,10 +77,38 @@ object Libs {
             const val espressoCore = "androidx.test.espresso:espresso-core:3.2.0"
         }
     }
+
+    object Hilt {
+        private const val version = "2.29.1-alpha"
+
+        const val gradlePlugin = "com.google.dagger:hilt-android-gradle-plugin:$version"
+        const val android = "com.google.dagger:hilt-android:$version"
+        const val compiler = "com.google.dagger:hilt-compiler:$version"
+        const val testing = "com.google.dagger:hilt-android-testing:$version"
+
+        object AndroidX {
+            private const val version = "1.0.0-alpha02"
+
+            const val compiler = "androidx.hilt:hilt-compiler:$version"
+            const val viewModel = "androidx.hilt:hilt-lifecycle-viewmodel:$version"
+        }
+    }
+
+    object JUnit {
+        private const val version = "4.13"
+        const val junit = "junit:junit:$version"
+    }
+
+    object AssistedInjection {
+        private const val version = "0.5.2"
+
+        const val dagger = "com.squareup.inject:assisted-inject-annotations-dagger2:$version"
+        const val processor = "com.squareup.inject:assisted-inject-processor-dagger2:$version"
+    }
 }
 
 object Urls {
     const val mavenCentralSnapshotRepo = "https://oss.sonatype.org/content/repositories/snapshots/"
-    const val composeSnapshotRepo = "https://androidx-dev-prod.appspot.com/snapshots/builds/" +
-        "${Libs.AndroidX.Compose.snapshot}/artifacts/ui/repository/"
+    const val composeSnapshotRepo = "https://androidx.dev/snapshots/builds/" +
+        "${Libs.AndroidX.Compose.snapshot}/artifacts/repository/"
 }
diff --git a/Crane/gradle.properties b/Crane/gradle.properties
index 23339e0df6..338e94c7f3 100644
--- a/Crane/gradle.properties
+++ b/Crane/gradle.properties
@@ -1,4 +1,21 @@
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
 # Project-wide Gradle settings.
+
 # IDE (e.g. Android Studio) users:
 # Gradle settings configured through the IDE *will override*
 # any settings specified in this file.
@@ -6,16 +23,20 @@
 # 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
+org.gradle.jvmargs=-Xmx2048m
+
+# Turn on parallel compilation, caching and on-demand configuration
+org.gradle.configureondemand=true
+org.gradle.caching=true
+org.gradle.parallel=true
+
 # AndroidX package structure to make it clearer which packages are bundled with the
 # Android operating system, and which are packaged with your app's APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
 android.useAndroidX=true
 # Automatically convert third-party libraries to use AndroidX
+# Needed for com.google.android.libraries.maps:maps
 android.enableJetifier=true
+
 # Kotlin code style for this project: "official" or "obsolete":
 kotlin.code.style=official
diff --git a/Crane/gradle/wrapper/gradle-wrapper.properties b/Crane/gradle/wrapper/gradle-wrapper.properties
index fbaa50600d..fd57ef47b9 100644
--- a/Crane/gradle/wrapper/gradle-wrapper.properties
+++ b/Crane/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Mon Jun 15 11:35:59 CEST 2020
+#Fri Oct 23 09:30:32 CEST 2020
 distributionBase=GRADLE_USER_HOME
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
 distributionPath=wrapper/dists
-zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
diff --git a/JetNews/app/build.gradle b/JetNews/app/build.gradle
index f5cbb880de..ae89f0abcd 100644
--- a/JetNews/app/build.gradle
+++ b/JetNews/app/build.gradle
@@ -90,7 +90,7 @@ dependencies {
     implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.3.0-beta01"
     implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-beta01"
 
-    androidTestImplementation 'junit:junit:4.13'
+    androidTestImplementation 'junit:junit:4.13.1'
     androidTestImplementation 'androidx.test:rules:1.3.0'
     androidTestImplementation 'androidx.test:runner:1.3.0'
     androidTestImplementation "androidx.ui:ui-test:$compose_version"
diff --git a/JetNews/app/src/androidTest/java/com/example/jetnews/HomeScreenSnackbarTest.kt b/JetNews/app/src/androidTest/java/com/example/jetnews/HomeScreenSnackbarTest.kt
index 3d7f493fef..1cbeecc096 100644
--- a/JetNews/app/src/androidTest/java/com/example/jetnews/HomeScreenSnackbarTest.kt
+++ b/JetNews/app/src/androidTest/java/com/example/jetnews/HomeScreenSnackbarTest.kt
@@ -39,7 +39,7 @@ import org.junit.Test
 class HomeScreenSnackbarTest {
 
     @get:Rule
-    val composeTestRule = createComposeRule(disableTransitions = true)
+    val composeTestRule = createComposeRule()
 
     @OptIn(
         ExperimentalMaterialApi::class,
diff --git a/JetNews/app/src/androidTest/java/com/example/jetnews/JetnewsUiTest.kt b/JetNews/app/src/androidTest/java/com/example/jetnews/JetnewsUiTest.kt
index 9340f9dc5b..5aceada18f 100644
--- a/JetNews/app/src/androidTest/java/com/example/jetnews/JetnewsUiTest.kt
+++ b/JetNews/app/src/androidTest/java/com/example/jetnews/JetnewsUiTest.kt
@@ -32,7 +32,7 @@ import org.junit.Test
 class JetnewsUiTest {
 
     @get:Rule
-    val composeTestRule = createComposeRule(disableTransitions = true)
+    val composeTestRule = createComposeRule()
 
     @Before
     fun setUp() {
diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/article/ArticleScreen.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/article/ArticleScreen.kt
index f04bdf49c2..110ae4cac7 100644
--- a/JetNews/app/src/main/java/com/example/jetnews/ui/article/ArticleScreen.kt
+++ b/JetNews/app/src/main/java/com/example/jetnews/ui/article/ArticleScreen.kt
@@ -19,7 +19,6 @@ package com.example.jetnews.ui.article
 import android.content.Context
 import android.content.Intent
 import androidx.compose.foundation.AmbientContentColor
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
@@ -27,6 +26,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.material.AlertDialog
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Scaffold
diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/article/PostContent.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/article/PostContent.kt
index 102839619f..605d8fc958 100644
--- a/JetNews/app/src/main/java/com/example/jetnews/ui/article/PostContent.kt
+++ b/JetNews/app/src/main/java/com/example/jetnews/ui/article/PostContent.kt
@@ -214,7 +214,7 @@ private fun BulletParagraph(
             Box(
                 modifier = Modifier
                     .preferredSize(8.sp.toDp(), 8.sp.toDp())
-                    .alignWithSiblings {
+                    .alignBy {
                         // Add an alignment "baseline" 1sp below the bottom of the circle
                         9.sp.toIntPx()
                     }
@@ -224,7 +224,7 @@ private fun BulletParagraph(
         Text(
             modifier = Modifier
                 .weight(1f)
-                .alignWithSiblings(FirstBaseline),
+                .alignBy(FirstBaseline),
             text = text,
             style = textStyle.merge(paragraphStyle)
         )
diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreen.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreen.kt
index 445a4d02a9..2b63d478f8 100644
--- a/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreen.kt
+++ b/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreen.kt
@@ -16,7 +16,6 @@
 
 package com.example.jetnews.ui.home
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.ScrollableRow
 import androidx.compose.foundation.Text
@@ -33,6 +32,7 @@ import androidx.compose.material.CircularProgressIndicator
 import androidx.compose.material.Divider
 import androidx.compose.material.DrawerValue
 import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/home/PostCards.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/home/PostCards.kt
index c161c9fb43..f33c643d44 100644
--- a/JetNews/app/src/main/java/com/example/jetnews/ui/home/PostCards.kt
+++ b/JetNews/app/src/main/java/com/example/jetnews/ui/home/PostCards.kt
@@ -16,16 +16,15 @@
 
 package com.example.jetnews.ui.home
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Row
-import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.material.AmbientEmphasisLevels
+import androidx.compose.material.Icon
 import androidx.compose.material.IconToggleButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
@@ -131,7 +130,7 @@ fun PostCardHistory(post: Post, navigateTo: (Screen) -> Unit) {
             )
         }
         ProvideEmphasis(AmbientEmphasisLevels.current.medium) {
-            Icon(asset = Icons.Filled.MoreVert)
+            Icon(Icons.Filled.MoreVert)
         }
     }
 }
@@ -144,19 +143,13 @@ fun BookmarkButton(
 ) {
     IconToggleButton(
         checked = isBookmarked,
-        onCheckedChange = { onClick() }
+        onCheckedChange = { onClick() },
+        modifier = modifier
     ) {
-        modifier.fillMaxSize()
         if (isBookmarked) {
-            Icon(
-                asset = Icons.Filled.Bookmark,
-                modifier = modifier
-            )
+            Icon(asset = Icons.Filled.Bookmark)
         } else {
-            Icon(
-                asset = Icons.Filled.BookmarkBorder,
-                modifier = modifier
-            )
+            Icon(asset = Icons.Filled.BookmarkBorder)
         }
     }
 }
diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/interests/InterestsScreen.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/interests/InterestsScreen.kt
index 42f321532c..313c6c59be 100644
--- a/JetNews/app/src/main/java/com/example/jetnews/ui/interests/InterestsScreen.kt
+++ b/JetNews/app/src/main/java/com/example/jetnews/ui/interests/InterestsScreen.kt
@@ -16,7 +16,6 @@
 
 package com.example.jetnews.ui.interests
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.Text
@@ -30,6 +29,7 @@ import androidx.compose.foundation.selection.toggleable
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.Divider
 import androidx.compose.material.DrawerValue
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Scaffold
diff --git a/JetNews/app/src/main/java/com/example/jetnews/ui/interests/SelectTopicButton.kt b/JetNews/app/src/main/java/com/example/jetnews/ui/interests/SelectTopicButton.kt
index fcad3396fe..9916dd5f30 100644
--- a/JetNews/app/src/main/java/com/example/jetnews/ui/interests/SelectTopicButton.kt
+++ b/JetNews/app/src/main/java/com/example/jetnews/ui/interests/SelectTopicButton.kt
@@ -16,11 +16,11 @@
 
 package com.example.jetnews.ui.interests
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.shape.CircleShape
 import androidx.compose.material.AmbientEmphasisLevels
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
 import androidx.compose.material.Surface
diff --git a/JetNews/build.gradle b/JetNews/build.gradle
index acfb92e0ed..ff6a558e49 100644
--- a/JetNews/build.gradle
+++ b/JetNews/build.gradle
@@ -16,7 +16,7 @@
 
 buildscript {
     ext.kotlin_version = '1.4.10'
-    ext.compose_version = '1.0.0-alpha05'
+    ext.compose_version = '1.0.0-alpha06'
 
     repositories {
         google()
@@ -24,13 +24,13 @@ buildscript {
     }
 
     dependencies {
-        classpath 'com.android.tools.build:gradle:4.2.0-alpha13'
+        classpath 'com.android.tools.build:gradle:4.2.0-alpha15'
         classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
     }
 }
 
 plugins {
-    id 'com.diffplug.spotless' version '5.1.1'
+    id 'com.diffplug.spotless' version '5.7.0'
 }
 
 subprojects {
@@ -46,7 +46,7 @@ subprojects {
             targetExclude("$buildDir/**/*.kt")
             targetExclude('bin/**/*.kt')
 
-            ktlint("0.38.1")
+            ktlint("0.39.0")
             licenseHeaderFile rootProject.file('spotless/copyright.kt')
         }
     }
diff --git a/JetNews/gradle.properties b/JetNews/gradle.properties
index 099d6a8fe5..9299bc6d0f 100644
--- a/JetNews/gradle.properties
+++ b/JetNews/gradle.properties
@@ -1,10 +1,31 @@
-## For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# 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=-Xmx2048m
 
+# Turn on parallel compilation, caching and on-demand configuration
 org.gradle.configureondemand=true
 org.gradle.caching=true
 org.gradle.parallel=true
@@ -16,4 +37,3 @@ android.useAndroidX=true
 
 # Kotlin code style for this project: "official" or "obsolete":
 kotlin.code.style=official
-org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M"
diff --git a/JetNews/gradle/wrapper/gradle-wrapper.properties b/JetNews/gradle/wrapper/gradle-wrapper.properties
index 715d777bf2..0136562534 100644
--- a/JetNews/gradle/wrapper/gradle-wrapper.properties
+++ b/JetNews/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Wed Sep 16 10:20:00 PDT 2020
+#Tue Oct 27 16:21:59 PDT 2020
 distributionBase=GRADLE_USER_HOME
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
 distributionPath=wrapper/dists
 zipStorePath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
diff --git a/Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/Home.kt b/Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/Home.kt
index 2d5968fa24..9fc5610c9f 100644
--- a/Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/Home.kt
+++ b/Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/Home.kt
@@ -16,7 +16,6 @@
 
 package com.example.jetcaster.ui.home
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.background
@@ -35,6 +34,7 @@ import androidx.compose.foundation.layout.preferredHeightIn
 import androidx.compose.foundation.layout.size
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.material.AmbientEmphasisLevels
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
diff --git a/Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/category/PodcastCategory.kt b/Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/category/PodcastCategory.kt
index 7b110f634d..be277cb0db 100644
--- a/Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/category/PodcastCategory.kt
+++ b/Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/category/PodcastCategory.kt
@@ -17,7 +17,6 @@
 package com.example.jetcaster.ui.home.category
 
 import androidx.compose.foundation.AmbientContentColor
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.clickable
@@ -38,6 +37,7 @@ import androidx.compose.foundation.lazy.LazyColumn
 import androidx.compose.foundation.lazy.LazyRowForIndexed
 import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.Divider
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
diff --git a/Jetcaster/app/src/main/java/com/example/jetcaster/util/Buttons.kt b/Jetcaster/app/src/main/java/com/example/jetcaster/util/Buttons.kt
index 103e51ca33..7de9a3843d 100644
--- a/Jetcaster/app/src/main/java/com/example/jetcaster/util/Buttons.kt
+++ b/Jetcaster/app/src/main/java/com/example/jetcaster/util/Buttons.kt
@@ -18,10 +18,10 @@ package com.example.jetcaster.util
 
 import androidx.compose.animation.animate
 import androidx.compose.foundation.AmbientContentColor
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.padding
 import androidx.compose.material.AmbientEmphasisLevels
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.icons.Icons
diff --git a/Jetcaster/build.gradle b/Jetcaster/build.gradle
index f3bb7bd530..08cef649e9 100644
--- a/Jetcaster/build.gradle
+++ b/Jetcaster/build.gradle
@@ -24,17 +24,13 @@ buildscript {
     }
 
     dependencies {
-        // Downgrade R8 due to b/169095082
-        // TODO remove this
-        classpath 'com.android.tools:r8:2.1.66'
-
         classpath Libs.androidGradlePlugin
         classpath Libs.Kotlin.gradlePlugin
     }
 }
 
 plugins {
-    id 'com.diffplug.spotless' version '5.5.1'
+    id 'com.diffplug.spotless' version '5.7.0'
 }
 
 subprojects {
diff --git a/Jetcaster/buildSrc/src/main/java/com/example/jetcaster/buildsrc/dependencies.kt b/Jetcaster/buildSrc/src/main/java/com/example/jetcaster/buildsrc/dependencies.kt
index 1d7000df8c..dc674ad645 100644
--- a/Jetcaster/buildSrc/src/main/java/com/example/jetcaster/buildsrc/dependencies.kt
+++ b/Jetcaster/buildSrc/src/main/java/com/example/jetcaster/buildsrc/dependencies.kt
@@ -21,7 +21,7 @@ object Versions {
 }
 
 object Libs {
-    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha13"
+    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha15"
     const val jdkDesugar = "com.android.tools:desugar_jdk_libs:1.0.9"
 
     const val junit = "junit:junit:4.13"
@@ -29,7 +29,7 @@ object Libs {
     const val material = "com.google.android.material:material:1.1.0"
 
     object Accompanist {
-        private const val version = "0.3.1"
+        private const val version = "0.3.2"
         const val coil = "dev.chrisbanes.accompanist:accompanist-coil:$version"
     }
 
@@ -57,16 +57,16 @@ object Libs {
         const val appcompat = "androidx.appcompat:appcompat:1.2.0-rc01"
         const val palette = "androidx.palette:palette:1.0.0"
 
-        const val core = "androidx.core:core:1.5.0-alpha02"
-        const val coreKtx = "androidx.core:core-ktx:1.5.0-alpha02"
+        const val core = "androidx.core:core:1.5.0-alpha04"
+        const val coreKtx = "androidx.core:core-ktx:1.5.0-alpha04"
 
         object Compose {
             private const val snapshot = ""
-            const val version = "1.0.0-alpha05"
+            const val version = "1.0.0-alpha06"
 
             @get:JvmStatic
             val snapshotUrl: String
-                get() = "https://androidx.dev/snapshots/builds/$snapshot/artifacts/ui/repository/"
+                get() = "https://androidx.dev/snapshots/builds/$snapshot/artifacts/repository/"
 
             const val runtime = "androidx.compose.runtime:runtime:$version"
             const val foundation = "androidx.compose.foundation:foundation:${version}"
diff --git a/Jetcaster/gradle.properties b/Jetcaster/gradle.properties
index 94c6607b24..646f68d67b 100644
--- a/Jetcaster/gradle.properties
+++ b/Jetcaster/gradle.properties
@@ -1,14 +1,31 @@
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
 # 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=-Xmx2048m
 
+# Turn on parallel compilation, caching and on-demand configurationv
 org.gradle.configureondemand=true
 org.gradle.caching=true
 org.gradle.parallel=true
@@ -19,4 +36,4 @@ org.gradle.parallel=true
 android.useAndroidX=true
 
 # Kotlin code style for this project: "official" or "obsolete":
-kotlin.code.style=official
\ No newline at end of file
+kotlin.code.style=official
diff --git a/Jetcaster/gradle/wrapper/gradle-wrapper.properties b/Jetcaster/gradle/wrapper/gradle-wrapper.properties
index 8d8e8abe86..e8894b0281 100644
--- a/Jetcaster/gradle/wrapper/gradle-wrapper.properties
+++ b/Jetcaster/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
diff --git a/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/ConversationTest.kt b/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/ConversationTest.kt
index 191a3488d9..65ff28d642 100644
--- a/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/ConversationTest.kt
+++ b/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/ConversationTest.kt
@@ -46,7 +46,7 @@ import org.junit.Test
 class ConversationTest {
 
     @get:Rule
-    val composeTestRule = createAndroidComposeRule<NavActivity>(disableTransitions = true)
+    val composeTestRule = createAndroidComposeRule<NavActivity>()
 
     // Note that keeping these references is only safe if the activity is not recreated.
     // See: https://issuetracker.google.com/160862278
diff --git a/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/NavigationTest.kt b/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/NavigationTest.kt
index e88636be6b..76254051b3 100644
--- a/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/NavigationTest.kt
+++ b/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/NavigationTest.kt
@@ -40,7 +40,7 @@ import org.junit.Test
 class NavigationTest {
 
     @get:Rule
-    val composeTestRule = createAndroidComposeRule<NavActivity>(disableTransitions = true)
+    val composeTestRule = createAndroidComposeRule<NavActivity>()
 
     // Note that keeping these references is only safe if the activity is not recreated.
     // See: https://issuetracker.google.com/160862278
diff --git a/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/UserInputTest.kt b/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/UserInputTest.kt
index b0367f8bd9..505ae383ae 100644
--- a/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/UserInputTest.kt
+++ b/Jetchat/app/src/androidTest/java/com/example/compose/jetchat/UserInputTest.kt
@@ -47,7 +47,7 @@ import org.junit.Test
 class UserInputTest {
 
     @get:Rule
-    val composeTestRule = createAndroidComposeRule<NavActivity>(disableTransitions = true)
+    val composeTestRule = createAndroidComposeRule<NavActivity>()
 
     // Note that keeping these references is only safe if the activity is not recreated.
     // See: https://issuetracker.google.com/160862278
diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/components/JetchatAppBar.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/components/JetchatAppBar.kt
index e0dccf0f72..9e5157e6b2 100644
--- a/Jetchat/app/src/main/java/com/example/compose/jetchat/components/JetchatAppBar.kt
+++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/components/JetchatAppBar.kt
@@ -74,3 +74,11 @@ fun JetchatAppBarPreview() {
         JetchatAppBar(title = { Text("Preview!") })
     }
 }
+
+@Preview
+@Composable
+fun JetchatAppBarPreviewDark() {
+    JetchatTheme(isDarkTheme = true) {
+        JetchatAppBar(title = { Text("Preview!") })
+    }
+}
diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/Conversation.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/Conversation.kt
index efbe18eab7..60ef17c249 100644
--- a/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/Conversation.kt
+++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/Conversation.kt
@@ -17,7 +17,6 @@
 package com.example.compose.jetchat.conversation
 
 import androidx.compose.foundation.ClickableText
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.ScrollableColumn
@@ -43,6 +42,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.text.LastBaseline
 import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.Divider
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
 import androidx.compose.material.Surface
@@ -316,7 +316,7 @@ private fun AuthorNameTimestamp(msg: Message) {
                 text = msg.author,
                 style = MaterialTheme.typography.subtitle1,
                 modifier = Modifier
-                    .alignWithSiblings(LastBaseline)
+                    .alignBy(LastBaseline)
                     .relativePaddingFrom(LastBaseline, after = 8.dp) // Space to 1st bubble
             )
         }
@@ -325,7 +325,7 @@ private fun AuthorNameTimestamp(msg: Message) {
             Text(
                 text = msg.timestamp,
                 style = MaterialTheme.typography.caption,
-                modifier = Modifier.alignWithSiblings(LastBaseline)
+                modifier = Modifier.alignBy(LastBaseline)
             )
         }
     }
diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/JumpToBottom.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/JumpToBottom.kt
index c1c85f015e..9e124c196a 100644
--- a/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/JumpToBottom.kt
+++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/JumpToBottom.kt
@@ -19,11 +19,11 @@ package com.example.compose.jetchat.conversation
 import androidx.compose.animation.DpPropKey
 import androidx.compose.animation.core.transitionDefinition
 import androidx.compose.animation.transition
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.offset
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.material.ExtendedFloatingActionButton
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.filled.ArrowDownward
diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/UserInput.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/UserInput.kt
index a0a1dbf93b..ad8646ff7a 100644
--- a/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/UserInput.kt
+++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/conversation/UserInput.kt
@@ -24,12 +24,10 @@ import androidx.compose.foundation.AmbientTextStyle
 import androidx.compose.foundation.BaseTextField
 import androidx.compose.foundation.BorderStroke
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.ScrollableRow
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.clickable
-import androidx.compose.foundation.contentColor
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -49,12 +47,12 @@ import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.Button
 import androidx.compose.material.ButtonConstants
 import androidx.compose.material.Divider
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
 import androidx.compose.material.Surface
 import androidx.compose.material.TextButton
-import androidx.compose.material.contentColorFor
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.AlternateEmail
 import androidx.compose.material.icons.outlined.Duo
@@ -309,27 +307,22 @@ private fun UserInputSelector(
         }
         Spacer(modifier = Modifier.weight(1f))
 
-        val backgroundColor = ButtonConstants.defaultButtonBackgroundColor(
-            enabled = sendMessageEnabled,
-            disabledColor = MaterialTheme.colors.surface
-        )
         val disabledContentColor =
             AmbientEmphasisLevels.current.disabled.applyEmphasis(MaterialTheme.colors.onSurface)
-        val contentColor = ButtonConstants.defaultButtonContentColor(
-            enabled = sendMessageEnabled,
-            defaultColor = contentColorFor(backgroundColor),
-            disabledColor = disabledContentColor
+
+        val buttonColors = ButtonConstants.defaultButtonColors(
+            disabledBackgroundColor = MaterialTheme.colors.surface,
+            disabledContentColor = disabledContentColor
         )
+
         // Send button
         Button(
             modifier = Modifier
                 .padding(horizontal = 16.dp)
                 .preferredHeight(36.dp),
-            elevation = 0.dp,
             enabled = sendMessageEnabled,
             onClick = onMessageSent,
-            contentColor = contentColor,
-            backgroundColor = backgroundColor,
+            colors = buttonColors,
             border = border,
             // TODO: Workaround for https://issuetracker.google.com/158830170
             contentPadding = PaddingValues(0.dp)
@@ -486,25 +479,20 @@ fun ExtendedSelectorInnerButton(
     selected: Boolean,
     modifier: Modifier = Modifier
 ) {
-    val backgroundColor = if (selected) {
-        MaterialTheme.colors.onSurface.copy(alpha = 0.08f)
-    } else {
-        // Same as background
-        getSelectorExpandedColor()
-    }
-    val color = if (selected) {
-        MaterialTheme.colors.onSurface
-    } else {
-        MaterialTheme.colors.onSurface.copy(alpha = 0.74f)
-    }
+    val colors = ButtonConstants.defaultButtonColors(
+        backgroundColor = MaterialTheme.colors.onSurface.copy(alpha = 0.08f),
+        disabledBackgroundColor = getSelectorExpandedColor(), // Same as background
+        contentColor = MaterialTheme.colors.onSurface,
+        disabledContentColor = MaterialTheme.colors.onSurface.copy(alpha = 0.74f)
+    )
     TextButton(
         onClick = onClick,
         modifier = modifier
             .padding(horizontal = 8.dp, vertical = 8.dp)
             .preferredHeight(30.dp),
         shape = MaterialTheme.shapes.medium,
-        backgroundColor = backgroundColor,
-        contentColor = color,
+        enabled = selected,
+        colors = colors,
         // TODO: Workaround for https://issuetracker.google.com//158830170
         contentPadding = PaddingValues(0.dp)
     ) {
diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/profile/Profile.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/profile/Profile.kt
index d2b20a9bd6..67c65bf2d1 100644
--- a/Jetchat/app/src/main/java/com/example/compose/jetchat/profile/Profile.kt
+++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/profile/Profile.kt
@@ -16,7 +16,6 @@
 
 package com.example.compose.jetchat.profile
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.ScrollableColumn
@@ -37,6 +36,7 @@ import androidx.compose.foundation.rememberScrollState
 import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.Divider
 import androidx.compose.material.FloatingActionButton
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
 import androidx.compose.material.Surface
diff --git a/Jetchat/app/src/main/java/com/example/compose/jetchat/theme/Color.kt b/Jetchat/app/src/main/java/com/example/compose/jetchat/theme/Color.kt
index c14d855daa..98cbe18a4a 100644
--- a/Jetchat/app/src/main/java/com/example/compose/jetchat/theme/Color.kt
+++ b/Jetchat/app/src/main/java/com/example/compose/jetchat/theme/Color.kt
@@ -16,13 +16,12 @@
 
 package com.example.compose.jetchat.theme
 
+import androidx.compose.material.AmbientElevationOverlay
 import androidx.compose.material.Colors
-import androidx.compose.material.Surface
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.compositeOver
 import androidx.compose.ui.unit.Dp
-import kotlin.math.ln
 
 /**
  * Return the fully opaque color that results from compositing [onSurface] atop [surface] with the
@@ -34,15 +33,12 @@ fun Colors.compositedOnSurface(alpha: Float): Color {
 }
 
 /**
- * Elevation overlay logic copied from [Surface] — https://issuetracker.google.com/155181601
+ * Calculates the color of an elevated `surface` in dark mode. Returns `surface` in light mode.
  */
+@Composable
 fun Colors.elevatedSurface(elevation: Dp): Color {
-    if (isLight) return surface
-    val foreground = calculateForeground(elevation)
-    return foreground.compositeOver(surface)
-}
-
-private fun calculateForeground(elevation: Dp): Color {
-    val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
-    return Color.White.copy(alpha = alpha)
+    return AmbientElevationOverlay.current?.apply(
+        color = this.surface,
+        elevation = elevation
+    ) ?: this.surface
 }
diff --git a/Jetchat/build.gradle b/Jetchat/build.gradle
index 64522a765b..b487e2c1e0 100644
--- a/Jetchat/build.gradle
+++ b/Jetchat/build.gradle
@@ -32,7 +32,7 @@ buildscript {
 }
 
 plugins {
-    id 'com.diffplug.spotless' version '5.1.1'
+    id 'com.diffplug.spotless' version '5.7.0'
 }
 
 subprojects {
diff --git a/Jetchat/buildSrc/src/main/java/com/example/compose/jetchat/buildsrc/dependencies.kt b/Jetchat/buildSrc/src/main/java/com/example/compose/jetchat/buildsrc/dependencies.kt
index 684053d3d8..6f71928609 100644
--- a/Jetchat/buildSrc/src/main/java/com/example/compose/jetchat/buildsrc/dependencies.kt
+++ b/Jetchat/buildSrc/src/main/java/com/example/compose/jetchat/buildsrc/dependencies.kt
@@ -21,7 +21,7 @@ object Versions {
 }
 
 object Libs {
-    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha13"
+    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha15"
     const val jdkDesugar = "com.android.tools:desugar_jdk_libs:1.0.9"
 
     const val junit = "junit:junit:4.13"
@@ -48,7 +48,7 @@ object Libs {
 
         object Compose {
             const val snapshot = ""
-            const val version = "1.0.0-alpha05"
+            const val version = "1.0.0-alpha06"
 
             const val foundation = "androidx.compose.foundation:foundation:$version"
             const val layout = "androidx.compose.foundation:foundation-layout:$version"
@@ -90,6 +90,6 @@ object Libs {
 }
 
 object Urls {
-    const val composeSnapshotRepo = "https://androidx-dev-prod.appspot.com/snapshots/builds/" +
-        "${Libs.AndroidX.Compose.snapshot}/artifacts/ui/repository/"
+    const val composeSnapshotRepo = "https://androidx.dev/snapshots/builds/" +
+        "${Libs.AndroidX.Compose.snapshot}/artifacts/repository/"
 }
diff --git a/Jetchat/gradle.properties b/Jetchat/gradle.properties
index 28624fcf4a..9299bc6d0f 100644
--- a/Jetchat/gradle.properties
+++ b/Jetchat/gradle.properties
@@ -19,26 +19,21 @@
 # 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=-Xmx2048m
 
-# 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
+# Turn on parallel compilation, caching and on-demand configuration
 org.gradle.configureondemand=true
 org.gradle.caching=true
 org.gradle.parallel=true
 
 # AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app"s APK
+# Android operating system, and which are packaged with your app's APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
 android.useAndroidX=true
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
+
 # Kotlin code style for this project: "official" or "obsolete":
-kotlin.code.style=official
\ No newline at end of file
+kotlin.code.style=official
diff --git a/Jetchat/gradle/wrapper/gradle-wrapper.properties b/Jetchat/gradle/wrapper/gradle-wrapper.properties
index 1737975991..436aaff960 100644
--- a/Jetchat/gradle/wrapper/gradle-wrapper.properties
+++ b/Jetchat/gradle/wrapper/gradle-wrapper.properties
@@ -19,4 +19,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/model/SnackCollection.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/model/SnackCollection.kt
index a7d0d90aaf..f0fb1817e5 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/model/SnackCollection.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/model/SnackCollection.kt
@@ -35,47 +35,54 @@ object SnackRepo {
     fun getSnacks(): List<SnackCollection> = snackCollections
     fun getSnack(snackId: Long) = snacks.find { it.id == snackId }!!
     fun getRelated(@Suppress("UNUSED_PARAMETER") snackId: Long) = related
+    fun getInspiredByCart() = inspiredByCart
     fun getFilters() = filters
+    fun getCart() = cart
 }
 
 /**
  * Static data
  */
 
-val tastyTreats = SnackCollection(
+private val tastyTreats = SnackCollection(
     id = 1L,
     name = "Android's picks",
     type = CollectionType.Highlight,
     snacks = snacks.subList(0, 13)
 )
 
-val popular = SnackCollection(
+private val popular = SnackCollection(
     id = 2L,
     name = "Popular on Jetsnack",
     snacks = snacks.subList(14, 19)
 )
 
-val wfhFavs = tastyTreats.copy(
+private val wfhFavs = tastyTreats.copy(
     id = 3L,
     name = "WFH favourites"
 )
 
-val newlyAdded = popular.copy(
+private val newlyAdded = popular.copy(
     id = 4L,
     name = "Newly Added"
 )
 
-val exclusive = tastyTreats.copy(
+private val exclusive = tastyTreats.copy(
     id = 5L,
     name = "Only on Jetsnack"
 )
 
-val also = tastyTreats.copy(
+private val also = tastyTreats.copy(
     id = 6L,
     name = "Customers also bought"
 )
 
-val snackCollections = listOf(
+private val inspiredByCart = tastyTreats.copy(
+    id = 7L,
+    name = "Inspired by your cart"
+)
+
+private val snackCollections = listOf(
     tastyTreats,
     popular,
     wfhFavs,
@@ -83,7 +90,9 @@ val snackCollections = listOf(
     exclusive
 )
 
-val related = listOf(
+private val related = listOf(
     also,
     popular
 )
+
+private val cart = snacks.subList(4, 7)
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Filters.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Filters.kt
index bde3ed3503..1dd037692b 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Filters.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Filters.kt
@@ -17,7 +17,6 @@
 package com.example.jetsnack.ui.components
 
 import androidx.compose.animation.animate
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.ScrollableRow
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.Box
@@ -28,6 +27,7 @@ import androidx.compose.foundation.layout.preferredHeightIn
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.selection.toggleable
 import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.icons.Icons
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/GradientTintedIconButton.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/GradientTintedIconButton.kt
index 7952b11817..cd499873c0 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/GradientTintedIconButton.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/GradientTintedIconButton.kt
@@ -16,7 +16,7 @@
 
 package com.example.jetsnack.ui.components
 
-import androidx.compose.foundation.Icon
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.Modifier
@@ -29,11 +29,12 @@ import com.example.jetsnack.ui.theme.JetsnackTheme
 fun JetsnackGradientTintedIconButton(
     asset: VectorAsset,
     onClick: () -> Unit,
+    modifier: Modifier = Modifier,
     colors: List<Color> = JetsnackTheme.colors.interactiveSecondary
 ) {
     // This should use a layer + srcIn but needs investigation
     val blendMode = if (JetsnackTheme.colors.isDark) BlendMode.Darken else BlendMode.Plus
-    IconButton(onClick = onClick) {
+    IconButton(onClick = onClick, modifier) {
         Icon(
             asset = asset,
             modifier = Modifier.diagonalGradientTint(
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/QuantitySelector.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/QuantitySelector.kt
new file mode 100644
index 0000000000..7a89a74919
--- /dev/null
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/QuantitySelector.kt
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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.jetsnack.ui.components
+
+import androidx.compose.foundation.Text
+import androidx.compose.foundation.layout.ChainStyle
+import androidx.compose.foundation.layout.ConstraintLayout
+import androidx.compose.foundation.layout.preferredWidthIn
+import androidx.compose.material.AmbientEmphasisLevels
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.ProvideEmphasis
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.AddCircleOutline
+import androidx.compose.material.icons.outlined.RemoveCircleOutline
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import androidx.ui.tooling.preview.Preview
+import com.example.jetsnack.R
+import com.example.jetsnack.ui.theme.JetsnackTheme
+
+@Composable
+fun QuantitySelector(
+    count: Int,
+    decreaseItemCount: () -> Unit,
+    increaseItemCount: () -> Unit,
+    modifier: Modifier = Modifier
+) {
+    ConstraintLayout(modifier = modifier) {
+        val (qty, minus, quantity, plus) = createRefs()
+        createHorizontalChain(qty, minus, quantity, plus, chainStyle = ChainStyle.Packed)
+        ProvideEmphasis(emphasis = AmbientEmphasisLevels.current.medium) {
+            Text(
+                text = stringResource(R.string.quantity),
+                style = MaterialTheme.typography.subtitle1,
+                color = JetsnackTheme.colors.textSecondary,
+                modifier = Modifier.constrainAs(qty) {
+                    start.linkTo(parent.start)
+                    linkTo(top = parent.top, bottom = parent.bottom)
+                }
+            )
+        }
+        JetsnackGradientTintedIconButton(
+            asset = Icons.Outlined.RemoveCircleOutline,
+            onClick = decreaseItemCount,
+            modifier = Modifier.constrainAs(minus) {
+                centerVerticallyTo(quantity)
+                linkTo(top = parent.top, bottom = parent.bottom)
+            }
+        )
+        ProvideEmphasis(emphasis = AmbientEmphasisLevels.current.high) {
+            Text(
+                text = "$count",
+                style = MaterialTheme.typography.subtitle2,
+                fontSize = 18.sp,
+                color = JetsnackTheme.colors.textPrimary,
+                textAlign = TextAlign.Center,
+                modifier = Modifier.preferredWidthIn(min = 24.dp).constrainAs(quantity) {
+                    baseline.linkTo(qty.baseline)
+                }
+            )
+        }
+        JetsnackGradientTintedIconButton(
+            asset = Icons.Outlined.AddCircleOutline,
+            onClick = increaseItemCount,
+            modifier = Modifier.constrainAs(plus) {
+                end.linkTo(parent.end)
+                centerVerticallyTo(quantity)
+                linkTo(top = parent.top, bottom = parent.bottom)
+            }
+        )
+    }
+}
+
+@Preview
+@Composable
+fun QuantitySelectorPreview() {
+    JetsnackTheme {
+        JetsnackSurface {
+            QuantitySelector(1, {}, {})
+        }
+    }
+}
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Snacks.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Snacks.kt
index 2878f3db1e..07debb35f1 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Snacks.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/components/Snacks.kt
@@ -16,7 +16,6 @@
 
 package com.example.jetsnack.ui.components
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.ScrollableRow
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.clickable
@@ -36,6 +35,7 @@ import androidx.compose.foundation.layout.wrapContentWidth
 import androidx.compose.foundation.lazy.LazyRowFor
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.icons.Icons
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Cart.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Cart.kt
index 7a13808ada..3827cc7f8a 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Cart.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Cart.kt
@@ -17,19 +17,329 @@
 package com.example.jetsnack.ui.home
 
 import androidx.compose.foundation.Text
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.ChainStyle
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ConstraintLayout
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
-import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.preferredHeight
+import androidx.compose.foundation.layout.preferredHeightIn
+import androidx.compose.foundation.layout.preferredSize
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.foundation.lazy.ExperimentalLazyDsl
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.text.LastBaseline
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Close
 import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.RectangleShape
 import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import androidx.ui.tooling.preview.Preview
 import com.example.jetsnack.R
+import com.example.jetsnack.model.Snack
+import com.example.jetsnack.model.SnackCollection
+import com.example.jetsnack.model.SnackRepo
+import com.example.jetsnack.ui.components.JetsnackButton
+import com.example.jetsnack.ui.components.JetsnackDivider
+import com.example.jetsnack.ui.components.JetsnackSurface
+import com.example.jetsnack.ui.components.QuantitySelector
+import com.example.jetsnack.ui.components.SnackCollection
+import com.example.jetsnack.ui.components.SnackImage
+import com.example.jetsnack.ui.theme.AlphaNearOpaque
+import com.example.jetsnack.ui.theme.JetsnackTheme
+import com.example.jetsnack.ui.utils.statusBarsHeight
 
 @Composable
-fun Cart(modifier: Modifier = Modifier) {
-    Text(
-        text = stringResource(R.string.home_cart),
+fun Cart(
+    onSnackClick: (Long) -> Unit,
+    modifier: Modifier = Modifier
+) {
+    val cartSnacks = remember { SnackRepo.getCart() }
+    val inspiredByCart = remember { SnackRepo.getInspiredByCart() }
+    Cart(cartSnacks, inspiredByCart, onSnackClick, modifier)
+}
+
+@Composable
+fun Cart(
+    cartSnacks: List<Snack>,
+    inspiredByCart: SnackCollection,
+    onSnackClick: (Long) -> Unit,
+    modifier: Modifier = Modifier
+) {
+    JetsnackSurface(modifier = modifier.fillMaxSize()) {
+        Box {
+            CartContent(
+                cartSnacks = cartSnacks,
+                inspiredByCart = inspiredByCart,
+                onSnackClick = onSnackClick,
+                modifier = Modifier.align(Alignment.TopCenter)
+            )
+            DestinationBar(modifier = Modifier.align(Alignment.TopCenter))
+            CheckoutBar(modifier = Modifier.align(Alignment.BottomCenter))
+        }
+    }
+}
+
+@OptIn(ExperimentalLazyDsl::class)
+@Composable
+private fun CartContent(
+    cartSnacks: List<Snack>,
+    inspiredByCart: SnackCollection,
+    onSnackClick: (Long) -> Unit,
+    modifier: Modifier = Modifier
+) {
+    LazyColumn(modifier) {
+        item {
+            Spacer(Modifier.statusBarsHeight(additional = 56.dp))
+            Text(
+                text = stringResource(R.string.cart_order_header, cartSnacks.size),
+                style = MaterialTheme.typography.h6,
+                color = JetsnackTheme.colors.brand,
+                maxLines = 1,
+                overflow = TextOverflow.Ellipsis,
+                modifier = Modifier
+                    .preferredHeightIn(min = 56.dp)
+                    .padding(horizontal = 24.dp, vertical = 4.dp)
+                    .wrapContentHeight()
+            )
+        }
+        items(cartSnacks) { cartSnack ->
+            CartItem(cartSnack, onSnackClick)
+        }
+        item {
+            SummaryItem()
+        }
+        item {
+            SnackCollection(
+                snackCollection = inspiredByCart,
+                onSnackClick = onSnackClick,
+                highlight = false
+            )
+            Spacer(Modifier.height(56.dp))
+        }
+    }
+}
+
+@Composable
+fun CartItem(
+    snack: Snack,
+    onSnackClick: (Long) -> Unit,
+    modifier: Modifier = Modifier
+) {
+    val (count, updateCount) = remember { mutableStateOf(1) }
+    ConstraintLayout(
         modifier = modifier
-            .fillMaxSize()
-            .wrapContentSize()
-    )
+            .fillMaxWidth()
+            .clickable { onSnackClick(snack.id) }
+            .padding(horizontal = 24.dp)
+    ) {
+        val (divider, image, name, tag, priceSpacer, price, remove, quantity) = createRefs()
+        createVerticalChain(name, tag, priceSpacer, price, chainStyle = ChainStyle.Packed)
+        SnackImage(
+            imageUrl = snack.imageUrl,
+            modifier = Modifier
+                .preferredSize(100.dp)
+                .constrainAs(image) {
+                    top.linkTo(parent.top, margin = 16.dp)
+                    bottom.linkTo(parent.bottom, margin = 16.dp)
+                    start.linkTo(parent.start)
+                }
+        )
+        Text(
+            text = snack.name,
+            style = MaterialTheme.typography.subtitle1,
+            color = JetsnackTheme.colors.textSecondary,
+            modifier = Modifier.constrainAs(name) {
+                linkTo(
+                    start = image.end,
+                    startMargin = 16.dp,
+                    end = remove.start,
+                    endMargin = 16.dp,
+                    bias = 0f
+                )
+            }
+        )
+        IconButton(
+            onClick = { /* todo */ },
+            modifier = Modifier.constrainAs(remove) {
+                top.linkTo(parent.top)
+                end.linkTo(parent.end)
+            }.padding(top = 12.dp)
+        ) {
+            Icon(asset = Icons.Filled.Close, tint = JetsnackTheme.colors.brand)
+        }
+        Text(
+            text = snack.tagline,
+            style = MaterialTheme.typography.body1,
+            color = JetsnackTheme.colors.textHelp,
+            modifier = Modifier.constrainAs(tag) {
+                linkTo(
+                    start = image.end,
+                    startMargin = 16.dp,
+                    end = parent.end,
+                    endMargin = 16.dp,
+                    bias = 0f
+                )
+            }
+        )
+        Spacer(
+            Modifier
+                .preferredHeight(8.dp)
+                .constrainAs(priceSpacer) {
+                    linkTo(top = tag.bottom, bottom = price.top)
+                }
+        )
+        Text(
+            text = "$12.99",
+            style = MaterialTheme.typography.subtitle1,
+            color = JetsnackTheme.colors.textPrimary,
+            modifier = Modifier.constrainAs(price) {
+                linkTo(
+                    start = image.end,
+                    end = quantity.start,
+                    startMargin = 16.dp,
+                    endMargin = 16.dp,
+                    bias = 0f
+                )
+            }
+        )
+        QuantitySelector(
+            count = count,
+            decreaseItemCount = { if (count > 0) updateCount(count - 1) },
+            increaseItemCount = { updateCount(count + 1) },
+            modifier = Modifier.constrainAs(quantity) {
+                baseline.linkTo(price.baseline)
+                end.linkTo(parent.end)
+            }
+        )
+        JetsnackDivider(
+            Modifier.constrainAs(divider) {
+                linkTo(start = parent.start, end = parent.end)
+                top.linkTo(parent.bottom)
+            }
+        )
+    }
+}
+
+// TODO: Hoist state instead of using hard-coded total price
+@Composable
+fun SummaryItem(modifier: Modifier = Modifier) {
+    Column(modifier) {
+        Text(
+            text = stringResource(R.string.cart_summary_header),
+            style = MaterialTheme.typography.h6,
+            color = JetsnackTheme.colors.brand,
+            maxLines = 1,
+            overflow = TextOverflow.Ellipsis,
+            modifier = Modifier
+                .padding(horizontal = 24.dp)
+                .preferredHeightIn(min = 56.dp)
+                .wrapContentHeight()
+        )
+        Row(modifier = Modifier.padding(horizontal = 24.dp)) {
+            Text(
+                text = stringResource(R.string.cart_subtotal_label),
+                style = MaterialTheme.typography.body1,
+                modifier = Modifier.weight(1f)
+                    .wrapContentWidth(Alignment.Start)
+                    .alignBy(LastBaseline)
+            )
+            Text(
+                text = "\$27.47",
+                style = MaterialTheme.typography.body1,
+                modifier = Modifier.alignBy(LastBaseline)
+            )
+        }
+        Row(modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
+            Text(
+                text = stringResource(R.string.cart_shipping_label),
+                style = MaterialTheme.typography.body1,
+                modifier = Modifier.weight(1f)
+                    .wrapContentWidth(Alignment.Start)
+                    .alignBy(LastBaseline)
+            )
+            Text(
+                text = "\$3.69",
+                style = MaterialTheme.typography.body1,
+                modifier = Modifier.alignBy(LastBaseline)
+            )
+        }
+        Spacer(modifier = Modifier.preferredHeight(8.dp))
+        JetsnackDivider()
+        Row(modifier = Modifier.padding(horizontal = 24.dp, vertical = 8.dp)) {
+            Text(
+                text = stringResource(R.string.cart_total_label),
+                style = MaterialTheme.typography.body1,
+                modifier = Modifier
+                    .weight(1f)
+                    .padding(end = 16.dp)
+                    .wrapContentWidth(Alignment.End)
+                    .alignBy(LastBaseline)
+            )
+            Text(
+                text = "\$31.16",
+                style = MaterialTheme.typography.subtitle1,
+                modifier = Modifier.alignBy(LastBaseline)
+            )
+        }
+        JetsnackDivider()
+    }
+}
+
+@Composable
+private fun CheckoutBar(modifier: Modifier = Modifier) {
+    Column(
+        modifier.background(
+            JetsnackTheme.colors.uiBackground.copy(alpha = AlphaNearOpaque)
+        )
+    ) {
+        JetsnackDivider()
+        Row {
+            Spacer(Modifier.weight(1f))
+            JetsnackButton(
+                onClick = { /* todo */ },
+                shape = RectangleShape,
+                modifier = Modifier
+                    .padding(horizontal = 12.dp, vertical = 8.dp)
+                    .weight(1f)
+            ) {
+                Text(
+                    text = stringResource(id = R.string.cart_checkout)
+                )
+            }
+        }
+    }
+}
+
+@Preview("Cart")
+@Composable
+fun CartPreview() {
+    JetsnackTheme {
+        Cart(onSnackClick = { })
+    }
+}
+
+@Preview("Cart • Dark Theme")
+@Composable
+fun CartDarkPreview() {
+    JetsnackTheme(darkTheme = true) {
+        Cart(onSnackClick = { })
+    }
 }
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/DestinationBar.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/DestinationBar.kt
new file mode 100644
index 0000000000..eeafa68b34
--- /dev/null
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/DestinationBar.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     https://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.jetsnack.ui.home
+
+import androidx.compose.foundation.Text
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material.Icon
+import androidx.compose.material.IconButton
+import androidx.compose.material.MaterialTheme
+import androidx.compose.material.TopAppBar
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.ExpandMore
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
+import androidx.compose.ui.unit.dp
+import com.example.jetsnack.ui.components.JetsnackDivider
+import com.example.jetsnack.ui.theme.AlphaNearOpaque
+import com.example.jetsnack.ui.theme.JetsnackTheme
+import com.example.jetsnack.ui.utils.statusBarsPadding
+
+@Composable
+fun DestinationBar(modifier: Modifier = Modifier) {
+    Column(modifier = modifier.statusBarsPadding()) {
+        TopAppBar(
+            backgroundColor = JetsnackTheme.colors.uiBackground.copy(alpha = AlphaNearOpaque),
+            contentColor = JetsnackTheme.colors.textSecondary,
+            elevation = 0.dp
+        ) {
+            Text(
+                text = "Delivery to 1600 Amphitheater Way",
+                style = MaterialTheme.typography.subtitle1,
+                color = JetsnackTheme.colors.textSecondary,
+                textAlign = TextAlign.Center,
+                maxLines = 1,
+                overflow = TextOverflow.Ellipsis,
+                modifier = Modifier
+                    .weight(1f)
+                    .align(Alignment.CenterVertically)
+            )
+            IconButton(
+                onClick = { /* todo */ },
+                modifier = Modifier.align(Alignment.CenterVertically)
+            ) {
+                Icon(
+                    asset = Icons.Outlined.ExpandMore,
+                    tint = JetsnackTheme.colors.brand
+                )
+            }
+        }
+        JetsnackDivider()
+    }
+}
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Feed.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Feed.kt
index 13f5865b0d..2b83ea5dd1 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Feed.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Feed.kt
@@ -16,25 +16,14 @@
 
 package com.example.jetsnack.ui.home
 
-import androidx.compose.foundation.Icon
-import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.lazy.ExperimentalLazyDsl
 import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.material.IconButton
-import androidx.compose.material.MaterialTheme
-import androidx.compose.material.TopAppBar
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.ExpandMore
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.remember
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
-import androidx.compose.ui.text.style.TextAlign
-import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.unit.dp
 import androidx.ui.tooling.preview.Preview
 import com.example.jetsnack.model.Filter
@@ -44,10 +33,8 @@ import com.example.jetsnack.ui.components.FilterBar
 import com.example.jetsnack.ui.components.JetsnackDivider
 import com.example.jetsnack.ui.components.JetsnackSurface
 import com.example.jetsnack.ui.components.SnackCollection
-import com.example.jetsnack.ui.theme.AlphaNearOpaque
 import com.example.jetsnack.ui.theme.JetsnackTheme
 import com.example.jetsnack.ui.utils.statusBarsHeight
-import com.example.jetsnack.ui.utils.statusBarsPadding
 
 @Composable
 fun Feed(
@@ -105,39 +92,6 @@ private fun SnackCollectionList(
     }
 }
 
-@Composable
-private fun DestinationBar(modifier: Modifier = Modifier) {
-    Column(modifier = modifier.statusBarsPadding()) {
-        TopAppBar(
-            backgroundColor = JetsnackTheme.colors.uiBackground.copy(alpha = AlphaNearOpaque),
-            contentColor = JetsnackTheme.colors.textSecondary,
-            elevation = 0.dp
-        ) {
-            Text(
-                text = "Delivery to 1600 Amphitheater Way",
-                style = MaterialTheme.typography.subtitle1,
-                color = JetsnackTheme.colors.textSecondary,
-                textAlign = TextAlign.Center,
-                maxLines = 1,
-                overflow = TextOverflow.Ellipsis,
-                modifier = Modifier
-                    .weight(1f)
-                    .align(Alignment.CenterVertically)
-            )
-            IconButton(
-                onClick = { /* todo */ },
-                modifier = Modifier.align(Alignment.CenterVertically)
-            ) {
-                Icon(
-                    asset = Icons.Outlined.ExpandMore,
-                    tint = JetsnackTheme.colors.brand
-                )
-            }
-        }
-        JetsnackDivider()
-    }
-}
-
 @Preview("Home")
 @Composable
 fun HomePreview() {
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Home.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Home.kt
index e517c4df46..796a96f877 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Home.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/Home.kt
@@ -24,7 +24,6 @@ import androidx.compose.animation.animate
 import androidx.compose.animation.animatedFloat
 import androidx.compose.animation.core.AnimationSpec
 import androidx.compose.animation.core.SpringSpec
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.border
 import androidx.compose.foundation.layout.Box
@@ -35,6 +34,7 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.selection.selectable
 import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.AccountCircle
@@ -94,7 +94,7 @@ fun Home(onSnackSelected: (Long) -> Unit) {
                     modifier = modifier
                 )
                 HomeSections.Search -> Search(onSnackSelected, modifier)
-                HomeSections.Cart -> Cart(modifier)
+                HomeSections.Cart -> Cart(onSnackSelected, modifier)
                 HomeSections.Profile -> Profile(modifier)
             }
         }
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Results.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Results.kt
index 0254320b27..e0d650d780 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Results.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Results.kt
@@ -16,7 +16,6 @@
 
 package com.example.jetsnack.ui.home.search
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.clickable
@@ -33,6 +32,7 @@ import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.foundation.lazy.LazyColumnForIndexed
 import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.icons.Icons
 import androidx.compose.material.icons.outlined.Add
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Search.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Search.kt
index 970e8ef761..0b1f883612 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Search.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/home/search/Search.kt
@@ -18,7 +18,6 @@ package com.example.jetsnack.ui.home.search
 
 import androidx.compose.foundation.BaseTextField
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.contentColor
 import androidx.compose.foundation.layout.Box
@@ -34,6 +33,7 @@ import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.layout.wrapContentHeight
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material.CircularProgressIndicator
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.icons.Icons
diff --git a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/snackdetail/SnackDetail.kt b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/snackdetail/SnackDetail.kt
index 5f5c84d649..b9fe76cb5f 100644
--- a/Jetsnack/app/src/main/java/com/example/jetsnack/ui/snackdetail/SnackDetail.kt
+++ b/Jetsnack/app/src/main/java/com/example/jetsnack/ui/snackdetail/SnackDetail.kt
@@ -16,7 +16,6 @@
 
 package com.example.jetsnack.ui.snackdetail
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.ScrollState
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.Text
@@ -33,15 +32,13 @@ import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredHeightIn
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.preferredWidth
-import androidx.compose.foundation.layout.preferredWidthIn
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.outlined.AddCircleOutline
 import androidx.compose.material.icons.outlined.ArrowBack
-import androidx.compose.material.icons.outlined.RemoveCircleOutline
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.key
 import androidx.compose.runtime.mutableStateOf
@@ -52,7 +49,6 @@ import androidx.compose.ui.Modifier
 import androidx.compose.ui.drawLayer
 import androidx.compose.ui.platform.DensityAmbient
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.Constraints
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.lerp
@@ -65,8 +61,8 @@ import com.example.jetsnack.model.SnackCollection
 import com.example.jetsnack.model.SnackRepo
 import com.example.jetsnack.ui.components.JetsnackButton
 import com.example.jetsnack.ui.components.JetsnackDivider
-import com.example.jetsnack.ui.components.JetsnackGradientTintedIconButton
 import com.example.jetsnack.ui.components.JetsnackSurface
+import com.example.jetsnack.ui.components.QuantitySelector
 import com.example.jetsnack.ui.components.SnackCollection
 import com.example.jetsnack.ui.components.SnackImage
 import com.example.jetsnack.ui.components.horizontalGradientBackground
@@ -315,26 +311,10 @@ private fun CartBottomBar(modifier: Modifier = Modifier) {
                     .then(HzPadding)
                     .preferredHeightIn(min = BottomBarHeight)
             ) {
-                Text(
-                    text = stringResource(R.string.quantity),
-                    style = MaterialTheme.typography.subtitle1,
-                    color = JetsnackTheme.colors.textSecondary
-                )
-                JetsnackGradientTintedIconButton(
-                    asset = Icons.Outlined.RemoveCircleOutline,
-                    onClick = { if (count > 0) updateCount(count - 1) }
-                )
-                Text(
-                    text = "$count",
-                    style = MaterialTheme.typography.subtitle2,
-                    fontSize = 18.sp,
-                    color = JetsnackTheme.colors.textPrimary,
-                    textAlign = TextAlign.Center,
-                    modifier = Modifier.preferredWidthIn(min = 24.dp)
-                )
-                JetsnackGradientTintedIconButton(
-                    asset = Icons.Outlined.AddCircleOutline,
-                    onClick = { updateCount(count + 1) }
+                QuantitySelector(
+                    count = count,
+                    decreaseItemCount = { if (count > 0) updateCount(count - 1) },
+                    increaseItemCount = { updateCount(count + 1) }
                 )
                 Spacer(Modifier.preferredWidth(16.dp))
                 JetsnackButton(
diff --git a/Jetsnack/app/src/main/res/values/strings.xml b/Jetsnack/app/src/main/res/values/strings.xml
index acf19e862e..0186a5b24a 100644
--- a/Jetsnack/app/src/main/res/values/strings.xml
+++ b/Jetsnack/app/src/main/res/values/strings.xml
@@ -34,4 +34,12 @@
     <string name="ingredients_list">Vanilla, Almond Flour, Eggs, Butter, Cream, Sugar</string>
     <string name="quantity">Qty</string>
     <string name="add_to_cart">ADD TO CART</string>
+
+    <!-- Cart -->
+    <string name="cart_order_header">Order (%1d items)</string>
+    <string name="cart_summary_header">Summary</string>
+    <string name="cart_subtotal_label">Subtotal</string>
+    <string name="cart_shipping_label">Shipping &amp; Handling</string>
+    <string name="cart_total_label">Total</string>
+    <string name="cart_checkout">Checkout</string>
 </resources>
diff --git a/Jetsnack/build.gradle b/Jetsnack/build.gradle
index f5b5cd6f48..da3492a695 100644
--- a/Jetsnack/build.gradle
+++ b/Jetsnack/build.gradle
@@ -32,7 +32,7 @@ buildscript {
 }
 
 plugins {
-    id 'com.diffplug.spotless' version '5.6.1'
+    id 'com.diffplug.spotless' version '5.7.0'
 }
 
 subprojects {
@@ -42,7 +42,7 @@ subprojects {
 
         if (!Libs.AndroidX.Compose.snapshot.isEmpty()) {
             maven {
-                url "https://androidx.dev/snapshots/builds/${Libs.AndroidX.Compose.snapshot}/artifacts/ui/repository/"
+                url "https://androidx.dev/snapshots/builds/${Libs.AndroidX.Compose.snapshot}/artifacts/repository/"
             }
         }
 
diff --git a/Jetsnack/buildSrc/src/main/java/com/example/jetsnack/buildsrc/Dependencies.kt b/Jetsnack/buildSrc/src/main/java/com/example/jetsnack/buildsrc/Dependencies.kt
index 1c0c9884cb..fd6824b3ea 100644
--- a/Jetsnack/buildSrc/src/main/java/com/example/jetsnack/buildsrc/Dependencies.kt
+++ b/Jetsnack/buildSrc/src/main/java/com/example/jetsnack/buildsrc/Dependencies.kt
@@ -21,11 +21,11 @@ object Versions {
 }
 
 object Libs {
-    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha13"
+    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha15"
     const val junit = "junit:junit:4.13"
 
     object Accompanist {
-        private const val version = "0.3.1"
+        private const val version = "0.3.2"
         const val coil = "dev.chrisbanes.accompanist:accompanist-coil:$version"
     }
 
@@ -44,11 +44,11 @@ object Libs {
     }
 
     object AndroidX {
-        const val coreKtx = "androidx.core:core-ktx:1.5.0-alpha02"
+        const val coreKtx = "androidx.core:core-ktx:1.5.0-alpha04"
 
         object Compose {
             const val snapshot = ""
-            const val version = "1.0.0-alpha05"
+            const val version = "1.0.0-alpha06"
 
             const val runtime = "androidx.compose.runtime:runtime:$version"
             const val foundation = "androidx.compose.foundation:foundation:${version}"
diff --git a/Jetsnack/gradle.properties b/Jetsnack/gradle.properties
index 2ec1b001e4..9299bc6d0f 100644
--- a/Jetsnack/gradle.properties
+++ b/Jetsnack/gradle.properties
@@ -1,14 +1,31 @@
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
 # 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=-Xmx2048m
 
+# Turn on parallel compilation, caching and on-demand configuration
 org.gradle.configureondemand=true
 org.gradle.caching=true
 org.gradle.parallel=true
diff --git a/Jetsnack/gradle/wrapper/gradle-wrapper.properties b/Jetsnack/gradle/wrapper/gradle-wrapper.properties
index 12d38de6a4..be52383ef4 100644
--- a/Jetsnack/gradle/wrapper/gradle-wrapper.properties
+++ b/Jetsnack/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/EmailState.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/EmailState.kt
index 1ebe11a9a1..0933c77f20 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/EmailState.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/EmailState.kt
@@ -28,7 +28,7 @@ class EmailState :
  * Returns an error to be displayed or null if no error was found
  */
 private fun emailValidationError(email: String): String {
-    return "Invalid email"
+    return "Invalid email: $email"
 }
 
 private fun isEmailValid(email: String): Boolean {
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/PasswordState.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/PasswordState.kt
index 3aaeb6a97b..4b1ffe32db 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/PasswordState.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/PasswordState.kt
@@ -40,6 +40,7 @@ private fun isPasswordValid(password: String): Boolean {
     return password.length > 3
 }
 
+@Suppress("UNUSED_PARAMETER")
 private fun passwordValidationError(password: String): String {
     return "Invalid password"
 }
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/SignInScreen.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/SignInScreen.kt
index 4f3761b0d8..1c395ad7f5 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/SignInScreen.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/SignInScreen.kt
@@ -16,7 +16,6 @@
 
 package com.example.compose.jetsurvey.signinsignup
 
-import androidx.compose.foundation.AmbientContentColor
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
@@ -165,10 +164,7 @@ fun ErrorSnackbar(
                 },
                 action = {
                     data.actionLabel?.let {
-                        TextButton(
-                            onClick = onDismiss,
-                            contentColor = AmbientContentColor.current
-                        ) {
+                        TextButton(onClick = onDismiss) {
                             Text(
                                 text = stringResource(id = R.string.dismiss),
                                 color = MaterialTheme.colors.snackbarAction
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/SignInSignUp.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/SignInSignUp.kt
index 1209e2a7b9..6ecd41c1cd 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/SignInSignUp.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/SignInSignUp.kt
@@ -17,7 +17,6 @@
 package com.example.compose.jetsurvey.signinsignup
 
 import androidx.compose.foundation.AmbientTextStyle
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.Box
@@ -31,6 +30,7 @@ import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.layout.wrapContentSize
 import androidx.compose.material.AmbientEmphasisLevels
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedButton
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/UserRepository.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/UserRepository.kt
index ef8a9c9610..7401dc4dd0 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/UserRepository.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/signinsignup/UserRepository.kt
@@ -37,10 +37,12 @@ object UserRepository {
     val user: User
         get() = _user
 
+    @Suppress("UNUSED_PARAMETER")
     fun signIn(email: String, password: String) {
         _user = User.LoggedInUser(email)
     }
 
+    @Suppress("UNUSED_PARAMETER")
     fun signUp(email: String, password: String) {
         _user = User.LoggedInUser(email)
     }
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyFragment.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyFragment.kt
index d1808fe780..7046f59797 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyFragment.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyFragment.kt
@@ -59,8 +59,7 @@ class SurveyFragment : Fragment() {
                             )
                             is SurveyState.Result -> SurveyResultScreen(
                                 result = surveyState,
-                                onDonePressed = { activity?.onBackPressedDispatcher?.onBackPressed() },
-                                onBackPressed = { activity?.onBackPressedDispatcher?.onBackPressed() }
+                                onDonePressed = { activity?.onBackPressedDispatcher?.onBackPressed() }
                             )
                         }
                     }
@@ -87,11 +86,13 @@ class SurveyFragment : Fragment() {
         }
     }
 
+    @Suppress("UNUSED_PARAMETER")
     private fun takeAPhoto(questionId: Int) {
-        // unsupported for now
+        // TODO: unsupported for now
     }
 
+    @Suppress("UNUSED_PARAMETER")
     private fun selectContact(questionId: Int) {
-        // unsupported for now
+        // TODO: unsupported for now
     }
 }
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyQuestions.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyQuestions.kt
index 089230a765..8d687f0a6a 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyQuestions.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyQuestions.kt
@@ -18,6 +18,7 @@ package com.example.compose.jetsurvey.survey
 
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.Text
+import androidx.compose.foundation.background
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.PaddingValues
@@ -27,9 +28,12 @@ import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.selection.selectable
+import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.Button
 import androidx.compose.material.Checkbox
+import androidx.compose.material.CheckboxConstants
 import androidx.compose.material.MaterialTheme
+import androidx.compose.material.ProvideEmphasis
 import androidx.compose.material.RadioButton
 import androidx.compose.material.RadioButtonConstants
 import androidx.compose.material.Slider
@@ -42,7 +46,30 @@ import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
 import androidx.compose.ui.unit.dp
+import androidx.ui.tooling.preview.Preview
 import com.example.compose.jetsurvey.R
+import com.example.compose.jetsurvey.theme.JetsurveyTheme
+import com.example.compose.jetsurvey.theme.questionBackground
+
+@Preview
+@Composable
+fun QuestionPreview() {
+    val question = Question(
+        id = 2,
+        questionText = R.string.pick_superhero,
+        answer = PossibleAnswer.SingleChoice(
+            optionsStringRes = listOf(
+                R.string.spiderman,
+                R.string.ironman,
+                R.string.unikitty,
+                R.string.captain_planet
+            )
+        )
+    )
+    JetsurveyTheme {
+        Question(question = question, answer = null, onAnswer = {}, onAction = { _, _ -> })
+    }
+}
 
 @Composable
 fun Question(
@@ -57,11 +84,20 @@ fun Question(
         contentPadding = PaddingValues(start = 20.dp, end = 20.dp)
     ) {
         Spacer(modifier = Modifier.preferredHeight(44.dp))
-        Text(
-            text = stringResource(id = question.questionText),
-            style = MaterialTheme.typography.h4,
-            modifier = Modifier.fillMaxWidth()
-        )
+        Row(
+            modifier = Modifier.fillMaxWidth().background(
+                color = MaterialTheme.colors.questionBackground,
+                shape = MaterialTheme.shapes.small
+            )
+        ) {
+            ProvideEmphasis(emphasis = AmbientEmphasisLevels.current.high) {
+                Text(
+                    text = stringResource(id = question.questionText),
+                    style = MaterialTheme.typography.subtitle1,
+                    modifier = Modifier.fillMaxWidth().padding(vertical = 24.dp, horizontal = 16.dp)
+                )
+            }
+        }
         Spacer(modifier = Modifier.preferredHeight(24.dp))
         when (question.answer) {
             is PossibleAnswer.SingleChoice -> SingleChoiceQuestion(
@@ -141,9 +177,7 @@ private fun SingleChoiceQuestion(
                 RadioButton(
                     selected = optionSelected,
                     onClick = onClickHandle,
-                    color = RadioButtonConstants.animateDefaultColor(
-                        selected = optionSelected,
-                        enabled = true,
+                    colors = RadioButtonConstants.defaultColors(
                         selectedColor = MaterialTheme.colors.primary
                     )
                 )
@@ -188,7 +222,9 @@ private fun MultipleChoiceQuestion(
                         checkedState = selected
                         onAnswerSelected(option.value, selected)
                     },
-                    checkedColor = MaterialTheme.colors.primary
+                    colors = CheckboxConstants.defaultColors(
+                        checkedColor = MaterialTheme.colors.primary
+                    )
                 )
                 Text(
                     text = option.key,
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyRepository.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyRepository.kt
index ce294c07bc..3fb7968878 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyRepository.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyRepository.kt
@@ -87,6 +87,7 @@ object SurveyRepository {
 
     suspend fun getSurvey() = jetpackSurvey
 
+    @Suppress("UNUSED_PARAMETER")
     fun getSurveyResult(answers: List<Answer<*>>): SurveyResult {
         return SurveyResult(
             library = "Compose",
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyScreen.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyScreen.kt
index 9f9a2831f1..a524e76304 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyScreen.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyScreen.kt
@@ -16,41 +16,39 @@
 
 package com.example.compose.jetsurvey.survey
 
-import androidx.annotation.StringRes
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.Text
+import androidx.compose.foundation.layout.ConstraintLayout
+import androidx.compose.foundation.layout.ExperimentalLayout
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
 import androidx.compose.foundation.layout.fillMaxSize
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
-import androidx.compose.foundation.layout.preferredWidth
-import androidx.compose.foundation.layout.wrapContentSize
-import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.AmbientEmphasisLevels
+import androidx.compose.material.Button
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
+import androidx.compose.material.LinearProgressIndicator
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.OutlinedButton
+import androidx.compose.material.ProvideEmphasis
 import androidx.compose.material.Scaffold
 import androidx.compose.material.Surface
-import androidx.compose.material.TextButton
-import androidx.compose.material.TopAppBar
 import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.ChevronLeft
-import androidx.compose.material.icons.filled.FiberManualRecord
-import androidx.compose.material.icons.outlined.FiberManualRecord
+import androidx.compose.material.icons.filled.Close
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.savedinstancestate.savedInstanceState
 import androidx.compose.runtime.setValue
-import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import com.example.compose.jetsurvey.R
+import com.example.compose.jetsurvey.theme.progressIndicatorBackground
 
 @Composable
 fun SurveyQuestionsScreen(
@@ -65,12 +63,21 @@ fun SurveyQuestionsScreen(
 
     Surface(modifier = Modifier.fillMaxSize()) {
         Scaffold(
-            topBar = { SurveyTopAppBar(questions.surveyTitle, onBackPressed) },
+            topBar = {
+                SurveyTopAppBar(
+                    questionIndex = questionState.questionIndex,
+                    totalQuestionsCount = questionState.totalQuestionsCount,
+                    onBackPressed = onBackPressed
+                )
+            },
             bodyContent = { innerPadding ->
                 Question(
                     question = questionState.question,
                     answer = questionState.answer,
-                    onAnswer = { questionState.answer = it },
+                    onAnswer = {
+                        questionState.answer = it
+                        questionState.enableNext = true
+                    },
                     onAction = onAction,
                     modifier = Modifier
                         .fillMaxSize()
@@ -92,12 +99,10 @@ fun SurveyQuestionsScreen(
 @Composable
 fun SurveyResultScreen(
     result: SurveyState.Result,
-    onDonePressed: () -> Unit,
-    onBackPressed: () -> Unit
+    onDonePressed: () -> Unit
 ) {
     Surface(modifier = Modifier.fillMaxSize()) {
         Scaffold(
-            topBar = { SurveyTopAppBar(result.surveyTitle, onBackPressed) },
             bodyContent = { innerPadding ->
                 val modifier = Modifier.padding(innerPadding)
                 SurveyResult(result = result, modifier = modifier)
@@ -139,31 +144,46 @@ private fun SurveyResult(result: SurveyState.Result, modifier: Modifier = Modifi
     }
 }
 
+@OptIn(ExperimentalLayout::class)
 @Composable
 private fun SurveyTopAppBar(
-    @StringRes surveyTitle: Int,
+    questionIndex: Int,
+    totalQuestionsCount: Int,
     onBackPressed: () -> Unit
 ) {
-    TopAppBar(
-        title = {
-            Text(
-                text = stringResource(id = surveyTitle),
-                textAlign = TextAlign.Center,
-                modifier = Modifier.fillMaxWidth()
-            )
-        },
-        navigationIcon = {
-            IconButton(onClick = onBackPressed) {
-                Icon(Icons.Filled.ChevronLeft)
+    ConstraintLayout(modifier = Modifier.fillMaxWidth()) {
+        val (button, text, progress) = createRefs()
+        Text(
+            text = stringResource(
+                R.string.question_count,
+                questionIndex + 1,
+                totalQuestionsCount
+            ),
+            style = MaterialTheme.typography.caption,
+            modifier = Modifier.padding(vertical = 20.dp).constrainAs(text) {
+                centerHorizontallyTo(parent)
+            }
+        )
+
+        ProvideEmphasis(emphasis = AmbientEmphasisLevels.current.medium) {
+            IconButton(
+                onClick = onBackPressed,
+                modifier = Modifier.padding(horizontal = 12.dp).constrainAs(button) {
+                    end.linkTo(parent.end)
+                }
+            ) {
+                Icon(Icons.Filled.Close)
             }
-        },
-        // We need to balance the navigation icon, so we add a spacer.
-        actions = {
-            Spacer(modifier = Modifier.preferredWidth(68.dp))
-        },
-        backgroundColor = MaterialTheme.colors.surface,
-        elevation = 0.dp
-    )
+        }
+
+        LinearProgressIndicator(
+            progress = (questionIndex + 1) / totalQuestionsCount.toFloat(),
+            modifier = Modifier.fillMaxWidth().padding(horizontal = 20.dp).constrainAs(progress) {
+                bottom.linkTo(text.bottom)
+            },
+            backgroundColor = MaterialTheme.colors.progressIndicatorBackground
+        )
+    }
 }
 
 @Composable
@@ -173,53 +193,41 @@ private fun SurveyBottomBar(
     onNextPressed: () -> Unit,
     onDonePressed: () -> Unit
 ) {
-    Row(
-        modifier = Modifier
-            .fillMaxWidth()
-            .padding(start = 20.dp, end = 20.dp, bottom = 24.dp)
+    Surface(
+        elevation = 3.dp,
+        modifier = Modifier.fillMaxWidth()
     ) {
-        TextButton(
-            modifier = Modifier.weight(1f).wrapContentWidth(align = Alignment.Start),
-            onClick = onPreviousPressed,
-            enabled = questionState.enablePrevious
+        Row(
+            modifier = Modifier
+                .fillMaxWidth()
+                .padding(horizontal = 16.dp, vertical = 20.dp)
         ) {
-            Text(text = stringResource(id = R.string.previous))
-        }
-        PageIndicator(
-            pagesCount = questionState.totalQuestionsCount,
-            currentPageIndex = questionState.questionIndex
-        )
-        if (questionState.showDone) {
-            TextButton(
-                modifier = Modifier.weight(1f).wrapContentWidth(align = Alignment.End),
-                onClick = onDonePressed
-            ) {
-                Text(text = stringResource(id = R.string.done))
-            }
-        } else {
-            TextButton(
-                modifier = Modifier.weight(1f).wrapContentWidth(align = Alignment.End),
-                onClick = onNextPressed
-            ) {
-                Text(text = stringResource(id = R.string.next))
+            if (questionState.showPrevious) {
+                OutlinedButton(
+                    modifier = Modifier.weight(1f),
+                    onClick = onPreviousPressed
+                ) {
+                    Text(text = stringResource(id = R.string.previous))
+                }
+                Spacer(modifier = Modifier.width(16.dp))
             }
-        }
-    }
-}
-
-@Composable
-private fun PageIndicator(pagesCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) {
-    Row(modifier = modifier.wrapContentSize(align = Alignment.Center)) {
-        for (pageIndex in 0 until pagesCount) {
-            val asset = if (currentPageIndex == pageIndex) {
-                Icons.Filled.FiberManualRecord
+            if (questionState.showDone) {
+                Button(
+                    modifier = Modifier.weight(1f),
+                    onClick = onDonePressed,
+                    enabled = questionState.enableNext
+                ) {
+                    Text(text = stringResource(id = R.string.done))
+                }
             } else {
-                Icons.Outlined.FiberManualRecord
+                Button(
+                    modifier = Modifier.weight(1f),
+                    onClick = onNextPressed,
+                    enabled = questionState.enableNext
+                ) {
+                    Text(text = stringResource(id = R.string.next))
+                }
             }
-            Icon(
-                asset = asset,
-                tint = MaterialTheme.colors.primary
-            )
         }
     }
 }
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyState.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyState.kt
index 8267a41bca..c457901ccb 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyState.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyState.kt
@@ -27,9 +27,10 @@ class QuestionState(
     val question: Question,
     val questionIndex: Int,
     val totalQuestionsCount: Int,
-    val enablePrevious: Boolean,
+    val showPrevious: Boolean,
     val showDone: Boolean
 ) {
+    var enableNext by mutableStateOf(false)
     var answer by mutableStateOf<Answer<*>?>(null)
 }
 
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyViewModel.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyViewModel.kt
index 64928ab636..944c32dacc 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyViewModel.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyViewModel.kt
@@ -37,9 +37,15 @@ class SurveyViewModel(private val surveyRepository: SurveyRepository) : ViewMode
 
             // Create the default questions state based on the survey questions
             val questions: List<QuestionState> = survey.questions.mapIndexed { index, question ->
-                val enablePrevious = index > 0
+                val showPrevious = index > 0
                 val showDone = index == survey.questions.size - 1
-                QuestionState(question, index, survey.questions.size, enablePrevious, showDone)
+                QuestionState(
+                    question = question,
+                    questionIndex = index,
+                    totalQuestionsCount = survey.questions.size,
+                    showPrevious = showPrevious,
+                    showDone = showDone
+                )
             }
             surveyInitialState = SurveyState.Questions(survey.title, questions)
             _uiState.value = surveyInitialState
@@ -64,6 +70,7 @@ class SurveyViewModel(private val surveyRepository: SurveyRepository) : ViewMode
                     questionState.question.id == questionId
                 }
             question.answer = Answer.Action(result)
+            question.enableNext = true
         }
     }
 }
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/theme/Color.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/theme/Color.kt
index 5d82e95450..5efe51c9c8 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/theme/Color.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/theme/Color.kt
@@ -25,3 +25,6 @@ val Purple800 = Color(0xFF0000E1)
 
 val Red300 = Color(0xFFD00036)
 val Red800 = Color(0xFFEA6D7E)
+
+val Gray100 = Color(0xFFF5F5F5)
+val Gray900 = Color(0xFF212121)
diff --git a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/theme/Theme.kt b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/theme/Theme.kt
index fbf84bda35..748aa869e8 100644
--- a/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/theme/Theme.kt
+++ b/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/theme/Theme.kt
@@ -56,6 +56,14 @@ val DarkThemeColors = darkColors(
 val Colors.snackbarAction: Color
     get() = if (isLight) Purple300 else Purple700
 
+@Composable
+val Colors.progressIndicatorBackground: Color
+    get() = if (isLight) Color.Black.copy(alpha = 0.12f) else Color.Black.copy(alpha = 0.24f)
+
+@Composable
+val Colors.questionBackground: Color
+    get() = if (isLight) Gray100 else Gray900
+
 @Composable
 fun JetsurveyTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
     val colors = if (darkTheme) {
diff --git a/Jetsurvey/app/src/main/res/values/strings.xml b/Jetsurvey/app/src/main/res/values/strings.xml
index db80f73f23..368cc33b99 100644
--- a/Jetsurvey/app/src/main/res/values/strings.xml
+++ b/Jetsurvey/app/src/main/res/values/strings.xml
@@ -21,22 +21,24 @@
     <string name="password">Password</string>
     <string name="confirm_password">Confirm password</string>
     <string name="sign_in">Sign in</string>
-    <string name="sign_in_guest">Sign in as guest</string>
+    <string name="sign_in_action">SIGN IN</string>
+    <string name="sign_in_guest">SIGN IN AS GUEST</string>
     <string name="sign_in_create_account">Sign in or create an account</string>
     <string name="or">or</string>
-    <string name="forgot_password">Forgot password</string>
-    <string name="user_continue">Continue</string>
-    <string name="create_account">Create account</string>
+    <string name="forgot_password">FORGOT PASSWORD?</string>
+    <string name="user_continue">CONTINUE</string>
+    <string name="create_account">CREATE ACCOUNT</string>
     <string name="terms_and_conditions">By continuing, you agree to our Terms of Service. We’ll
         handle your data according to our Privacy Policy.</string>
     <string name="feature_not_available">Feature not available</string>
-    <string name="dismiss">Dismiss</string>
-    <string name="next">Next</string>
-    <string name="previous">Previous</string>
-    <string name="done">Done</string>
+    <string name="dismiss">DISMISS</string>
+    <string name="next">NEXT</string>
+    <string name="previous">PREVIOUS</string>
+    <string name="done">DONE</string>
 
     <!-- survey-->
     <string name="which_jetpack_library">Which Jetpack library are you?</string>
+    <string name="question_count">%1$d of %2$d</string>
 
     <!-- question 1-->
     <string name="in_my_free_time">In my free time I like to …</string>
diff --git a/Jetsurvey/build.gradle b/Jetsurvey/build.gradle
index 8963493a05..21f2f12519 100644
--- a/Jetsurvey/build.gradle
+++ b/Jetsurvey/build.gradle
@@ -29,7 +29,7 @@ buildscript {
 }
 
 plugins {
-    id 'com.diffplug.spotless' version '5.1.1'
+    id 'com.diffplug.spotless' version '5.7.0'
 }
 
 subprojects {
@@ -57,6 +57,9 @@ subprojects {
 
     tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach {
         kotlinOptions {
+            // Treat all Kotlin warnings as errors
+            allWarningsAsErrors = true
+
             freeCompilerArgs += '-Xopt-in=kotlin.RequiresOptIn'
             freeCompilerArgs += '-Xallow-jvm-ir-dependencies'
 
diff --git a/Jetsurvey/buildSrc/src/main/java/com/example/compose/jetsurvey/buildsrc/dependencies.kt b/Jetsurvey/buildSrc/src/main/java/com/example/compose/jetsurvey/buildsrc/dependencies.kt
index 9e08760240..d7ae38de39 100644
--- a/Jetsurvey/buildSrc/src/main/java/com/example/compose/jetsurvey/buildsrc/dependencies.kt
+++ b/Jetsurvey/buildSrc/src/main/java/com/example/compose/jetsurvey/buildsrc/dependencies.kt
@@ -17,11 +17,11 @@
 package com.example.compose.jetsurvey.buildsrc
 
 object Versions {
-    const val ktlint = "0.38.1"
+    const val ktlint = "0.39.0"
 }
 
 object Libs {
-    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha13"
+    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha15"
     const val jdkDesugar = "com.android.tools:desugar_jdk_libs:1.0.9"
 
     const val junit = "junit:junit:4.13"
@@ -41,11 +41,11 @@ object Libs {
 
         object Compose {
             const val snapshot = ""
-            const val version = "1.0.0-alpha05"
+            const val version = "1.0.0-alpha06"
 
             @get:JvmStatic
             val snapshotUrl: String
-                get() = "https://androidx.dev/snapshots/builds/$snapshot/artifacts/ui/repository/"
+                get() = "https://androidx.dev/snapshots/builds/$snapshot/artifacts/repository/"
 
             const val foundation = "androidx.compose.foundation:foundation:$version"
             const val layout = "androidx.compose.foundation:foundation-layout:$version"
diff --git a/Jetsurvey/gradle.properties b/Jetsurvey/gradle.properties
index 9bb1cb21f9..9299bc6d0f 100644
--- a/Jetsurvey/gradle.properties
+++ b/Jetsurvey/gradle.properties
@@ -1,4 +1,21 @@
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
 # Project-wide Gradle settings.
+
 # IDE (e.g. Android Studio) users:
 # Gradle settings configured through the IDE *will override*
 # any settings specified in this file.
@@ -7,15 +24,16 @@
 # Specifies the JVM arguments used for the daemon process.
 # The setting is particularly useful for tweaking memory settings.
 org.gradle.jvmargs=-Xmx2048m
-# 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
+
+# Turn on parallel compilation, caching and on-demand configuration
+org.gradle.configureondemand=true
+org.gradle.caching=true
+org.gradle.parallel=true
+
 # AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app"s APK
+# Android operating system, and which are packaged with your app's APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
 android.useAndroidX=true
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
+
 # Kotlin code style for this project: "official" or "obsolete":
 kotlin.code.style=official
diff --git a/Jetsurvey/gradle/wrapper/gradle-wrapper.properties b/Jetsurvey/gradle/wrapper/gradle-wrapper.properties
index e194248109..9a517b7c1a 100644
--- a/Jetsurvey/gradle/wrapper/gradle-wrapper.properties
+++ b/Jetsurvey/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
diff --git a/Owl/app/src/main/java/com/example/owl/ui/common/CourseListItem.kt b/Owl/app/src/main/java/com/example/owl/ui/common/CourseListItem.kt
index 7a74b9ef2f..a3de83fbb6 100644
--- a/Owl/app/src/main/java/com/example/owl/ui/common/CourseListItem.kt
+++ b/Owl/app/src/main/java/com/example/owl/ui/common/CourseListItem.kt
@@ -16,7 +16,6 @@
 
 package com.example.owl.ui.common
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Column
@@ -28,6 +27,7 @@ import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.wrapContentWidth
 import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Surface
 import androidx.compose.material.icons.Icons
diff --git a/Owl/app/src/main/java/com/example/owl/ui/course/CourseDetails.kt b/Owl/app/src/main/java/com/example/owl/ui/course/CourseDetails.kt
index 43291465b8..1046aa011d 100644
--- a/Owl/app/src/main/java/com/example/owl/ui/course/CourseDetails.kt
+++ b/Owl/app/src/main/java/com/example/owl/ui/course/CourseDetails.kt
@@ -17,7 +17,6 @@
 package com.example.owl.ui.course
 
 import androidx.compose.animation.animate
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.ScrollableRow
@@ -41,6 +40,7 @@ import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.Divider
 import androidx.compose.material.ExperimentalMaterialApi
 import androidx.compose.material.FractionalThreshold
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
diff --git a/Owl/app/src/main/java/com/example/owl/ui/courses/Courses.kt b/Owl/app/src/main/java/com/example/owl/ui/courses/Courses.kt
index d1786663e6..e8c92b7eae 100644
--- a/Owl/app/src/main/java/com/example/owl/ui/courses/Courses.kt
+++ b/Owl/app/src/main/java/com/example/owl/ui/courses/Courses.kt
@@ -19,13 +19,13 @@ package com.example.owl.ui.courses
 import androidx.annotation.DrawableRes
 import androidx.annotation.StringRes
 import androidx.compose.foundation.AmbientContentColor
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.material.BottomNavigation
 import androidx.compose.material.BottomNavigationItem
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Scaffold
diff --git a/Owl/app/src/main/java/com/example/owl/ui/courses/FeaturedCourses.kt b/Owl/app/src/main/java/com/example/owl/ui/courses/FeaturedCourses.kt
index 0bc6cbffc2..215797d4cc 100644
--- a/Owl/app/src/main/java/com/example/owl/ui/courses/FeaturedCourses.kt
+++ b/Owl/app/src/main/java/com/example/owl/ui/courses/FeaturedCourses.kt
@@ -16,7 +16,6 @@
 
 package com.example.owl.ui.courses
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.clickable
@@ -25,6 +24,8 @@ import androidx.compose.foundation.layout.ExperimentalLayout
 import androidx.compose.foundation.layout.aspectRatio
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredSize
+import androidx.compose.material.AmbientElevationOverlay
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Surface
 import androidx.compose.material.icons.Icons
@@ -41,7 +42,6 @@ import com.example.owl.model.courses
 import com.example.owl.ui.common.OutlinedAvatar
 import com.example.owl.ui.theme.BlueTheme
 import com.example.owl.ui.theme.OwlTheme
-import com.example.owl.ui.theme.elevatedSurface
 import com.example.owl.ui.utils.NetworkImage
 import com.example.owl.ui.utils.statusBarsPadding
 import kotlin.math.ceil
@@ -93,11 +93,13 @@ fun FeaturedCourse(
                         top.linkTo(parent.top)
                     }
             )
+            val outlineColor = AmbientElevationOverlay.current?.apply(
+                color = MaterialTheme.colors.surface,
+                elevation = OwlTheme.elevations.card
+            ) ?: MaterialTheme.colors.surface
             OutlinedAvatar(
                 url = course.instructor,
-                outlineColor = MaterialTheme.colors.elevatedSurface(
-                    OwlTheme.elevations.card
-                ),
+                outlineColor = outlineColor,
                 modifier = Modifier
                     .preferredSize(38.dp)
                     .constrainAs(avatar) {
diff --git a/Owl/app/src/main/java/com/example/owl/ui/courses/SearchCourses.kt b/Owl/app/src/main/java/com/example/owl/ui/courses/SearchCourses.kt
index 74d2ea3b40..450edaaa3f 100644
--- a/Owl/app/src/main/java/com/example/owl/ui/courses/SearchCourses.kt
+++ b/Owl/app/src/main/java/com/example/owl/ui/courses/SearchCourses.kt
@@ -18,7 +18,6 @@ package com.example.owl.ui.courses
 
 import androidx.compose.foundation.BaseTextField
 import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.Text
@@ -26,6 +25,7 @@ import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.fillMaxWidth
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.TopAppBar
diff --git a/Owl/app/src/main/java/com/example/owl/ui/onboarding/Onboarding.kt b/Owl/app/src/main/java/com/example/owl/ui/onboarding/Onboarding.kt
index 029b6a30f0..f1e7925754 100644
--- a/Owl/app/src/main/java/com/example/owl/ui/onboarding/Onboarding.kt
+++ b/Owl/app/src/main/java/com/example/owl/ui/onboarding/Onboarding.kt
@@ -20,7 +20,6 @@ import androidx.compose.animation.DpPropKey
 import androidx.compose.animation.core.FloatPropKey
 import androidx.compose.animation.core.transitionDefinition
 import androidx.compose.animation.transition
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Image
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.horizontalScroll
@@ -40,6 +39,7 @@ import androidx.compose.foundation.selection.toggleable
 import androidx.compose.foundation.shape.CornerSize
 import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.FloatingActionButton
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
diff --git a/Owl/app/src/main/java/com/example/owl/ui/theme/Color.kt b/Owl/app/src/main/java/com/example/owl/ui/theme/Color.kt
index 7768bb21ec..1d38c957b4 100644
--- a/Owl/app/src/main/java/com/example/owl/ui/theme/Color.kt
+++ b/Owl/app/src/main/java/com/example/owl/ui/theme/Color.kt
@@ -17,12 +17,9 @@
 package com.example.owl.ui.theme
 
 import androidx.compose.material.Colors
-import androidx.compose.material.Surface
 import androidx.compose.runtime.Composable
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.graphics.compositeOver
-import androidx.compose.ui.unit.Dp
-import kotlin.math.ln
 
 val yellow200 = Color(0xffffeb46)
 val yellow400 = Color(0xffffc000)
@@ -47,17 +44,3 @@ val pinkDarkPrimary = Color(0xff24191c)
 fun Colors.compositedOnSurface(alpha: Float): Color {
     return onSurface.copy(alpha = alpha).compositeOver(surface)
 }
-
-/**
- * Elevation overlay logic copied from [Surface] — https://issuetracker.google.com/155181601
- */
-fun Colors.elevatedSurface(elevation: Dp): Color {
-    if (isLight) return surface
-    val foreground = calculateForeground(elevation)
-    return foreground.compositeOver(surface)
-}
-
-private fun calculateForeground(elevation: Dp): Color {
-    val alpha = ((4.5f * ln(elevation.value + 1)) + 2f) / 100f
-    return Color.White.copy(alpha = alpha)
-}
diff --git a/Owl/build.gradle b/Owl/build.gradle
index 38e5b90065..48c4a24d02 100644
--- a/Owl/build.gradle
+++ b/Owl/build.gradle
@@ -29,7 +29,7 @@ buildscript {
 }
 
 plugins {
-    id 'com.diffplug.spotless' version '5.1.1'
+    id 'com.diffplug.spotless' version '5.7.0'
 }
 
 subprojects {
@@ -39,7 +39,7 @@ subprojects {
 
         if (!Libs.AndroidX.Compose.snapshot.isEmpty()) {
             maven {
-                url "https://androidx.dev/snapshots/builds/${Libs.AndroidX.Compose.snapshot}/artifacts/ui/repository/"
+                url "https://androidx.dev/snapshots/builds/${Libs.AndroidX.Compose.snapshot}/artifacts/repository/"
             }
         }
 
diff --git a/Owl/buildSrc/src/main/java/com/example/owl/buildsrc/Dependencies.kt b/Owl/buildSrc/src/main/java/com/example/owl/buildsrc/Dependencies.kt
index 5a83c5c5fc..bf9d478d07 100644
--- a/Owl/buildSrc/src/main/java/com/example/owl/buildsrc/Dependencies.kt
+++ b/Owl/buildSrc/src/main/java/com/example/owl/buildsrc/Dependencies.kt
@@ -21,11 +21,11 @@ object Versions {
 }
 
 object Libs {
-    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha13"
+    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha15"
     const val junit = "junit:junit:4.13"
 
     object Accompanist {
-        private const val version = "0.3.1"
+        private const val version = "0.3.2"
         const val coil = "dev.chrisbanes.accompanist:accompanist-coil:$version"
     }
 
@@ -44,11 +44,11 @@ object Libs {
     }
 
     object AndroidX {
-        const val coreKtx = "androidx.core:core-ktx:1.5.0-alpha02"
+        const val coreKtx = "androidx.core:core-ktx:1.5.0-alpha04"
 
         object Compose {
             const val snapshot = ""
-            const val version = "1.0.0-alpha05"
+            const val version = "1.0.0-alpha06"
 
             const val runtime = "androidx.compose.runtime:runtime:$version"
             const val foundation = "androidx.compose.foundation:foundation:$version"
diff --git a/Owl/gradle.properties b/Owl/gradle.properties
index 2ec1b001e4..9299bc6d0f 100644
--- a/Owl/gradle.properties
+++ b/Owl/gradle.properties
@@ -1,14 +1,31 @@
+#
+# Copyright 2020 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
 # 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=-Xmx2048m
 
+# Turn on parallel compilation, caching and on-demand configuration
 org.gradle.configureondemand=true
 org.gradle.caching=true
 org.gradle.parallel=true
diff --git a/Owl/gradle/wrapper/gradle-wrapper.jar b/Owl/gradle/wrapper/gradle-wrapper.jar
index 62d4c05355..e708b1c023 100644
Binary files a/Owl/gradle/wrapper/gradle-wrapper.jar and b/Owl/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/Owl/gradle/wrapper/gradle-wrapper.properties b/Owl/gradle/wrapper/gradle-wrapper.properties
index a29c0b9852..be52383ef4 100644
--- a/Owl/gradle/wrapper/gradle-wrapper.properties
+++ b/Owl/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,5 @@
-#Wed Jun 10 18:35:56 BST 2020
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
diff --git a/Owl/gradlew b/Owl/gradlew
index fbd7c51583..4f906e0c81 100755
--- a/Owl/gradlew
+++ b/Owl/gradlew
@@ -130,7 +130,7 @@ fi
 if [ "$cygwin" = "true" -o "$msys" = "true" ] ; 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
diff --git a/Owl/gradlew.bat b/Owl/gradlew.bat
index a9f778a7a9..ac1b06f938 100644
--- a/Owl/gradlew.bat
+++ b/Owl/gradlew.bat
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
 
 set JAVA_EXE=java.exe
 %JAVA_EXE% -version >NUL 2>&1
-if "%ERRORLEVEL%" == "0" goto init
+if "%ERRORLEVEL%" == "0" goto execute
 
 echo.
 echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -54,7 +54,7 @@ goto fail
 set JAVA_HOME=%JAVA_HOME:"=%
 set JAVA_EXE=%JAVA_HOME%/bin/java.exe
 
-if exist "%JAVA_EXE%" goto init
+if exist "%JAVA_EXE%" goto execute
 
 echo.
 echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -64,21 +64,6 @@ 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
 
@@ -86,7 +71,7 @@ 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%
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
 
 :end
 @rem End local scope for the variables with windows NT shell
diff --git a/Rally/app/src/androidTest/java/com/example/compose/rally/AnimatingCircleTests.kt b/Rally/app/src/androidTest/java/com/example/compose/rally/AnimatingCircleTests.kt
index 9c0613ba10..9d2d00bac4 100644
--- a/Rally/app/src/androidTest/java/com/example/compose/rally/AnimatingCircleTests.kt
+++ b/Rally/app/src/androidTest/java/com/example/compose/rally/AnimatingCircleTests.kt
@@ -37,12 +37,14 @@ import org.junit.Test
  * For assertions, a simple screenshot testing framework is used. It requires SDK 26+ and to
  * be run on a device with 420dpi, as that the density used to generate the golden images
  * present in androidTest/assets. It runs bitmap comparisons on device.
+ *
+ * Note that different systems can produce slightly different screenshots making the test fail.
  */
 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.O)
 class AnimatingCircleTests {
 
     @get:Rule
-    val composeTestRule = createComposeRule(disableTransitions = false)
+    val composeTestRule = createComposeRule()
 
     @Test
     fun circleAnimation_idle_screenshot() {
diff --git a/Rally/app/src/main/java/com/example/compose/rally/ui/components/CommonUi.kt b/Rally/app/src/main/java/com/example/compose/rally/ui/components/CommonUi.kt
index 08823687c1..ea2b6f054c 100644
--- a/Rally/app/src/main/java/com/example/compose/rally/ui/components/CommonUi.kt
+++ b/Rally/app/src/main/java/com/example/compose/rally/ui/components/CommonUi.kt
@@ -16,7 +16,6 @@
 
 package com.example.compose.rally.ui.components
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.background
 import androidx.compose.foundation.layout.Arrangement
@@ -29,6 +28,7 @@ import androidx.compose.foundation.layout.preferredSize
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.Divider
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
 import androidx.compose.material.icons.Icons
diff --git a/Rally/app/src/main/java/com/example/compose/rally/ui/components/TopAppBar.kt b/Rally/app/src/main/java/com/example/compose/rally/ui/components/TopAppBar.kt
index ba4c90023e..532fb188f7 100644
--- a/Rally/app/src/main/java/com/example/compose/rally/ui/components/TopAppBar.kt
+++ b/Rally/app/src/main/java/com/example/compose/rally/ui/components/TopAppBar.kt
@@ -20,7 +20,6 @@ import androidx.compose.animation.animate
 import androidx.compose.animation.animateContentSize
 import androidx.compose.animation.core.LinearEasing
 import androidx.compose.animation.core.tween
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.layout.Row
 import androidx.compose.foundation.layout.Spacer
@@ -29,6 +28,7 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.foundation.layout.preferredWidth
 import androidx.compose.foundation.selection.selectable
+import androidx.compose.material.Icon
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.Surface
 import androidx.compose.material.ripple.RippleIndication
diff --git a/Rally/app/src/main/java/com/example/compose/rally/ui/overview/OverviewScreen.kt b/Rally/app/src/main/java/com/example/compose/rally/ui/overview/OverviewScreen.kt
index 814ec10866..cb11b7119f 100644
--- a/Rally/app/src/main/java/com/example/compose/rally/ui/overview/OverviewScreen.kt
+++ b/Rally/app/src/main/java/com/example/compose/rally/ui/overview/OverviewScreen.kt
@@ -16,7 +16,6 @@
 
 package com.example.compose.rally.ui.overview
 
-import androidx.compose.foundation.Icon
 import androidx.compose.foundation.ScrollableColumn
 import androidx.compose.foundation.Text
 import androidx.compose.foundation.background
@@ -30,6 +29,7 @@ import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.preferredHeight
 import androidx.compose.material.AmbientEmphasisLevels
 import androidx.compose.material.Card
+import androidx.compose.material.Icon
 import androidx.compose.material.IconButton
 import androidx.compose.material.MaterialTheme
 import androidx.compose.material.ProvideEmphasis
diff --git a/Rally/build.gradle b/Rally/build.gradle
index 6ccc84cc18..19f440f706 100644
--- a/Rally/build.gradle
+++ b/Rally/build.gradle
@@ -31,7 +31,7 @@ buildscript {
 }
 
 plugins {
-    id 'com.diffplug.spotless' version '5.1.1'
+    id 'com.diffplug.spotless' version '5.7.0'
 }
 
 subprojects {
diff --git a/Rally/buildSrc/src/main/java/com/example/compose/rally/buildsrc/dependencies.kt b/Rally/buildSrc/src/main/java/com/example/compose/rally/buildsrc/dependencies.kt
index 11a2c38908..39e7f684cb 100644
--- a/Rally/buildSrc/src/main/java/com/example/compose/rally/buildsrc/dependencies.kt
+++ b/Rally/buildSrc/src/main/java/com/example/compose/rally/buildsrc/dependencies.kt
@@ -21,7 +21,7 @@ object Versions {
 }
 
 object Libs {
-    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha13"
+    const val androidGradlePlugin = "com.android.tools.build:gradle:4.2.0-alpha15"
     const val jdkDesugar = "com.android.tools:desugar_jdk_libs:1.0.9"
 
     const val junit = "junit:junit:4.13"
@@ -48,7 +48,7 @@ object Libs {
 
         object Compose {
             const val snapshot = ""
-            const val version = "1.0.0-alpha05"
+            const val version = "1.0.0-alpha06"
 
             const val core = "androidx.compose.ui:ui:$version"
             const val foundation = "androidx.compose.foundation:foundation:$version"
@@ -92,5 +92,5 @@ object Libs {
 
 object Urls {
     const val composeSnapshotRepo = "https://androidx-dev-prod.appspot.com/snapshots/builds/" +
-        "${Libs.AndroidX.Compose.snapshot}/artifacts/ui/repository/"
+        "${Libs.AndroidX.Compose.snapshot}/artifacts/repository/"
 }
diff --git a/Rally/gradle.properties b/Rally/gradle.properties
index 28624fcf4a..9299bc6d0f 100644
--- a/Rally/gradle.properties
+++ b/Rally/gradle.properties
@@ -19,26 +19,21 @@
 # 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=-Xmx2048m
 
-# 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
+# Turn on parallel compilation, caching and on-demand configuration
 org.gradle.configureondemand=true
 org.gradle.caching=true
 org.gradle.parallel=true
 
 # AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app"s APK
+# Android operating system, and which are packaged with your app's APK
 # https://developer.android.com/topic/libraries/support-library/androidx-rn
 android.useAndroidX=true
-# Automatically convert third-party libraries to use AndroidX
-android.enableJetifier=true
+
 # Kotlin code style for this project: "official" or "obsolete":
-kotlin.code.style=official
\ No newline at end of file
+kotlin.code.style=official
diff --git a/Rally/gradle/wrapper/gradle-wrapper.properties b/Rally/gradle/wrapper/gradle-wrapper.properties
index 1737975991..436aaff960 100644
--- a/Rally/gradle/wrapper/gradle-wrapper.properties
+++ b/Rally/gradle/wrapper/gradle-wrapper.properties
@@ -19,4 +19,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-bin.zip