Skip to content

Commit 4b73097

Browse files
Merge branch 'main' into jv/pull-to-refresh
2 parents c68fa68 + 591dfa5 commit 4b73097

File tree

4 files changed

+329
-2
lines changed

4 files changed

+329
-2
lines changed

compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,13 @@ import com.example.compose.snippets.components.ScaffoldExample
4848
import com.example.compose.snippets.components.SliderExamples
4949
import com.example.compose.snippets.components.SwitchExamples
5050
import com.example.compose.snippets.components.TimePickerExamples
51+
import com.example.compose.snippets.components.TooltipExamples
5152
import com.example.compose.snippets.graphics.ApplyPolygonAsClipImage
5253
import com.example.compose.snippets.graphics.BitmapFromComposableFullSnippet
5354
import com.example.compose.snippets.graphics.BrushExamplesScreen
5455
import com.example.compose.snippets.images.ImageExamplesScreen
5556
import com.example.compose.snippets.landing.LandingScreen
57+
import com.example.compose.snippets.layouts.PagerExamples
5658
import com.example.compose.snippets.navigation.Destination
5759
import com.example.compose.snippets.navigation.TopComponentsDestination
5860
import com.example.compose.snippets.ui.theme.SnippetsTheme
@@ -87,6 +89,7 @@ class SnippetsActivity : ComponentActivity() {
8789
}
8890
Destination.ShapesExamples -> ApplyPolygonAsClipImage()
8991
Destination.SharedElementExamples -> PlaceholderSizeAnimated_Demo()
92+
Destination.PagerExamples -> PagerExamples()
9093
}
9194
}
9295
}
@@ -113,6 +116,7 @@ class SnippetsActivity : ComponentActivity() {
113116
TopComponentsDestination.DatePickerExamples -> DatePickerExamples()
114117
TopComponentsDestination.CarouselExamples -> CarouselExamples()
115118
TopComponentsDestination.MenusExample -> MenusExamples()
119+
TopComponentsDestination.TooltipExamples -> TooltipExamples()
116120
}
117121
}
118122
}
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* Copyright 2024 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.example.compose.snippets.components
18+
19+
import androidx.compose.foundation.layout.Arrangement
20+
import androidx.compose.foundation.layout.Row
21+
import androidx.compose.foundation.layout.fillMaxSize
22+
import androidx.compose.foundation.layout.fillMaxWidth
23+
import androidx.compose.foundation.layout.padding
24+
import androidx.compose.material.icons.Icons
25+
import androidx.compose.material.icons.filled.Camera
26+
import androidx.compose.material.icons.filled.Favorite
27+
import androidx.compose.material.icons.filled.Info
28+
import androidx.compose.material3.ExperimentalMaterial3Api
29+
import androidx.compose.material3.Icon
30+
import androidx.compose.material3.IconButton
31+
import androidx.compose.material3.PlainTooltip
32+
import androidx.compose.material3.RichTooltip
33+
import androidx.compose.material3.Text
34+
import androidx.compose.material3.TextButton
35+
import androidx.compose.material3.TooltipBox
36+
import androidx.compose.material3.TooltipDefaults
37+
import androidx.compose.material3.TooltipState
38+
import androidx.compose.runtime.Composable
39+
import androidx.compose.runtime.LaunchedEffect
40+
import androidx.compose.runtime.getValue
41+
import androidx.compose.runtime.mutableStateOf
42+
import androidx.compose.runtime.remember
43+
import androidx.compose.runtime.setValue
44+
import androidx.compose.ui.Alignment
45+
import androidx.compose.ui.Modifier
46+
import androidx.compose.ui.text.style.TextAlign
47+
import androidx.compose.ui.tooling.preview.Preview
48+
import androidx.compose.ui.unit.DpSize
49+
import androidx.compose.ui.unit.dp
50+
51+
@Composable
52+
fun TooltipExamples() {
53+
Text(
54+
"Long press an icon to see the tooltip.",
55+
modifier = Modifier.fillMaxWidth().padding(16.dp),
56+
textAlign = TextAlign.Center
57+
)
58+
Row(
59+
horizontalArrangement = Arrangement.SpaceEvenly,
60+
verticalAlignment = Alignment.CenterVertically,
61+
modifier = Modifier.fillMaxSize()
62+
) {
63+
PlainTooltipExample()
64+
RichTooltipExample()
65+
AdvancedRichTooltipExample()
66+
}
67+
}
68+
69+
@Preview
70+
@Composable
71+
private fun TooltipExamplesPreview() {
72+
TooltipExamples()
73+
}
74+
75+
@OptIn(ExperimentalMaterial3Api::class)
76+
// [START android_compose_components_plaintooltipexample]
77+
@Composable
78+
fun PlainTooltipExample(
79+
modifier: Modifier = Modifier,
80+
plainTooltipText: String = "Add to favorites"
81+
) {
82+
var tooltipState by remember { mutableStateOf(TooltipState()) }
83+
TooltipBox(
84+
modifier = modifier,
85+
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
86+
tooltip = {
87+
PlainTooltip { Text(plainTooltipText) }
88+
},
89+
state = tooltipState
90+
) {
91+
IconButton(onClick = { /* Do something... */ }) {
92+
Icon(
93+
imageVector = Icons.Filled.Favorite,
94+
contentDescription = "Add to favorites"
95+
)
96+
}
97+
}
98+
99+
// Reset tooltipState after closing the tooltip.
100+
LaunchedEffect(tooltipState.isVisible) {
101+
if (!tooltipState.isVisible) {
102+
tooltipState = TooltipState()
103+
}
104+
}
105+
}
106+
107+
// [END android_compose_components_plaintooltipexample]
108+
109+
@Preview
110+
@Composable
111+
private fun PlainTooltipSamplePreview() {
112+
PlainTooltipExample()
113+
}
114+
115+
@OptIn(ExperimentalMaterial3Api::class)
116+
// [START android_compose_components_richtooltipexample]
117+
@Composable
118+
fun RichTooltipExample(
119+
modifier: Modifier = Modifier,
120+
richTooltipSubheadText: String = "Rich Tooltip",
121+
richTooltipText: String = "Rich tooltips support multiple lines of informational text."
122+
) {
123+
var tooltipState by remember { mutableStateOf(TooltipState(isPersistent = true)) }
124+
125+
TooltipBox(
126+
modifier = modifier,
127+
positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
128+
tooltip = {
129+
RichTooltip(
130+
title = { Text(richTooltipSubheadText) }
131+
) {
132+
Text(richTooltipText)
133+
}
134+
},
135+
state = tooltipState
136+
) {
137+
IconButton(onClick = { /* Icon button's click event */ }) {
138+
Icon(
139+
imageVector = Icons.Filled.Info,
140+
contentDescription = "Show more information"
141+
)
142+
}
143+
}
144+
145+
// Reset tooltipState after closing the tooltip.
146+
LaunchedEffect(tooltipState.isVisible) {
147+
if (!tooltipState.isVisible) {
148+
tooltipState = TooltipState(isPersistent = true)
149+
}
150+
}
151+
}
152+
// [END android_compose_components_richtooltipexample]
153+
154+
@Preview
155+
@Composable
156+
private fun RichTooltipSamplePreview() {
157+
RichTooltipExample()
158+
}
159+
160+
@OptIn(ExperimentalMaterial3Api::class)
161+
// [START android_compose_components_advancedrichtooltipexample]
162+
@Composable
163+
fun AdvancedRichTooltipExample(
164+
modifier: Modifier = Modifier,
165+
richTooltipSubheadText: String = "Custom Rich Tooltip",
166+
richTooltipText: String = "Rich tooltips support multiple lines of informational text.",
167+
richTooltipActionText: String = "Dismiss"
168+
) {
169+
var tooltipState by remember { mutableStateOf(TooltipState(isPersistent = true)) }
170+
171+
TooltipBox(
172+
modifier = modifier,
173+
positionProvider = TooltipDefaults.rememberRichTooltipPositionProvider(),
174+
tooltip = {
175+
RichTooltip(
176+
title = { Text(richTooltipSubheadText) },
177+
action = {
178+
Row {
179+
TextButton(onClick = { tooltipState.dismiss() }) {
180+
Text(richTooltipActionText)
181+
}
182+
}
183+
},
184+
caretSize = DpSize(32.dp, 16.dp)
185+
) {
186+
Text(richTooltipText)
187+
}
188+
},
189+
state = tooltipState
190+
) {
191+
IconButton(onClick = { tooltipState.dismiss() }) {
192+
Icon(
193+
imageVector = Icons.Filled.Camera,
194+
contentDescription = "Open camera"
195+
)
196+
}
197+
}
198+
199+
// Reset tooltipState after closing the tooltip.
200+
LaunchedEffect(tooltipState.isVisible) {
201+
if (!tooltipState.isVisible) {
202+
tooltipState = TooltipState(isPersistent = true)
203+
}
204+
}
205+
}
206+
// [END android_compose_components_advancedrichtooltipexample]
207+
208+
@Preview
209+
@Composable
210+
private fun RichTooltipWithCustomCaretSamplePreview() {
211+
AdvancedRichTooltipExample()
212+
}

