diff --git a/app/build.gradle b/app/build.gradle index 75677b5..c35e5f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,12 +5,12 @@ plugins { android { namespace 'com.lahsuak.apps.jetpackcomposebasic' - compileSdk 33 + compileSdk 34 defaultConfig { applicationId "com.lahsuak.apps.jetpackcomposebasic" minSdk 23 - targetSdk 33 + targetSdk 34 versionCode 1 versionName "1.0" @@ -27,17 +27,17 @@ android { } } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = '17' } buildFeatures { compose true } composeOptions { - kotlinCompilerExtensionVersion '1.1.1' + kotlinCompilerExtensionVersion '1.4.3' } packagingOptions { resources { @@ -48,15 +48,15 @@ android { dependencies { - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.1' - implementation 'androidx.activity:activity-compose:1.6.1' + implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.1' + implementation 'androidx.activity:activity-compose:1.7.2' implementation "androidx.compose.ui:ui:$compose_version" implementation "androidx.compose.ui:ui-tooling-preview:$compose_version" - implementation 'androidx.compose.material3:material3:1.1.0-alpha03' + implementation 'androidx.compose.material3:material3:1.2.0-alpha03' testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.4' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' + androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version" debugImplementation "androidx.compose.ui:ui-tooling:$compose_version" debugImplementation "androidx.compose.ui:ui-test-manifest:$compose_version" diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/MainActivity.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/MainActivity.kt index 187ee32..1d78655 100644 --- a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/MainActivity.kt +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/MainActivity.kt @@ -6,10 +6,10 @@ import androidx.activity.compose.setContent import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface -import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview +import com.lahsuak.apps.jetpackcomposebasic.ui.screen.HomeScreen import com.lahsuak.apps.jetpackcomposebasic.ui.theme.JetPackComposeBasicTheme class MainActivity : ComponentActivity() { @@ -22,29 +22,22 @@ class MainActivity : ComponentActivity() { modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { - Greeting("Android") + HomeScreen() } } } } } -/*** -Composable functions : -A composable function is a regular function annotated with @Composable. -This enables your function to call other @Composable functions within it. -You can see how the Greeting function is marked as @Composable. -This function will produce a piece of UI hierarchy displaying the given input, -String. Text is a composable function provided by the library. -***/ -@Composable -fun Greeting(name: String) { - Text(text = "Hello $name!") -} @Preview(showBackground = true) @Composable fun DefaultPreview() { JetPackComposeBasicTheme { - Greeting("Android") + Surface( + modifier = Modifier.fillMaxSize(), + color = MaterialTheme.colorScheme.background + ) { + HomeScreen() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/model/BottomMenuContent.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/model/BottomMenuContent.kt new file mode 100644 index 0000000..76cd88c --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/model/BottomMenuContent.kt @@ -0,0 +1,8 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.model + +import androidx.annotation.DrawableRes + +data class BottomMenuContent( + val title: String, + @DrawableRes val iconId: Int +) diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/model/Feature.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/model/Feature.kt new file mode 100644 index 0000000..c9faa16 --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/model/Feature.kt @@ -0,0 +1,12 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.model + +import androidx.annotation.DrawableRes +import androidx.compose.ui.graphics.Color + +data class Feature( + val title: String, + @DrawableRes val iconId: Int, + val lightColor: Color, + val mediumColor: Color, + val darkColor: Color, +) diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/HomeScreen.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/HomeScreen.kt new file mode 100644 index 0000000..5571a16 --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/screen/HomeScreen.kt @@ -0,0 +1,388 @@ +package com.lahsuak.apps.jetpackcomposebasic.ui.screen + +import androidx.compose.runtime.Composable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.* +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Alignment.Companion.BottomCenter +import androidx.compose.ui.Alignment.Companion.Center +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Path +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.lahsuak.apps.jetpackcomposebasic.ui.model.BottomMenuContent +import com.lahsuak.apps.jetpackcomposebasic.ui.model.Feature +import com.lahsuak.apps.jetpackcomposebasic.R +import com.lahsuak.apps.jetpackcomposebasic.util.standardQuadFromTo +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.AquaBlue +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.Beige1 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.Beige2 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.Beige3 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.BlueViolet1 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.BlueViolet2 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.BlueViolet3 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.ButtonBlue +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.DarkerButtonBlue +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.DeepBlue +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.LightGreen1 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.LightGreen2 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.LightGreen3 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.LightRed +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.OrangeYellow1 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.OrangeYellow2 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.OrangeYellow3 +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.TextWhite +import com.lahsuak.apps.jetpackcomposebasic.ui.theme.Transparent + +@Composable +fun HomeScreen() { + Box( + modifier = Modifier + .background(DeepBlue) + .fillMaxSize() + ) { + Column { + GreetingSection() + ChipSection(chips = listOf("Sweet Sleep", "insomnia", "Depression", "Anxiety")) + DailyThought(Modifier.fillMaxWidth().padding(horizontal = 16.dp)) + FeatureSection( + features = listOf( + Feature( + title = "Sleep meditation", + R.drawable.ic_headphone, + BlueViolet1, + BlueViolet2, + BlueViolet3 + ), + Feature( + title = "Tips for sleeping", + R.drawable.ic_videocam, + LightGreen1, + LightGreen2, + LightGreen3 + ), + Feature( + title = "Night island", + R.drawable.ic_moon, + OrangeYellow1, + OrangeYellow2, + OrangeYellow3 + ), + Feature( + title = "Calming sounds", + R.drawable.ic_music, + Beige1, + Beige2, + Beige3 + ) + ) + ) + } + BottomMenu( + items = listOf( + BottomMenuContent("Home", R.drawable.ic_home), + BottomMenuContent("Meditate", R.drawable.ic_bubble), + BottomMenuContent("Sleep", R.drawable.ic_moon), + BottomMenuContent("Music", R.drawable.ic_music), + BottomMenuContent("Profile", R.drawable.ic_profile), + ), + modifier = Modifier.align(BottomCenter) + ) + } +} + +@Composable +fun GreetingSection() { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(15.dp) + ) { + Column(verticalArrangement = Arrangement.Center) { + Text( + text = "Good Morning, Kaushal", + style = MaterialTheme.typography.headlineMedium, + color = Color.White + ) + Text( + text = "We wish you have a good day!", + style = MaterialTheme.typography.bodyMedium, + color = Color.White + ) + } + Icon( + painter = painterResource(id = R.drawable.ic_search), + contentDescription = "Search", + tint = Color.White, + modifier = Modifier.size(24.dp) + ) + } +} + +@Composable +fun ChipSection( + chips: List, +) { + var selectedChipIndex by remember { mutableIntStateOf(0) } + LazyRow { + items(chips.size) { + Box(contentAlignment = Center, + modifier = Modifier + .padding(start = 15.dp, top = 15.dp, bottom = 15.dp) + .clickable { + selectedChipIndex = it + } + .clip(RoundedCornerShape(10.dp)) + .background( + if (selectedChipIndex == it) + ButtonBlue + else + DarkerButtonBlue + ) + .padding(15.dp) + ) { + Text(text = chips[it], color = TextWhite) + } + + } + } +} + +@Composable +fun DailyThought(modifier: Modifier) { + Box( + contentAlignment = Center, + modifier = modifier + .clip(RoundedCornerShape(10.dp)) + .background(LightRed) + .padding(15.dp) + ) { + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .padding(15.dp) + ) { + Column(verticalArrangement = Arrangement.Center) { + Text(text = "Daily Thought", style = MaterialTheme.typography.headlineSmall) + Text(text = "Meditation", style = MaterialTheme.typography.bodyMedium) + } + Icon( + painter = painterResource(id = R.drawable.ic_play), + contentDescription = "Play", + tint = Color.White, + modifier = Modifier.size(24.dp) + ) + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +fun FeatureSection(features: List) { + Column(modifier = Modifier.fillMaxSize()) { + Text( + text = "Features", + color = TextWhite, + style = MaterialTheme.typography.headlineMedium, + modifier = Modifier.padding(15.dp) + ) + LazyVerticalGrid( + columns = GridCells.Fixed(2), + contentPadding = PaddingValues( + start = 7.5.dp, + end = 7.5.dp, bottom = 100.dp + ), + modifier = Modifier.fillMaxHeight() + ) { + items(features.size) { + FeatureItem(feature = features[it]) + } + } + } +} + +@Composable +fun FeatureItem(feature: Feature) { + BoxWithConstraints( + modifier = Modifier + .padding(7.5.dp) + .aspectRatio(1f) + .clip(RoundedCornerShape(10.dp)) + .background(feature.darkColor) + ) { + val width = constraints.maxWidth + val height = constraints.maxHeight + + // Medium colored path + val mediumColoredPoint1 = Offset(0f, height * 0.3f) + val mediumColoredPoint2 = Offset(width * 0.1f, height * 0.35f) + val mediumColoredPoint3 = Offset(width * 0.4f, height * 0.05f) + val mediumColoredPoint4 = Offset(width * 0.75f, height * 0.7f) + val mediumColoredPoint5 = Offset(width * 1.4f, -height.toFloat()) + + val mediumColoredPath = Path().apply { + moveTo(mediumColoredPoint1.x, mediumColoredPoint1.y) + standardQuadFromTo(mediumColoredPoint1, mediumColoredPoint2) + standardQuadFromTo(mediumColoredPoint2, mediumColoredPoint3) + standardQuadFromTo(mediumColoredPoint3, mediumColoredPoint4) + standardQuadFromTo(mediumColoredPoint4, mediumColoredPoint5) + lineTo(width.toFloat() + 100f, height.toFloat() + 100f) + lineTo(-100f, height.toFloat() + 100f) + close() + } + + // Light colored path + val lightPoint1 = Offset(0f, height * 0.35f) + val lightPoint2 = Offset(width * 0.1f, height * 0.4f) + val lightPoint3 = Offset(width * 0.3f, height * 0.35f) + val lightPoint4 = Offset(width * 0.65f, height.toFloat()) + val lightPoint5 = Offset(width * 1.4f, -height.toFloat() / 3f) + + val lightColoredPath = Path().apply { + moveTo(lightPoint1.x, lightPoint1.y) + standardQuadFromTo(lightPoint1, lightPoint2) + standardQuadFromTo(lightPoint2, lightPoint3) + standardQuadFromTo(lightPoint3, lightPoint4) + standardQuadFromTo(lightPoint4, lightPoint5) + lineTo(width.toFloat() + 100f, height.toFloat() + 100f) + lineTo(-100f, height.toFloat() + 100f) + close() + } + androidx.compose.foundation.Canvas( + modifier = Modifier + .fillMaxSize() + ) { + drawPath( + path = mediumColoredPath, + color = feature.mediumColor + ) + drawPath( + path = lightColoredPath, + color = feature.lightColor + ) + } + Box( + modifier = Modifier + .fillMaxSize() + .padding(15.dp) + ) { + Text( + text = feature.title, + style = MaterialTheme.typography.headlineMedium, + lineHeight = 22.sp, + modifier = Modifier.align(Alignment.TopStart) + ) + Icon( + painter = painterResource(id = feature.iconId), + contentDescription = feature.title, + tint = Color.White, + modifier = Modifier.align(Alignment.BottomStart) + ) + Text( + text = "Start", + color = TextWhite, + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier + .clickable { + // Handle the click + } + .align(Alignment.BottomEnd) + .clip(RoundedCornerShape(10.dp)) + .background(ButtonBlue) + .padding(vertical = 6.dp, horizontal = 15.dp) + ) + } + } +} + +@Composable +fun BottomMenu( + items: List, + modifier: Modifier = Modifier, + activeHighlightColor: Color = ButtonBlue, + activeTextColor: Color = Color.White, + inactiveTextColor: Color = AquaBlue, + initialSelectedItemIndex: Int = 0, +) { + var selectedItemIndex by remember { + mutableIntStateOf(initialSelectedItemIndex) + } + Row( + horizontalArrangement = Arrangement.SpaceAround, + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + .fillMaxWidth() + .background(DeepBlue) + .padding(15.dp) + ) { + items.forEachIndexed { index, item -> + BottomMenuItem( + item = item, + isSelected = index == selectedItemIndex, + activeHighlightColor = activeHighlightColor, + activeTextColor = activeTextColor, + inactiveTextColor = inactiveTextColor + ) { + selectedItemIndex = index + } + } + } +} + +@Composable +fun BottomMenuItem( + item: BottomMenuContent, + isSelected: Boolean = false, + activeHighlightColor: Color = ButtonBlue, + activeTextColor: Color = Color.White, + inactiveTextColor: Color = AquaBlue, + onItemClick: () -> Unit, +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + modifier = Modifier.clickable { + onItemClick() + } + ) { + Box( + contentAlignment = Center, + modifier = Modifier + .clip(RoundedCornerShape(10.dp)) + .background(if (isSelected) activeHighlightColor else Transparent) + .padding(10.dp) + ) { + Icon( + painter = painterResource(id = item.iconId), + contentDescription = item.title, + tint = if (isSelected) activeTextColor else inactiveTextColor, + modifier = Modifier.size(20.dp) + ) + } + Text( + text = item.title, + color = if (isSelected) activeTextColor else inactiveTextColor + ) + } +} + diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Color.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Color.kt index e0a4930..17ce0eb 100644 --- a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Color.kt +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/ui/theme/Color.kt @@ -8,4 +8,25 @@ val Pink80 = Color(0xFFEFB8C8) val Purple40 = Color(0xFF6650a4) val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file +val Pink40 = Color(0xFF7D5260) + + +val TextWhite = Color(0xffeeeeee) +val DeepBlue = Color(0xff06164c) +val ButtonBlue = Color(0xff505cf3) +val DarkerButtonBlue = Color(0xff566894) +val LightRed = Color(0xfffc879a) +val AquaBlue = Color(0xff9aa5c4) +val OrangeYellow1 = Color(0xfff0bd28) +val OrangeYellow2 = Color(0xfff1c746) +val OrangeYellow3 = Color(0xfff4cf65) +val Beige1 = Color(0xfffdbda1) +val Beige2 = Color(0xfffcaf90) +val Beige3 = Color(0xfff9a27b) +val LightGreen1 = Color(0xff54e1b6) +val LightGreen2 = Color(0xff36ddab) +val LightGreen3 = Color(0xff11d79b) +val BlueViolet1 = Color(0xffaeb4fd) +val BlueViolet2 = Color(0xff9fa5fe) +val BlueViolet3 = Color(0xff8f98fd) +val Transparent = Color(0x00ffffff) diff --git a/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/util/PathUtil.kt b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/util/PathUtil.kt new file mode 100644 index 0000000..492c007 --- /dev/null +++ b/app/src/main/java/com/lahsuak/apps/jetpackcomposebasic/util/PathUtil.kt @@ -0,0 +1,14 @@ +package com.lahsuak.apps.jetpackcomposebasic.util + +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Path +import kotlin.math.abs + +fun Path.standardQuadFromTo(from: Offset, to: Offset) { + quadraticBezierTo( + from.x, + from.y, + abs(from.x + to.x) / 2f, + abs(from.y + to.y) / 2f + ) +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_bubble.xml b/app/src/main/res/drawable/ic_bubble.xml new file mode 100644 index 0000000..f00ab81 --- /dev/null +++ b/app/src/main/res/drawable/ic_bubble.xml @@ -0,0 +1,21 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_headphone.xml b/app/src/main/res/drawable/ic_headphone.xml new file mode 100644 index 0000000..142dcd9 --- /dev/null +++ b/app/src/main/res/drawable/ic_headphone.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_home.xml b/app/src/main/res/drawable/ic_home.xml new file mode 100644 index 0000000..68f271f --- /dev/null +++ b/app/src/main/res/drawable/ic_home.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_moon.xml b/app/src/main/res/drawable/ic_moon.xml new file mode 100644 index 0000000..5b112c5 --- /dev/null +++ b/app/src/main/res/drawable/ic_moon.xml @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_music.xml b/app/src/main/res/drawable/ic_music.xml new file mode 100644 index 0000000..470b857 --- /dev/null +++ b/app/src/main/res/drawable/ic_music.xml @@ -0,0 +1,27 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_play.xml b/app/src/main/res/drawable/ic_play.xml new file mode 100644 index 0000000..9b4a00f --- /dev/null +++ b/app/src/main/res/drawable/ic_play.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_profile.xml b/app/src/main/res/drawable/ic_profile.xml new file mode 100644 index 0000000..abeb4f9 --- /dev/null +++ b/app/src/main/res/drawable/ic_profile.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml new file mode 100644 index 0000000..db7c335 --- /dev/null +++ b/app/src/main/res/drawable/ic_search.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_videocam.xml b/app/src/main/res/drawable/ic_videocam.xml new file mode 100644 index 0000000..fab7fdc --- /dev/null +++ b/app/src/main/res/drawable/ic_videocam.xml @@ -0,0 +1,20 @@ + + + + \ No newline at end of file diff --git a/build.gradle b/build.gradle index e868fc8..b0f2f02 100644 --- a/build.gradle +++ b/build.gradle @@ -1,10 +1,10 @@ buildscript { ext { - compose_version = '1.3.2' + compose_version = '1.4.3' } }// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { - id 'com.android.application' version '7.3.0' apply false - id 'com.android.library' version '7.3.0' apply false - id 'org.jetbrains.kotlin.android' version '1.6.10' apply false + id 'com.android.application' version '8.0.2' apply false + id 'com.android.library' version '8.0.2' apply false + id 'org.jetbrains.kotlin.android' version '1.8.10' apply false } \ No newline at end of file