Skip to content

Commit

Permalink
[All] Update to Compose 1.0.0-alpha06 (#261)
Browse files Browse the repository at this point in the history
* [Jetsnack] Cart implementation
* [Jetsnack] Reuse Quantity Selector
* [Jetsnack] Reuse Destination Bar
* [Jetsnack] Add Inspired By Section to Cart
* [Jetsnack] Add Checkout Bar to cart
* [Jetsnack] Fix Spotless issues
* [Jetsnack] Fix review comments
* [Jetsnack] Fix review comments
* Update to snapshot 6922857.
* [Jetsurvey] Bump to alpha06
* [Owl] Update to snapshot 6922857.
* [JetChat] Update to snapshot 6922857
* [Rally] Update to snapshot 6922857
* [Jetcaster] Update to snapshot 6922857.
* Removes unncessary elevation param
* [Crane] Update to 6922857 snapshot (#246)
* [Owl] Replace elevation overlay workaround with AmbientElevationOverlay.
* [Jetchat] Replace elevation overlay workaround with AmbientElevationOverlay
* [Jetsurvey] Redesigned next/prev buttons
* [Jetsurvey] Redesigned progress indicator
* [Jetsurvey] Adding elevation to the action bar
* [Jetsurvey] Capitalising strings
* [Jetsurvey] Question title redesigned
* [Jetsurvey] Removing unused composable
* [Jetsurvey] Removing the column usage in progress indicator
* [Crane] Adds Hilt to the project (#237)
* [JetNews] bump to alpha06 (#258)
* [JetNews] bump to alpha06
* Fix unused modifier on BookmarkButton
* Update all samples to AGP 4.2.0-alpha15
* Turn on allWarningsAsErrors in Jetsurvey
* Tidy up gradle.properties

We now have parallel compilation, caching
and on-demand configuration enabled on all projects

* Disable Jetifier where possible
Only Crane requires Jetifier becuase it uses the
Google Maps SDK.

* Update all projects to ktlint 0.39.0
* Update snapshot URL
* Update to Compose 1.0.0-alpha06

Co-authored-by: Jolanda Verhoef <[email protected]>
Co-authored-by: Nick Butcher <[email protected]>
Co-authored-by: Nick Butcher <[email protected]>
Co-authored-by: Florina Muntenescu <[email protected]>
Co-authored-by: Jose Alcerreca <[email protected]>
Co-authored-by: Jolanda Verhoef <[email protected]>
Co-authored-by: Jose Alcérreca <[email protected]>
Co-authored-by: Florina Muntenescu <[email protected]>
Co-authored-by: Manuel Vivo <[email protected]>
Co-authored-by: Sean McQuillan <[email protected]>
  • Loading branch information
11 people authored Oct 28, 2020
1 parent 4ffcd3b commit 4d21fa4
Show file tree
Hide file tree
Showing 124 changed files with 1,910 additions and 830 deletions.
42 changes: 21 additions & 21 deletions .github/ci-gradle.properties
Original file line number Diff line number Diff line change
@@ -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
34 changes: 34 additions & 0 deletions Crane/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
35 changes: 33 additions & 2 deletions Crane/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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", "") ]
}
Expand Down Expand Up @@ -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
Expand All @@ -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
}
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 4d21fa4

Please sign in to comment.