compose/snippets/src/main/java/com/example/compose/snippets/layouts/PagerSnippets.kt

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,12 @@ package com.example.compose.snippets.layouts
2020

2121
import android.util.Log
2222
import androidx.compose.foundation.Image
23+
import androidx.compose.foundation.LocalIndication
2324
import androidx.compose.foundation.background
25+
import androidx.compose.foundation.clickable
26+
import androidx.compose.foundation.interaction.MutableInteractionSource
27+
import androidx.compose.foundation.interaction.collectIsDraggedAsState
28+
import androidx.compose.foundation.interaction.collectIsPressedAsState
2429
import androidx.compose.foundation.layout.Arrangement
2530
import androidx.compose.foundation.layout.Box
2631
import androidx.compose.foundation.layout.Column
@@ -50,6 +55,8 @@ import androidx.compose.material3.TabRow
5055
import androidx.compose.material3.Text
5156
import androidx.compose.runtime.Composable
5257
import androidx.compose.runtime.LaunchedEffect
58+
import androidx.compose.runtime.getValue
59+
import androidx.compose.runtime.remember
5360
import androidx.compose.runtime.rememberCoroutineScope
5461
import androidx.compose.runtime.snapshotFlow
5562
import androidx.compose.ui.Alignment
@@ -58,13 +65,15 @@ import androidx.compose.ui.draw.clip
5865
import androidx.compose.ui.graphics.Color
5966
import androidx.compose.ui.graphics.graphicsLayer
6067
import androidx.compose.ui.layout.ContentScale
68+
import androidx.compose.ui.text.style.TextAlign
6169
import androidx.compose.ui.tooling.preview.Preview
6270
import androidx.compose.ui.unit.Density
6371
import androidx.compose.ui.unit.dp
6472
import androidx.compose.ui.util.lerp
6573
import coil.compose.rememberAsyncImagePainter
6674
import com.example.compose.snippets.util.rememberRandomSampleImageUrl
6775
import kotlin.math.absoluteValue
76+
import kotlinx.coroutines.delay
6877
import kotlinx.coroutines.launch
6978

