Skip to content

Commit 2900c01

Browse files
authored
Jetlagged Home Screen - use Flows (#1435)
Using FlowRow + FlowColumn combined with WindowSize classes to adapt better to different device sizes. ![image](https://github.com/user-attachments/assets/2acc2f43-5cd5-435b-ac86-8e9aa78f4683)
2 parents 324bc6f + f54357a commit 2900c01

26 files changed

+1400
-170
lines changed

JetLagged/app/build.gradle.kts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,9 @@ android {
7474
sourceCompatibility = JavaVersion.VERSION_17
7575
targetCompatibility = JavaVersion.VERSION_17
7676
}
77-
77+
kotlinOptions {
78+
jvmTarget = "17"
79+
}
7880
buildFeatures {
7981
compose = true
8082
// Disable unused AGP features
@@ -108,6 +110,8 @@ dependencies {
108110
implementation(libs.androidx.core.ktx)
109111
implementation(libs.androidx.activity.compose)
110112
implementation(libs.androidx.lifecycle.viewModelCompose)
113+
implementation(libs.androidx.lifecycle.runtime)
114+
implementation(libs.androidx.lifecycle.runtime.compose)
111115
implementation(libs.androidx.navigation.compose)
112116
implementation(libs.androidx.constraintlayout.compose)
113117
implementation(libs.google.android.material)
@@ -119,6 +123,7 @@ dependencies {
119123
implementation(libs.androidx.compose.material3)
120124
implementation(libs.androidx.compose.animation)
121125
implementation(libs.androidx.compose.material.iconsExtended)
126+
implementation(libs.androidx.compose.materialWindow)
122127
implementation(libs.androidx.compose.ui.googlefonts)
123128
implementation(libs.androidx.compose.ui.tooling.preview)
124129
debugImplementation(libs.androidx.compose.ui.tooling)

JetLagged/app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
android:icon="@mipmap/ic_launcher"
2424
android:label="@string/app_name"
2525
android:supportsRtl="true"
26+
android:resizeableActivity="true"
2627
android:theme="@style/Theme.JetLagged">
2728

2829
<profileable android:shell="true" tools:targetApi="q" />
@@ -31,6 +32,7 @@
3132
android:name=".MainActivity"
3233
android:theme="@style/Theme.JetLagged"
3334
android:exported="true"
35+
android:configChanges="screenSize|smallestScreenSize|orientation|screenLayout|uiMode"
3436
android:windowSoftInputMode="adjustResize">
3537
<intent-filter>
3638
<action android:name="android.intent.action.MAIN" />
Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
1+
/*
2+
* Copyright 2023 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.jetlagged
18+
19+
import androidx.compose.foundation.BorderStroke
20+
import androidx.compose.foundation.layout.Arrangement
21+
import androidx.compose.foundation.layout.Box
22+
import androidx.compose.foundation.layout.BoxWithConstraints
23+
import androidx.compose.foundation.layout.Column
24+
import androidx.compose.foundation.layout.ExperimentalLayoutApi
25+
import androidx.compose.foundation.layout.FlowRow
26+
import androidx.compose.foundation.layout.Row
27+
import androidx.compose.foundation.layout.Spacer
28+
import androidx.compose.foundation.layout.aspectRatio
29+
import androidx.compose.foundation.layout.fillMaxHeight
30+
import androidx.compose.foundation.layout.fillMaxSize
31+
import androidx.compose.foundation.layout.fillMaxWidth
32+
import androidx.compose.foundation.layout.height
33+
import androidx.compose.foundation.layout.heightIn
34+
import androidx.compose.foundation.layout.padding
35+
import androidx.compose.foundation.layout.size
36+
import androidx.compose.foundation.layout.sizeIn
37+
import androidx.compose.foundation.layout.width
38+
import androidx.compose.foundation.layout.widthIn
39+
import androidx.compose.foundation.layout.wrapContentSize
40+
import androidx.compose.foundation.layout.wrapContentWidth
41+
import androidx.compose.foundation.shape.RoundedCornerShape
42+
import androidx.compose.material.icons.Icons
43+
import androidx.compose.material.icons.filled.SingleBed
44+
import androidx.compose.material.icons.filled.Watch
45+
import androidx.compose.material3.Card
46+
import androidx.compose.material3.CardDefaults
47+
import androidx.compose.material3.Icon
48+
import androidx.compose.material3.Text
49+
import androidx.compose.runtime.Composable
50+
import androidx.compose.ui.Alignment.Companion.Center
51+
import androidx.compose.ui.Alignment.Companion.CenterHorizontally
52+
import androidx.compose.ui.Alignment.Companion.CenterStart
53+
import androidx.compose.ui.Alignment.Companion.CenterVertically
54+
import androidx.compose.ui.Modifier
55+
import androidx.compose.ui.draw.drawBehind
56+
import androidx.compose.ui.graphics.Color
57+
import androidx.compose.ui.graphics.vector.ImageVector
58+
import androidx.compose.ui.res.stringResource
59+
import androidx.compose.ui.text.style.TextAlign
60+
import androidx.compose.ui.tooling.preview.Preview
61+
import androidx.compose.ui.unit.dp
62+
import androidx.compose.ui.unit.sp
63+
import com.example.jetlagged.backgrounds.BubbleBackground
64+
import com.example.jetlagged.backgrounds.FadingCircleBackground
65+
import com.example.jetlagged.data.WellnessData
66+
import com.example.jetlagged.ui.theme.HeadingStyle
67+
import com.example.jetlagged.ui.theme.LightBlue
68+
import com.example.jetlagged.ui.theme.Lilac
69+
import com.example.jetlagged.ui.theme.MintGreen
70+
import com.example.jetlagged.ui.theme.SmallHeadingStyle
71+
72+
@Composable
73+
fun BasicInformationalCard(
74+
modifier: Modifier = Modifier,
75+
borderColor: Color,
76+
content: @Composable () -> Unit
77+
) {
78+
val shape = RoundedCornerShape(24.dp)
79+
Card(
80+
shape = shape,
81+
colors = CardDefaults.cardColors(containerColor = Color.White),
82+
modifier = modifier
83+
.padding(8.dp),
84+
border = BorderStroke(2.dp, borderColor)
85+
) {
86+
Box {
87+
content()
88+
}
89+
}
90+
}
91+
92+
@Composable
93+
fun TwoLineInfoCard(
94+
borderColor: Color,
95+
firstLineText: String,
96+
secondLineText: String,
97+
icon: ImageVector,
98+
modifier: Modifier = Modifier
99+
) {
100+
BasicInformationalCard(
101+
borderColor = borderColor,
102+
modifier = modifier.size(200.dp)
103+
) {
104+
BubbleBackground(
105+
modifier = Modifier.fillMaxSize(),
106+
numberBubbles = 3, bubbleColor = borderColor.copy(0.25f)
107+
)
108+
BoxWithConstraints(
109+
modifier = Modifier
110+
.padding(16.dp)
111+
.fillMaxSize(),
112+
) {
113+
if (maxWidth > 400.dp) {
114+
Row(
115+
modifier = Modifier
116+
.wrapContentSize()
117+
.align(CenterStart)
118+
) {
119+
Icon(
120+
icon, contentDescription = null,
121+
modifier = Modifier
122+
.size(50.dp)
123+
.align(CenterVertically)
124+
)
125+
Spacer(modifier = Modifier.width(16.dp))
126+
Column(
127+
modifier = Modifier
128+
.align(CenterVertically)
129+
.wrapContentSize()
130+
) {
131+
Text(
132+
firstLineText,
133+
style = SmallHeadingStyle
134+
)
135+
Text(
136+
secondLineText,
137+
style = HeadingStyle,
138+
)
139+
}
140+
}
141+
} else {
142+
Column(
143+
modifier = Modifier
144+
.wrapContentSize()
145+
.align(Center)
146+
) {
147+
Icon(
148+
icon, contentDescription = null,
149+
modifier = Modifier
150+
.size(50.dp)
151+
.align(CenterHorizontally)
152+
)
153+
Spacer(modifier = Modifier.height(16.dp))
154+
Column(modifier = Modifier.align(CenterHorizontally)) {
155+
Text(
156+
firstLineText,
157+
style = SmallHeadingStyle,
158+
modifier = Modifier.align(CenterHorizontally)
159+
)
160+
Text(
161+
secondLineText,
162+
style = HeadingStyle,
163+
modifier = Modifier.align(CenterHorizontally)
164+
)
165+
}
166+
}
167+
}
168+
}
169+
}
170+
}
171+
172+
@Preview
173+
@Preview(widthDp = 500, name = "larger screen")
174+
@Composable
175+
fun AverageTimeInBedCard(modifier: Modifier = Modifier) {
176+
TwoLineInfoCard(
177+
borderColor = Lilac,
178+
firstLineText = stringResource(R.string.ave_time_in_bed_heading),
179+
secondLineText = "8h42min",
180+
icon = Icons.Default.Watch,
181+
modifier = modifier
182+
.wrapContentWidth()
183+
.heightIn(min = 156.dp)
184+
)
185+
}
186+
187+
@Preview
188+
@Preview(widthDp = 500, name = "larger screen")
189+
@Composable
190+
fun AverageTimeAsleepCard(modifier: Modifier = Modifier) {
191+
TwoLineInfoCard(
192+
borderColor = MintGreen,
193+
firstLineText = stringResource(R.string.ave_time_sleep_heading),
194+
secondLineText = "7h42min",
195+
icon = Icons.Default.SingleBed,
196+
modifier = modifier
197+
.wrapContentWidth()
198+
.heightIn(min = 156.dp)
199+
)
200+
}
201+
202+
@OptIn(ExperimentalLayoutApi::class)
203+
@Preview
204+
@Composable
205+
fun WellnessCard(
206+
modifier: Modifier = Modifier,
207+
wellnessData: WellnessData = WellnessData(0, 0, 0)
208+
) {
209+
BasicInformationalCard(
210+
borderColor = LightBlue,
211+
modifier = modifier
212+
.widthIn(max = 400.dp)
213+
.heightIn(min = 200.dp)
214+
) {
215+
FadingCircleBackground(36.dp, LightBlue.copy(0.25f))
216+
Column(
217+
horizontalAlignment = CenterHorizontally,
218+
modifier = Modifier
219+
.fillMaxWidth()
220+
) {
221+
HomeScreenCardHeading(text = stringResource(R.string.wellness_heading))
222+
FlowRow(
223+
horizontalArrangement = Arrangement.Center,
224+
verticalArrangement = Arrangement.Center,
225+
modifier = Modifier.fillMaxHeight()
226+
) {
227+
WellnessBubble(
228+
titleText = stringResource(R.string.snoring_heading),
229+
countText = wellnessData.snoring.toString(),
230+
metric = "min"
231+
)
232+
WellnessBubble(
233+
titleText = stringResource(R.string.coughing_heading),
234+
countText = wellnessData.coughing.toString(),
235+
metric = "times"
236+
)
237+
WellnessBubble(
238+
titleText = stringResource(R.string.respiration_heading),
239+
countText = wellnessData.respiration.toString(),
240+
metric = "rpm"
241+
)
242+
}
243+
}
244+
}
245+
}
246+
247+
@Composable
248+
fun WellnessBubble(
249+
titleText: String,
250+
countText: String,
251+
metric: String,
252+
modifier: Modifier = Modifier
253+
) {
254+
Column(
255+
modifier = modifier
256+
.padding(4.dp)
257+
.sizeIn(maxHeight = 100.dp)
258+
.aspectRatio(1f)
259+
.drawBehind {
260+
drawCircle(LightBlue)
261+
},
262+
verticalArrangement = Arrangement.Center,
263+
horizontalAlignment = CenterHorizontally
264+
) {
265+
Text(titleText, fontSize = 12.sp)
266+
Text(countText, fontSize = 36.sp)
267+
Text(metric, fontSize = 12.sp)
268+
}
269+
}
270+
271+
@Composable
272+
fun HomeScreenCardHeading(text: String) {
273+
Text(
274+
text,
275+
modifier = Modifier
276+
.fillMaxWidth()
277+
.padding(top = 8.dp),
278+
textAlign = TextAlign.Center,
279+
style = HeadingStyle
280+
)
281+
}

JetLagged/app/src/main/java/com/example/jetlagged/JetLaggedDrawer.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ import androidx.compose.material3.NavigationDrawerItem
3838
import androidx.compose.material3.NavigationDrawerItemDefaults
3939
import androidx.compose.material3.Surface
4040
import androidx.compose.material3.Text
41+
import androidx.compose.material3.windowsizeclass.WindowSizeClass
42+
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
4143
import androidx.compose.runtime.Composable
4244
import androidx.compose.runtime.getValue
4345
import androidx.compose.runtime.mutableStateOf
@@ -54,7 +56,8 @@ import androidx.compose.ui.util.lerp
5456
import kotlinx.coroutines.launch
5557

5658
@Composable
57-
fun HomeScreenDrawer() {
59+
fun HomeScreenDrawer(windowSizeClass: WindowSizeClass) {
60+
5861
Surface(
5962
modifier = Modifier.fillMaxSize()
6063
) {
@@ -105,6 +108,7 @@ fun HomeScreenDrawer() {
105108
})
106109
val decay = rememberSplineBasedDecay<Float>()
107110
ScreenContents(
111+
windowWidthSizeClass = windowSizeClass.widthSizeClass,
108112
selectedScreen = screenState,
109113
onDrawerClicked = ::toggleDrawerState,
110114
modifier = Modifier
@@ -166,6 +170,7 @@ fun HomeScreenDrawer() {
166170

167171
@Composable
168172
private fun ScreenContents(
173+
windowWidthSizeClass: WindowWidthSizeClass,
169174
selectedScreen: Screen,
170175
onDrawerClicked: () -> Unit,
171176
modifier: Modifier = Modifier
@@ -174,6 +179,7 @@ private fun ScreenContents(
174179
when (selectedScreen) {
175180
Screen.Home ->
176181
JetLaggedScreen(
182+
windowSizeClass = windowWidthSizeClass,
177183
modifier = Modifier,
178184
onDrawerClicked = onDrawerClicked
179185
)

0 commit comments

Comments
 (0)