Skip to content

Commit 19c9b87

Browse files
Merge branch 'main' into implementation/jetsnack-swipe-delete
2 parents d1d7be4 + b0922b3 commit 19c9b87

File tree

99 files changed

+1572
-760
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+1572
-760
lines changed

Crane/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Studies built with [Jetpack Compose](https://developer.android.com/jetpack/compo
55
The goal of the sample is to showcase Material components, draggable UI elements, Android Views
66
inside Compose, and UI state handling.
77

8-
To try out this sample app, you need to use the latest version of [Android Studio Arctic Fox](https://developer.android.com/studio/preview).
8+
To try out this sample app, you need to use [Android Studio Arctic Fox](https://developer.android.com/studio).
99
You can clone this repository or import the
1010
project from Android Studio following the steps
1111
[here](https://developer.android.com/jetpack/compose/setup#sample).

Crane/app/build.gradle

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,11 @@ if (rootProject.file("local.properties").exists()) {
3030
}
3131

3232
android {
33-
compileSdkVersion 30
33+
compileSdkVersion 31
3434
defaultConfig {
3535
applicationId "androidx.compose.samples.crane"
3636
minSdkVersion 21
37-
targetSdkVersion 30
37+
targetSdkVersion 31
3838
versionCode 1
3939
versionName "1.0"
4040
vectorDrawables.useSupportLibrary = true
@@ -132,7 +132,10 @@ dependencies {
132132

133133
implementation Libs.Coil.coilCompose
134134

135+
debugImplementation Libs.AndroidX.Compose.uiTestManifest
136+
135137
androidTestImplementation Libs.JUnit.junit
138+
androidTestImplementation Libs.AndroidX.Test.core
136139
androidTestImplementation Libs.AndroidX.Test.runner
137140
androidTestImplementation Libs.AndroidX.Test.espressoCore
138141
androidTestImplementation Libs.AndroidX.Test.rules

Crane/app/src/androidTest/java/androidx/compose/samples/crane/calendar/CalendarTest.kt

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,16 @@ import androidx.compose.samples.crane.data.DatesRepository
2727
import androidx.compose.samples.crane.ui.CraneTheme
2828
import androidx.compose.ui.test.ExperimentalTestApi
2929
import androidx.compose.ui.test.SemanticsMatcher
30-
import androidx.compose.ui.test.assertContentDescriptionEquals
3130
import androidx.compose.ui.test.assertIsDisplayed
31+
import androidx.compose.ui.test.assertTextEquals
3232
import androidx.compose.ui.test.hasScrollToKeyAction
3333
import androidx.compose.ui.test.junit4.ComposeTestRule
3434
import androidx.compose.ui.test.junit4.createAndroidComposeRule
35-
import androidx.compose.ui.test.onNodeWithContentDescription
35+
import androidx.compose.ui.test.onNodeWithText
36+
import androidx.compose.ui.test.onRoot
3637
import androidx.compose.ui.test.performClick
3738
import androidx.compose.ui.test.performScrollToKey
39+
import androidx.compose.ui.test.printToLog
3840
import dagger.hilt.android.testing.HiltAndroidRule
3941
import dagger.hilt.android.testing.HiltAndroidTest
4042
import org.junit.Before
@@ -70,45 +72,46 @@ class CalendarTest {
7072
@ExperimentalTestApi
7173
@Test
7274
fun scrollsToTheBottom() {
73-
composeTestRule.onNodeWithContentDescription("January 1").assertIsDisplayed()
75+
composeTestRule.onNodeWithText("January 1 2020").assertIsDisplayed()
7476
composeTestRule.onNode(hasScrollToKeyAction()).performScrollToKey("2020/12/4")
75-
composeTestRule.onNodeWithContentDescription("December 31").performClick()
77+
composeTestRule.onNodeWithText("December 31 2020").performClick()
7678
assert(datesRepository.datesSelected.toString() == "Dec 31")
7779
}
7880

7981
@Test
8082
fun onDaySelected() {
81-
composeTestRule.onNodeWithContentDescription("January 1").assertIsDisplayed()
82-
composeTestRule.onNodeWithContentDescription("January 2")
83+
composeTestRule.onNodeWithText("January 1 2020").assertIsDisplayed()
84+
composeTestRule.onNodeWithText("January 2 2020")
8385
.assertIsDisplayed().performClick()
84-
composeTestRule.onNodeWithContentDescription("January 3").assertIsDisplayed()
86+
composeTestRule.onNodeWithText("January 3 2020").assertIsDisplayed()
8587

8688
val datesNoSelected = composeTestRule.onDateNodes(NoSelected)
87-
datesNoSelected[0].assertContentDescriptionEquals("January 1")
88-
datesNoSelected[1].assertContentDescriptionEquals("January 3")
89+
datesNoSelected[0].assertTextEquals("January 1 2020")
90+
datesNoSelected[1].assertTextEquals("January 3 2020")
8991

90-
composeTestRule.onDateNode(FirstLastDay).assertContentDescriptionEquals("January 2")
92+
composeTestRule.onDateNode(FirstLastDay).assertTextEquals("January 2 2020")
9193
}
9294

9395
@Test
9496
fun twoDaysSelected() {
95-
composeTestRule.onNodeWithContentDescription("January 2")
97+
composeTestRule.onNodeWithText("January 2 2020")
9698
.assertIsDisplayed().performClick()
9799

98100
val datesNoSelectedOneClick = composeTestRule.onDateNodes(NoSelected)
99-
datesNoSelectedOneClick[0].assertContentDescriptionEquals("January 1")
100-
datesNoSelectedOneClick[1].assertContentDescriptionEquals("January 3")
101+
composeTestRule.onRoot().printToLog("JOLO")
102+
datesNoSelectedOneClick[0].assertTextEquals("January 1 2020")
103+
datesNoSelectedOneClick[1].assertTextEquals("January 3 2020")
101104

102-
composeTestRule.onNodeWithContentDescription("January 4")
105+
composeTestRule.onNodeWithText("January 4 2020")
103106
.assertIsDisplayed().performClick()
104107

105-
composeTestRule.onDateNode(FirstDay).assertContentDescriptionEquals("January 2")
106-
composeTestRule.onDateNode(Selected).assertContentDescriptionEquals("January 3")
107-
composeTestRule.onDateNode(LastDay).assertContentDescriptionEquals("January 4")
108+
composeTestRule.onDateNode(FirstDay).assertTextEquals("January 2 2020")
109+
composeTestRule.onDateNode(Selected).assertTextEquals("January 3 2020")
110+
composeTestRule.onDateNode(LastDay).assertTextEquals("January 4 2020")
108111

109112
val datesNoSelected = composeTestRule.onDateNodes(NoSelected)
110-
datesNoSelected[0].assertContentDescriptionEquals("January 1")
111-
datesNoSelected[1].assertContentDescriptionEquals("January 5")
113+
datesNoSelected[0].assertTextEquals("January 1 2020")
114+
datesNoSelected[1].assertTextEquals("January 5 2020")
112115
}
113116
}
114117

Crane/app/src/debug/AndroidManifest.xml

Lines changed: 0 additions & 26 deletions
This file was deleted.

Crane/app/src/main/AndroidManifest.xml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,23 @@
4141
<meta-data android:name="com.google.android.geo.API_KEY"
4242
android:value="${googleMapsKey}"/>
4343

44-
<activity android:name=".home.MainActivity">
44+
<activity
45+
android:name=".home.MainActivity"
46+
android:exported="true">
4547
<intent-filter>
4648
<action android:name="android.intent.action.MAIN" />
47-
4849
<category android:name="android.intent.category.LAUNCHER" />
4950
</intent-filter>
5051
</activity>
51-
<activity android:name=".details.DetailsActivity" />
52-
<activity android:name=".calendar.CalendarActivity" />
52+
53+
<activity
54+
android:name=".details.DetailsActivity"
55+
android:exported="false" />
56+
57+
<activity
58+
android:name=".calendar.CalendarActivity"
59+
android:exported="false" />
60+
5361
</application>
5462

5563
</manifest>

Crane/app/src/main/java/androidx/compose/samples/crane/CraneApplication.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,24 @@
1717
package androidx.compose.samples.crane
1818

1919
import android.app.Application
20+
import androidx.compose.samples.crane.util.UnsplashSizingInterceptor
21+
import coil.ImageLoader
22+
import coil.ImageLoaderFactory
23+
import coil.compose.rememberImagePainter
2024
import dagger.hilt.android.HiltAndroidApp
2125

2226
@HiltAndroidApp
23-
class CraneApplication : Application()
27+
class CraneApplication : Application(), ImageLoaderFactory {
28+
29+
/**
30+
* Create the singleton [ImageLoader].
31+
* This is used by [rememberImagePainter] to load images in the app.
32+
*/
33+
override fun newImageLoader(): ImageLoader {
34+
return ImageLoader.Builder(this)
35+
.componentRegistry {
36+
add(UnsplashSizingInterceptor)
37+
}
38+
.build()
39+
}
40+
}

Crane/app/src/main/java/androidx/compose/samples/crane/calendar/Calendar.kt

Lines changed: 39 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import androidx.compose.material.MaterialTheme
3636
import androidx.compose.material.Surface
3737
import androidx.compose.material.Text
3838
import androidx.compose.runtime.Composable
39+
import androidx.compose.samples.crane.R
3940
import androidx.compose.samples.crane.calendar.model.CalendarDay
4041
import androidx.compose.samples.crane.calendar.model.CalendarMonth
4142
import androidx.compose.samples.crane.calendar.model.DayOfWeek
@@ -48,10 +49,14 @@ import androidx.compose.samples.crane.util.SemiRect
4849
import androidx.compose.ui.Alignment
4950
import androidx.compose.ui.Modifier
5051
import androidx.compose.ui.graphics.Color
52+
import androidx.compose.ui.res.stringResource
5153
import androidx.compose.ui.semantics.SemanticsPropertyKey
5254
import androidx.compose.ui.semantics.SemanticsPropertyReceiver
53-
import androidx.compose.ui.semantics.contentDescription
55+
import androidx.compose.ui.semantics.clearAndSetSemantics
5456
import androidx.compose.ui.semantics.semantics
57+
import androidx.compose.ui.semantics.stateDescription
58+
import androidx.compose.ui.semantics.text
59+
import androidx.compose.ui.text.AnnotatedString
5560
import androidx.compose.ui.tooling.preview.Preview
5661
import androidx.compose.ui.unit.dp
5762
import com.google.accompanist.insets.navigationBarsHeight
@@ -78,7 +83,7 @@ fun Calendar(
7883

7984
@Composable
8085
private fun MonthHeader(modifier: Modifier = Modifier, month: String, year: String) {
81-
Row(modifier = modifier) {
86+
Row(modifier = modifier.clearAndSetSemantics { }) {
8287
Text(
8388
modifier = Modifier.weight(1f),
8489
text = month,
@@ -112,10 +117,7 @@ private fun Week(
112117
Day(
113118
day,
114119
onDayClicked,
115-
Modifier.semantics {
116-
contentDescription = "${month.name} ${day.value}"
117-
dayStatusProperty = day.status
118-
}
120+
month
119121
)
120122
}
121123
Surface(modifier = spaceModifiers, color = rightFillColor) {
@@ -126,7 +128,7 @@ private fun Week(
126128

127129
@Composable
128130
private fun DaysOfWeek(modifier: Modifier = Modifier) {
129-
Row(modifier = modifier) {
131+
Row(modifier = modifier.clearAndSetSemantics { }) {
130132
for (day in DayOfWeek.values()) {
131133
Day(name = day.name.take(1))
132134
}
@@ -137,20 +139,28 @@ private fun DaysOfWeek(modifier: Modifier = Modifier) {
137139
private fun Day(
138140
day: CalendarDay,
139141
onDayClicked: (CalendarDay) -> Unit,
142+
month: CalendarMonth,
140143
modifier: Modifier = Modifier
141144
) {
142145
val enabled = day.status != DaySelectedStatus.NonClickable
143146
DayContainer(
144-
modifier = modifier,
145-
onClick = { if (day.status != DaySelectedStatus.NonClickable) onDayClicked(day) },
147+
modifier = modifier.semantics {
148+
if (enabled) text = AnnotatedString("${month.name} ${day.value} ${month.year}")
149+
dayStatusProperty = day.status
150+
},
151+
selected = day.status != DaySelectedStatus.NoSelected,
152+
onClick = { onDayClicked(day) },
146153
onClickEnabled = enabled,
147-
backgroundColor = day.status.color(MaterialTheme.colors)
154+
backgroundColor = day.status.color(MaterialTheme.colors),
155+
onClickLabel = stringResource(id = R.string.click_label_select)
148156
) {
149157
DayStatusContainer(status = day.status) {
150158
Text(
151159
modifier = Modifier
152160
.fillMaxSize()
153-
.wrapContentSize(Alignment.Center),
161+
.wrapContentSize(Alignment.Center)
162+
// Parent will handle semantics
163+
.clearAndSetSemantics {},
154164
text = day.value,
155165
style = MaterialTheme.typography.body1.copy(color = Color.White)
156166
)
@@ -173,17 +183,33 @@ private fun Day(name: String) {
173183
@Composable
174184
private fun DayContainer(
175185
modifier: Modifier = Modifier,
186+
selected: Boolean = false,
176187
onClick: () -> Unit = { },
177188
onClickEnabled: Boolean = true,
178189
backgroundColor: Color = Color.Transparent,
190+
onClickLabel: String? = null,
179191
content: @Composable () -> Unit
180192
) {
181193
// What if this doesn't fit the screen? - LayoutFlexible(1f) + LayoutAspectRatio(1f)
194+
val stateDescriptionLabel = stringResource(
195+
if (selected) R.string.state_descr_selected else R.string.state_descr_not_selected
196+
)
182197
Surface(
183-
modifier = modifier.size(width = CELL_SIZE, height = CELL_SIZE),
198+
modifier = modifier
199+
.size(width = CELL_SIZE, height = CELL_SIZE)
200+
.then(
201+
if (onClickEnabled) {
202+
modifier.semantics {
203+
stateDescription = stateDescriptionLabel
204+
}
205+
} else {
206+
modifier.clearAndSetSemantics { }
207+
}
208+
),
184209
onClick = onClick,
185210
enabled = onClickEnabled,
186-
color = backgroundColor
211+
color = backgroundColor,
212+
onClickLabel = onClickLabel
187213
) {
188214
content()
189215
}

Crane/app/src/main/java/androidx/compose/samples/crane/calendar/CalendarActivity.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ import androidx.compose.samples.crane.calendar.model.CalendarMonth
3838
import androidx.compose.samples.crane.calendar.model.DaySelected
3939
import androidx.compose.samples.crane.data.CalendarYear
4040
import androidx.compose.samples.crane.ui.CraneTheme
41-
import androidx.compose.samples.crane.util.ProvideImageLoader
4241
import androidx.compose.ui.Modifier
4342
import androidx.compose.ui.res.painterResource
4443
import androidx.compose.ui.res.stringResource
@@ -64,10 +63,8 @@ class CalendarActivity : ComponentActivity() {
6463

6564
setContent {
6665
ProvideWindowInsets {
67-
ProvideImageLoader {
68-
CraneTheme {
69-
CalendarScreen(onBackPressed = { finish() })
70-
}
66+
CraneTheme {
67+
CalendarScreen(onBackPressed = { finish() })
7168
}
7269
}
7370
}

Crane/app/src/main/java/androidx/compose/samples/crane/details/DetailsActivity.kt

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ import androidx.compose.runtime.setValue
5050
import androidx.compose.samples.crane.base.Result
5151
import androidx.compose.samples.crane.data.ExploreModel
5252
import androidx.compose.samples.crane.ui.CraneTheme
53-
import androidx.compose.samples.crane.util.ProvideImageLoader
5453
import androidx.compose.ui.Alignment
5554
import androidx.compose.ui.Modifier
5655
import androidx.compose.ui.text.style.TextAlign
@@ -92,16 +91,14 @@ class DetailsActivity : ComponentActivity() {
9291

9392
setContent {
9493
ProvideWindowInsets {
95-
ProvideImageLoader {
96-
CraneTheme {
97-
Surface {
98-
DetailsScreen(
99-
onErrorLoading = { finish() },
100-
modifier = Modifier
101-
.statusBarsPadding()
102-
.navigationBarsPadding()
103-
)
104-
}
94+
CraneTheme {
95+
Surface {
96+
DetailsScreen(
97+
onErrorLoading = { finish() },
98+
modifier = Modifier
99+
.statusBarsPadding()
100+
.navigationBarsPadding()
101+
)
105102
}
106103
}
107104
}

0 commit comments

Comments
 (0)