7079
/*
@@ -83,6 +92,18 @@ import kotlinx.coroutines.launch
8392
* limitations under the License.
8493
*/
8594

95+
@Composable
96+
fun PagerExamples() {
97+
AutoAdvancePager(
98+
listOf(
99+
Color.Red,
100+
Color.Gray,
101+
Color.Green,
102+
Color.White
103+
)
104+
)
105+
}
106+
86107
@Preview
87108
@Composable
88109
fun HorizontalPagerSample() {
@@ -392,6 +413,94 @@ fun PagerIndicator() {
392413
}
393414
}
394415

416+
@Composable
417+
fun AutoAdvancePager(pageItems: List<Color>, modifier: Modifier = Modifier) {
418+
Box(modifier = Modifier.fillMaxSize()) {
419+
val pagerState = rememberPagerState(pageCount = { pageItems.size })
420+
val pagerIsDragged by pagerState.interactionSource.collectIsDraggedAsState()
421+
422+
val pageInteractionSource = remember { MutableInteractionSource() }
423+
val pageIsPressed by pageInteractionSource.collectIsPressedAsState()
424+
425+
// Stop auto-advancing when pager is dragged or one of the pages is pressed
426+
val autoAdvance = !pagerIsDragged && !pageIsPressed
427+
428+
if (autoAdvance) {
429+
LaunchedEffect(pagerState, pageInteractionSource) {
430+
while (true) {
431+
delay(2000)
432+
val nextPage = (pagerState.currentPage + 1) % pageItems.size
433+
pagerState.animateScrollToPage(nextPage)
434+
}
435+
}
436+
}
437+
438+
HorizontalPager(
439+
state = pagerState
440+
) { page ->
441+
Text(
442+
text = "Page: $page",
443+
textAlign = TextAlign.Center,
444+
modifier = modifier
445+
.fillMaxSize()
446+
.background(pageItems[page])
447+
.clickable(
448+
interactionSource = pageInteractionSource,
449+
indication = LocalIndication.current
450+
) {
451+
// Handle page click
452+
}
453+
.wrapContentSize(align = Alignment.Center)
454+
)
455+
}
456+
457+
PagerIndicator(pageItems.size, pagerState.currentPage)
458+
}
459+
}
460+
461+
@Preview
462+
@Composable
463+
private fun AutoAdvancePagerPreview() {
464+
val pageItems: List<Color> = listOf(
465+
Color.Red,
466+
Color.Gray,
467+
Color.Green,
468+
Color.White
469+
)
470+
AutoAdvancePager(pageItems = pageItems)
471+
}
472+
473+
@Composable
474+
fun PagerIndicator(pageCount: Int, currentPageIndex: Int, modifier: Modifier = Modifier) {
475+
Box(modifier = Modifier.fillMaxSize()) {
476+
Row(
477+
modifier = Modifier
478+
.wrapContentHeight()
479+
.fillMaxWidth()
480+
.align(Alignment.BottomCenter)
481+
.padding(bottom = 8.dp),
482+
horizontalArrangement = Arrangement.Center
483+
) {
484+
repeat(pageCount) { iteration ->
485+
val color = if (currentPageIndex == iteration) Color.DarkGray else Color.LightGray
486+
Box(
487+
modifier = modifier
488+
.padding(2.dp)
489+
.clip(CircleShape)
490+
.background(color)
491+
.size(16.dp)
492+
)
493+
}
494+
}
495+
}
496+
}
497+
498+
@Preview
499+
@Composable
500+
private fun PagerIndicatorPreview() {
501+
PagerIndicator(pageCount = 4, currentPageIndex = 1)
502+
}
503+
395504
// [START android_compose_pager_custom_page_size]
396505
private val threePagesPerViewport = object : PageSize {
397506
override fun Density.calculateMainAxisPageSize(

0 commit comments

Comments
 (0)