├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── devsadeq │ │ └── composecinematicketsreservations │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── devsadeq │ │ │ └── composecinematicketsreservations │ │ │ ├── CinemaApplication.kt │ │ │ ├── ui │ │ │ ├── composable │ │ │ │ ├── ActorsImages.kt │ │ │ │ ├── AppChip.kt │ │ │ │ ├── BlurBackground.kt │ │ │ │ ├── CustomButton.kt │ │ │ │ ├── DayItem.kt │ │ │ │ ├── DetailsBackgroundImage.kt │ │ │ │ ├── DetailsBottomSheet.kt │ │ │ │ ├── DetailsDescription.kt │ │ │ │ ├── DetailsHeader.kt │ │ │ │ ├── HomeFilterChips.kt │ │ │ │ ├── HomeOverview.kt │ │ │ │ ├── HomePager.kt │ │ │ │ ├── MovieCategories.kt │ │ │ │ ├── MovieDetailsTitle.kt │ │ │ │ ├── MovieInfoText.kt │ │ │ │ ├── MovieItem.kt │ │ │ │ ├── MovieStatistics.kt │ │ │ │ ├── ReservationDays.kt │ │ │ │ ├── ReservationFooter.kt │ │ │ │ ├── ReservationPriceAndSubmitButton.kt │ │ │ │ ├── ReservationScreenHeader.kt │ │ │ │ ├── ReservationSeats.kt │ │ │ │ ├── ReservationTimes.kt │ │ │ │ ├── RoundedBannerImage.kt │ │ │ │ ├── SeatItem.kt │ │ │ │ ├── SeatPair.kt │ │ │ │ ├── SeatStatusIndicator.kt │ │ │ │ ├── SeatsGrid.kt │ │ │ │ ├── SeatsIndicators.kt │ │ │ │ └── TimeItem.kt │ │ │ ├── main │ │ │ │ ├── AppNavGraph.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ ├── MainScreen.kt │ │ │ │ └── Screen.kt │ │ │ ├── screen │ │ │ │ ├── cart │ │ │ │ │ └── CartScreen.kt │ │ │ │ ├── details │ │ │ │ │ └── DetailsScreen.kt │ │ │ │ ├── home │ │ │ │ │ └── HomeScreen.kt │ │ │ │ ├── profile │ │ │ │ │ └── ProfileScreen.kt │ │ │ │ ├── reservation │ │ │ │ │ └── ReservationScreen.kt │ │ │ │ └── search │ │ │ │ │ └── SearchScreen.kt │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── viewmodel │ │ │ ├── details │ │ │ ├── DetailsUIState.kt │ │ │ └── DetailsViewModel.kt │ │ │ ├── home │ │ │ ├── HomeUIState.kt │ │ │ └── HomeViewModel.kt │ │ │ └── reservation │ │ │ ├── ReservationUIState.kt │ │ │ └── ReservationViewModel.kt │ └── res │ │ ├── drawable │ │ ├── actor_1.jpg │ │ ├── actor_2.jpg │ │ ├── actor_3.webp │ │ ├── actor_4.jpg │ │ ├── actor_5.jpg │ │ ├── actor_6.jpg │ │ ├── actor_7.jpg │ │ ├── actor_8.jpg │ │ ├── banner.jpg │ │ ├── ic_clock.xml │ │ ├── ic_close_circle.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_play.xml │ │ ├── ic_profile.xml │ │ ├── ic_search.xml │ │ ├── ic_ticket.xml │ │ ├── ic_video_play.xml │ │ ├── movie1.png │ │ ├── movie2.png │ │ ├── movie3.png │ │ ├── seat.xml │ │ └── seat_belt.xml │ │ ├── font │ │ ├── opensans_medium.ttf │ │ └── opensans_regular.ttf │ │ ├── mipmap-anydpi │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── devsadeq │ └── composecinematicketsreservations │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration file (sdk path, etc) 6 | local.properties 7 | 8 | # Log/OS Files 9 | *.log 10 | 11 | # Android Studio generated files and folders 12 | captures/ 13 | .externalNativeBuild/ 14 | .cxx/ 15 | *.apk 16 | output.json 17 | 18 | # IntelliJ 19 | *.iml 20 | .idea/ 21 | misc.xml 22 | deploymentTargetDropDown.xml 23 | render.experimental.xml 24 | 25 | # Keystore files 26 | *.jks 27 | *.keystore 28 | 29 | # Google Services (e.g. APIs or Firebase) 30 | google-services.json 31 | 32 | # Android Profiling 33 | *.hprof -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Compose Cinema Tickets Reservations App🎬 2 | 3 | Welcome to the Compose Cinema Tickets Reservations app! This app lets you book cinema tickets with ease, using the awesome power of Jetpack Compose. It has 3 screens. 4 | 5 | ### Features: 6 | - **Animated Slider:** Enjoy a cool animated slider that gives you sneak peeks into the most exciting movies. 7 | - **Intuitive Seat Selection:** Easily pick your seats for the movie you want to watch. You can select multiple seats at once. 8 | - **Smooth Animations:** Experience seamless and smooth interactions with our app's animations. 9 | 10 | ## Demo Video 📽️ 11 | To see the app in action, check out the demo video [here](https://youtube.com/shorts/iiZLfVxJowc?feature=share)! 12 | 13 | ## Screenshots 📸 14 |
15 | Screenshot 1 16 | Screenshot 2 17 | Screenshot 3 18 |
19 | 20 | ## Keywords 21 | Jetpack Compose, Cinema Tickets, Reservations, Animated Slider, Seat Selection, User-Friendly, Smooth Animations, Android App, Blockbuster Movies, Booking Experience, UI Interactions. 22 | 23 | ## Contact 24 | - Email: dev.sadeq@gmail.com 25 | - Linkedin: [@devsadeq](https://www.linkedin.com/in/devsadeq/) 26 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | @Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed 2 | plugins { 3 | alias(libs.plugins.androidApplication) 4 | alias(libs.plugins.kotlinAndroid) 5 | alias(libs.plugins.kotlinKapt) 6 | alias(libs.plugins.hilt) 7 | } 8 | 9 | android { 10 | namespace = "com.devsadeq.composecinematicketsreservations" 11 | compileSdk = 33 12 | 13 | defaultConfig { 14 | applicationId = "com.devsadeq.composecinematicketsreservations" 15 | minSdk = 28 16 | targetSdk = 33 17 | versionCode = 1 18 | versionName = "1.0" 19 | 20 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 21 | vectorDrawables { 22 | useSupportLibrary = true 23 | } 24 | } 25 | 26 | buildTypes { 27 | release { 28 | isMinifyEnabled = false 29 | proguardFiles( 30 | getDefaultProguardFile("proguard-android-optimize.txt"), 31 | "proguard-rules.pro" 32 | ) 33 | } 34 | } 35 | compileOptions { 36 | sourceCompatibility = JavaVersion.VERSION_17 37 | targetCompatibility = JavaVersion.VERSION_17 38 | } 39 | kotlinOptions { 40 | jvmTarget = "17" 41 | } 42 | buildFeatures { 43 | compose = true 44 | } 45 | composeOptions { 46 | kotlinCompilerExtensionVersion = "1.4.3" 47 | } 48 | packaging { 49 | resources { 50 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 51 | } 52 | } 53 | } 54 | 55 | dependencies { 56 | 57 | implementation(libs.core.ktx) 58 | implementation(libs.lifecycle.runtime.ktx) 59 | implementation(libs.activity.compose) 60 | implementation(platform(libs.compose.bom)) 61 | implementation(libs.ui) 62 | implementation(libs.ui.graphics) 63 | implementation(libs.ui.tooling.preview) 64 | implementation(libs.material3) 65 | testImplementation(libs.junit) 66 | androidTestImplementation(libs.androidx.test.ext.junit) 67 | androidTestImplementation(libs.espresso.core) 68 | androidTestImplementation(platform(libs.compose.bom)) 69 | androidTestImplementation(libs.ui.test.junit4) 70 | debugImplementation(libs.ui.tooling) 71 | debugImplementation(libs.ui.test.manifest) 72 | 73 | implementation(libs.lifecycle.viewmodel) 74 | implementation(libs.hilt.android) 75 | implementation(libs.hilt.navigation) 76 | kapt(libs.hilt.android.compiler) 77 | implementation(libs.coil.compose) 78 | implementation(libs.compose.ui.util) 79 | implementation(libs.navigation.compose) 80 | implementation(libs.compose.material) 81 | implementation(libs.hilt.navigation.compose) 82 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/devsadeq/composecinematicketsreservations/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.devsadeq.composecinematicketsreservations", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 17 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/CinemaApplication.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations 2 | 3 | import android.app.Application 4 | import dagger.hilt.android.HiltAndroidApp 5 | 6 | @HiltAndroidApp 7 | class CinemaApplication : Application() -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/ActorsImages.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.PaddingValues 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.foundation.layout.size 9 | import androidx.compose.foundation.layout.width 10 | import androidx.compose.foundation.lazy.LazyRow 11 | import androidx.compose.foundation.shape.RoundedCornerShape 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.draw.clip 15 | import androidx.compose.ui.layout.ContentScale 16 | import androidx.compose.ui.res.painterResource 17 | import androidx.compose.ui.unit.dp 18 | 19 | @Composable 20 | fun ActorsImages( 21 | actors: List 22 | ) { 23 | LazyRow( 24 | modifier = Modifier.padding(vertical = 16.dp), 25 | horizontalArrangement = Arrangement.Center, 26 | contentPadding = PaddingValues(horizontal = 24.dp), 27 | ) { 28 | items(10) { 29 | Image( 30 | painter = painterResource(actors[it]), 31 | contentDescription = null, 32 | modifier = Modifier 33 | .size(80.dp) 34 | .clip(RoundedCornerShape(100.dp)), 35 | contentScale = ContentScale.Crop, 36 | ) 37 | Spacer(modifier = Modifier.width(6.dp)) 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/AppChip.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.BorderStroke 4 | import androidx.compose.foundation.layout.PaddingValues 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.material.Chip 8 | import androidx.compose.material.ChipDefaults 9 | import androidx.compose.material.ExperimentalMaterialApi 10 | import androidx.compose.material3.Text 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.text.TextStyle 15 | import androidx.compose.ui.unit.dp 16 | import androidx.compose.ui.unit.sp 17 | import com.devsadeq.composecinematicketsreservations.ui.theme.Grey 18 | import com.devsadeq.composecinematicketsreservations.ui.theme.Orange 19 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 20 | 21 | 22 | @OptIn(ExperimentalMaterialApi::class) 23 | @Composable 24 | fun AppChip( 25 | label: String, 26 | onClick: () -> Unit, 27 | modifier: Modifier = Modifier, 28 | enabled: Boolean = false, 29 | labelPadding: PaddingValues = PaddingValues(8.dp), 30 | borderColor: Color = Grey, 31 | labelStyle: TextStyle = TextStyle( 32 | fontSize = 14.sp, 33 | letterSpacing = 0.15.sp, 34 | color = White 35 | ) 36 | ) { 37 | Chip( 38 | onClick = onClick, 39 | modifier = modifier, 40 | shape = RoundedCornerShape(50.dp), 41 | colors = ChipDefaults.chipColors( 42 | backgroundColor = Orange, 43 | disabledBackgroundColor = Color.Transparent, 44 | ), 45 | enabled = enabled, 46 | border = if (enabled.not()) BorderStroke(1.dp, borderColor) else null, 47 | ) { 48 | Text( 49 | text = label, 50 | modifier = Modifier.padding(labelPadding), 51 | style = labelStyle, 52 | ) 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/BlurBackground.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.foundation.layout.offset 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.draw.blur 9 | import androidx.compose.ui.res.painterResource 10 | import androidx.compose.ui.unit.dp 11 | 12 | @Composable 13 | fun BlurBackground( 14 | image: Int 15 | ) { 16 | Image( 17 | painter = painterResource(image), 18 | contentDescription = null, 19 | modifier = Modifier 20 | .fillMaxSize() 21 | .blur(80.dp) 22 | .offset(y = (-300).dp) 23 | ) 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/CustomButton.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Row 4 | import androidx.compose.foundation.layout.Spacer 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.layout.size 7 | import androidx.compose.foundation.layout.width 8 | import androidx.compose.material3.Button 9 | import androidx.compose.material3.ButtonDefaults 10 | import androidx.compose.material3.Icon 11 | import androidx.compose.material3.Text 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.res.painterResource 15 | import androidx.compose.ui.res.stringResource 16 | import androidx.compose.ui.text.font.FontWeight 17 | import androidx.compose.ui.unit.dp 18 | import androidx.compose.ui.unit.sp 19 | import com.devsadeq.composecinematicketsreservations.R 20 | import com.devsadeq.composecinematicketsreservations.ui.theme.Orange 21 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 22 | 23 | @Composable 24 | fun CustomButton( 25 | icon: Int = R.drawable.ic_ticket, 26 | text: String = stringResource(R.string.booking), 27 | modifier: Modifier = Modifier, 28 | onClick: () -> Unit = {} 29 | ) { 30 | Button( 31 | onClick = onClick, 32 | modifier = modifier, 33 | colors = ButtonDefaults.buttonColors( 34 | containerColor = Orange 35 | ) 36 | ) { 37 | Row( 38 | modifier = Modifier.padding(8.dp) 39 | ) { 40 | Icon( 41 | painter = painterResource(icon), 42 | contentDescription = null, 43 | tint = White, 44 | modifier = Modifier.size(28.dp) 45 | ) 46 | Spacer(modifier = Modifier.width(8.dp)) 47 | Text( 48 | text, 49 | color = White, 50 | fontSize = 18.sp, 51 | fontWeight = FontWeight.Medium 52 | ) 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/DayItem.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.border 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.foundation.shape.RoundedCornerShape 8 | import androidx.compose.material3.Text 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.draw.clip 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.text.font.FontWeight 15 | import androidx.compose.ui.unit.dp 16 | import androidx.compose.ui.unit.sp 17 | import com.devsadeq.composecinematicketsreservations.ui.theme.Black 18 | import com.devsadeq.composecinematicketsreservations.ui.theme.Grey 19 | import com.devsadeq.composecinematicketsreservations.ui.theme.LightGrey 20 | import com.devsadeq.composecinematicketsreservations.ui.theme.OpenSans 21 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 22 | 23 | 24 | @Composable 25 | fun DayItem( 26 | dayOfMonth: Int, 27 | dayOfWeek: String, 28 | isSelected: Boolean = false, 29 | ) { 30 | Column( 31 | modifier = Modifier 32 | .border(1.dp, if (isSelected) Grey else LightGrey, RoundedCornerShape(24.dp)) 33 | .clip(RoundedCornerShape(24.dp)) 34 | .background(if (isSelected) Grey else Color.Transparent) 35 | .padding(vertical = 12.dp, horizontal = 16.dp), 36 | horizontalAlignment = Alignment.CenterHorizontally, 37 | ) { 38 | Text( 39 | text = dayOfMonth.toString(), 40 | fontSize = 24.sp, 41 | fontWeight = FontWeight.Medium, 42 | color = if (isSelected) White else Black 43 | ) 44 | Text( 45 | text = dayOfWeek, 46 | color = if (isSelected) White else Grey, 47 | fontSize = 12.sp, 48 | fontWeight = FontWeight.Medium, 49 | fontFamily = OpenSans, 50 | ) 51 | } 52 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/DetailsBackgroundImage.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.foundation.layout.offset 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.layout.ContentScale 9 | import androidx.compose.ui.res.painterResource 10 | import androidx.compose.ui.unit.dp 11 | import com.devsadeq.composecinematicketsreservations.R 12 | 13 | @Composable 14 | fun DetailsBackgroundImage(modifier: Modifier = Modifier) { 15 | Image( 16 | painter = painterResource(R.drawable.movie2), 17 | contentDescription = null, 18 | modifier = modifier 19 | .fillMaxSize() 20 | .offset(y = (-200).dp), 21 | contentScale = ContentScale.FillWidth, 22 | ) 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/DetailsBottomSheet.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.material3.Card 8 | import androidx.compose.material3.CardDefaults 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.draw.clip 13 | import androidx.compose.ui.unit.dp 14 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 15 | import com.devsadeq.composecinematicketsreservations.viewmodel.details.DetailsUIState 16 | 17 | @Composable 18 | fun DetailsBottomSheet( 19 | state: DetailsUIState, 20 | onReserveClicked: () -> Unit, 21 | modifier: Modifier = Modifier 22 | ) { 23 | Card( 24 | modifier = modifier 25 | .fillMaxWidth() 26 | .clip( 27 | RoundedCornerShape( 28 | topStart = 40.dp, 29 | topEnd = 40.dp, 30 | bottomStart = 0.dp, 31 | bottomEnd = 0.dp 32 | ) 33 | ), 34 | colors = CardDefaults.cardColors(containerColor = White) 35 | ) { 36 | Column( 37 | modifier = Modifier 38 | .fillMaxWidth() 39 | .padding(vertical = 24.dp), 40 | horizontalAlignment = Alignment.CenterHorizontally, 41 | ) { 42 | MovieStatistics() 43 | MovieTitle() 44 | MovieCategories() 45 | ActorsImages(state.actors) 46 | MovieDescription() 47 | CustomButton(onClick = onReserveClicked, modifier = Modifier.padding(top = 24.dp)) 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/DetailsDescription.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.fillMaxWidth 4 | import androidx.compose.foundation.layout.padding 5 | import androidx.compose.material3.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.res.stringResource 9 | import androidx.compose.ui.text.font.FontWeight 10 | import androidx.compose.ui.text.style.TextAlign 11 | import androidx.compose.ui.text.style.TextOverflow 12 | import androidx.compose.ui.unit.dp 13 | import androidx.compose.ui.unit.sp 14 | import com.devsadeq.composecinematicketsreservations.R 15 | import com.devsadeq.composecinematicketsreservations.ui.theme.OpenSans 16 | 17 | 18 | @Composable 19 | fun MovieDescription() { 20 | Text( 21 | stringResource(R.string.details_description), 22 | modifier = Modifier 23 | .fillMaxWidth() 24 | .padding(horizontal = 24.dp), 25 | fontFamily = OpenSans, 26 | fontWeight = FontWeight.Medium, 27 | maxLines = 3, 28 | textAlign = TextAlign.Center, 29 | overflow = TextOverflow.Ellipsis, 30 | fontSize = 14.sp, 31 | ) 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/DetailsHeader.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.clickable 5 | import androidx.compose.foundation.layout.Arrangement 6 | import androidx.compose.foundation.layout.Box 7 | import androidx.compose.foundation.layout.Column 8 | import androidx.compose.foundation.layout.Row 9 | import androidx.compose.foundation.layout.Spacer 10 | import androidx.compose.foundation.layout.fillMaxHeight 11 | import androidx.compose.foundation.layout.fillMaxSize 12 | import androidx.compose.foundation.layout.fillMaxWidth 13 | import androidx.compose.foundation.layout.height 14 | import androidx.compose.foundation.layout.padding 15 | import androidx.compose.foundation.layout.size 16 | import androidx.compose.foundation.shape.RoundedCornerShape 17 | import androidx.compose.material3.Icon 18 | import androidx.compose.material3.IconButton 19 | import androidx.compose.material3.Text 20 | import androidx.compose.runtime.Composable 21 | import androidx.compose.ui.Alignment 22 | import androidx.compose.ui.Modifier 23 | import androidx.compose.ui.draw.clip 24 | import androidx.compose.ui.res.painterResource 25 | import androidx.compose.ui.res.stringResource 26 | import androidx.compose.ui.text.style.TextAlign 27 | import androidx.compose.ui.unit.dp 28 | import androidx.compose.ui.unit.sp 29 | import com.devsadeq.composecinematicketsreservations.R 30 | import com.devsadeq.composecinematicketsreservations.ui.theme.Orange 31 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 32 | 33 | 34 | @Composable 35 | fun DetailsHeader( 36 | onCloseClicked: () -> Unit 37 | ) { 38 | Column( 39 | modifier = Modifier.fillMaxHeight(), 40 | horizontalAlignment = Alignment.CenterHorizontally, 41 | ) { 42 | Row( 43 | modifier = Modifier 44 | .fillMaxWidth() 45 | .padding(vertical = 24.dp, horizontal = 18.dp), 46 | horizontalArrangement = Arrangement.SpaceBetween, 47 | verticalAlignment = Alignment.CenterVertically, 48 | ) { 49 | Box( 50 | modifier = Modifier 51 | .clip(RoundedCornerShape(30.dp)) 52 | .background(White.copy(alpha = 0.3f)) 53 | .clickable { onCloseClicked() } 54 | ) { 55 | Icon( 56 | painter = painterResource(R.drawable.ic_close_circle), 57 | contentDescription = null, 58 | modifier = Modifier 59 | .padding(8.dp) 60 | .size(28.dp), 61 | tint = White, 62 | ) 63 | } 64 | Box( 65 | modifier = Modifier 66 | .clip(RoundedCornerShape(30.dp)) 67 | .background(White.copy(alpha = 0.3f)) 68 | ) { 69 | Row( 70 | modifier = Modifier 71 | .padding(8.dp) 72 | .align(Alignment.TopCenter), 73 | verticalAlignment = Alignment.CenterVertically, 74 | ) { 75 | Icon( 76 | painter = painterResource(R.drawable.ic_clock), 77 | contentDescription = null, 78 | modifier = Modifier 79 | .padding(end = 4.dp) 80 | .size(18.dp), 81 | tint = White, 82 | ) 83 | Text( 84 | stringResource(R.string._2h_23m), 85 | modifier = Modifier.padding(end = 4.dp), 86 | color = White, 87 | textAlign = TextAlign.Center, 88 | fontSize = 14.sp 89 | ) 90 | } 91 | } 92 | } 93 | Spacer(modifier = Modifier.height(110.dp)) 94 | IconButton( 95 | onClick = {}, 96 | modifier = Modifier.size(65.dp), 97 | ) { 98 | Box( 99 | modifier = Modifier 100 | .fillMaxSize() 101 | .clip(RoundedCornerShape(100.dp)) 102 | .background(Orange), 103 | contentAlignment = Alignment.Center 104 | ) { 105 | Icon( 106 | painter = painterResource(id = R.drawable.ic_play), 107 | tint = White, 108 | contentDescription = "", 109 | modifier = Modifier.size(30.dp) 110 | ) 111 | } 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/HomeFilterChips.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.Spacer 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.height 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.layout.width 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.res.stringResource 13 | import androidx.compose.ui.unit.dp 14 | import com.devsadeq.composecinematicketsreservations.R 15 | 16 | 17 | @Composable 18 | fun HomeFilterChips(modifier: Modifier = Modifier) { 19 | Row( 20 | modifier = modifier 21 | .fillMaxWidth() 22 | .padding(top = 36.dp, bottom = 28.dp), 23 | horizontalArrangement = Arrangement.Center 24 | ) { 25 | AppChip( 26 | label = stringResource(R.string.now_showing), 27 | onClick = {}, 28 | enabled = true, 29 | modifier = Modifier.height(40.dp) 30 | ) 31 | Spacer(modifier = Modifier.width(8.dp)) 32 | AppChip( 33 | label = stringResource(R.string.coming_soon), 34 | onClick = {}, 35 | enabled = false, 36 | modifier = Modifier.height(40.dp) 37 | ) 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/HomeOverview.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.ExperimentalFoundationApi 4 | import androidx.compose.foundation.layout.Arrangement 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.PaddingValues 7 | import androidx.compose.foundation.layout.Row 8 | import androidx.compose.foundation.layout.Spacer 9 | import androidx.compose.foundation.layout.fillMaxWidth 10 | import androidx.compose.foundation.layout.padding 11 | import androidx.compose.foundation.layout.size 12 | import androidx.compose.foundation.layout.width 13 | import androidx.compose.foundation.pager.PagerState 14 | import androidx.compose.material3.Icon 15 | import androidx.compose.material3.MaterialTheme 16 | import androidx.compose.material3.Text 17 | import androidx.compose.runtime.Composable 18 | import androidx.compose.ui.Alignment 19 | import androidx.compose.ui.Modifier 20 | import androidx.compose.ui.res.painterResource 21 | import androidx.compose.ui.text.font.FontWeight 22 | import androidx.compose.ui.text.style.TextAlign 23 | import androidx.compose.ui.unit.dp 24 | import androidx.compose.ui.unit.sp 25 | import com.devsadeq.composecinematicketsreservations.R 26 | import com.devsadeq.composecinematicketsreservations.ui.theme.DarkGrey 27 | import com.devsadeq.composecinematicketsreservations.ui.theme.Grey 28 | import com.devsadeq.composecinematicketsreservations.ui.theme.LightGrey 29 | import com.devsadeq.composecinematicketsreservations.ui.theme.OpenSans 30 | import com.devsadeq.composecinematicketsreservations.viewmodel.home.HomeUIState 31 | 32 | 33 | @OptIn(ExperimentalFoundationApi::class) 34 | @Composable 35 | fun HomeOverview( 36 | state: HomeUIState, 37 | pagerState: PagerState, 38 | ) { 39 | Column( 40 | modifier = Modifier.padding(vertical = 36.dp, horizontal = 60.dp), 41 | horizontalAlignment = Alignment.CenterHorizontally, 42 | ) { 43 | Row( 44 | verticalAlignment = Alignment.CenterVertically, 45 | ) { 46 | Icon( 47 | painter = painterResource(R.drawable.ic_clock), 48 | contentDescription = null, 49 | tint = Grey, 50 | modifier = Modifier.size(16.dp) 51 | ) 52 | Text( 53 | text = state.movies[pagerState.settledPage].duration, 54 | fontWeight = FontWeight.Medium, 55 | fontSize = 14.sp, 56 | modifier = Modifier.padding(start = 4.dp), 57 | ) 58 | } 59 | Text( 60 | text = state.movies[pagerState.settledPage].title.replaceFirstChar { it.uppercase() }, 61 | fontFamily = OpenSans, 62 | fontWeight = FontWeight.Medium, 63 | fontSize = 24.sp, 64 | modifier = Modifier.padding(vertical = 8.dp), 65 | textAlign = TextAlign.Center, 66 | lineHeight = 32.sp 67 | ) 68 | Row( 69 | modifier = Modifier 70 | .fillMaxWidth(), 71 | horizontalArrangement = Arrangement.Center, 72 | ) { 73 | state.movies[pagerState.settledPage].genres.forEach { genre -> 74 | AppChip( 75 | label = genre, 76 | onClick = {}, 77 | labelPadding = PaddingValues(2.dp), 78 | borderColor = LightGrey, 79 | labelStyle = MaterialTheme.typography.labelMedium.copy( 80 | fontSize = 14.sp, 81 | letterSpacing = 0.15.sp, 82 | color = DarkGrey, 83 | fontWeight = FontWeight.Bold 84 | ) 85 | ) 86 | Spacer(modifier = Modifier.width(4.dp)) 87 | } 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/HomePager.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.animation.core.animateFloatAsState 4 | import androidx.compose.foundation.ExperimentalFoundationApi 5 | import androidx.compose.foundation.clickable 6 | import androidx.compose.foundation.layout.PaddingValues 7 | import androidx.compose.foundation.pager.HorizontalPager 8 | import androidx.compose.foundation.pager.PagerState 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Modifier 11 | import androidx.compose.ui.draw.scale 12 | import androidx.compose.ui.unit.dp 13 | import com.devsadeq.composecinematicketsreservations.viewmodel.home.HomeUIState 14 | 15 | @OptIn(ExperimentalFoundationApi::class) 16 | @Composable 17 | fun HomePager( 18 | images: List, 19 | modifier: Modifier = Modifier, 20 | pagerState: PagerState, 21 | onItemClicked: () -> Unit 22 | ) { 23 | HorizontalPager( 24 | pageCount = images.size, 25 | contentPadding = PaddingValues(horizontal = 60.dp), 26 | pageSpacing = (-15).dp, 27 | state = pagerState, 28 | ) { page -> 29 | val scale = animateFloatAsState( 30 | targetValue = if (page == pagerState.currentPage) 1.0f else 0.8f, 31 | label = "" 32 | ).value 33 | 34 | MovieItem( 35 | image = images[page].imageRes, 36 | modifier = modifier 37 | .scale(scale) 38 | .clickable { onItemClicked() } 39 | ) 40 | } 41 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/MovieCategories.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.PaddingValues 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.Spacer 6 | import androidx.compose.foundation.layout.width 7 | import androidx.compose.foundation.layout.wrapContentSize 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.res.stringResource 11 | import androidx.compose.ui.text.TextStyle 12 | import androidx.compose.ui.text.font.FontWeight 13 | import androidx.compose.ui.unit.dp 14 | import androidx.compose.ui.unit.sp 15 | import com.devsadeq.composecinematicketsreservations.R 16 | import com.devsadeq.composecinematicketsreservations.ui.theme.DarkGrey 17 | import com.devsadeq.composecinematicketsreservations.ui.theme.LightGrey 18 | import com.devsadeq.composecinematicketsreservations.ui.theme.OpenSans 19 | 20 | 21 | @Composable 22 | fun MovieCategories() { 23 | Row( 24 | modifier = Modifier.wrapContentSize(), 25 | ) { 26 | AppChip( 27 | label = stringResource(R.string.fantasy), 28 | onClick = {}, 29 | labelPadding = PaddingValues(2.dp), 30 | borderColor = LightGrey, 31 | labelStyle = TextStyle().copy( 32 | fontFamily = OpenSans, 33 | fontSize = 16.sp, 34 | letterSpacing = 0.15.sp, 35 | color = DarkGrey, 36 | fontWeight = FontWeight.SemiBold 37 | ) 38 | ) 39 | Spacer(modifier = Modifier.width(4.dp)) 40 | AppChip( 41 | label = stringResource(R.string.adventure), 42 | onClick = {}, 43 | labelPadding = PaddingValues(2.dp), 44 | borderColor = LightGrey, 45 | labelStyle = TextStyle().copy( 46 | fontFamily = OpenSans, 47 | fontSize = 16.sp, 48 | letterSpacing = 0.15.sp, 49 | color = DarkGrey, 50 | fontWeight = FontWeight.SemiBold 51 | ) 52 | ) 53 | } 54 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/MovieDetailsTitle.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.padding 4 | import androidx.compose.material3.Text 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.ui.Modifier 7 | import androidx.compose.ui.res.stringResource 8 | import androidx.compose.ui.text.font.FontWeight 9 | import androidx.compose.ui.text.style.TextAlign 10 | import androidx.compose.ui.unit.dp 11 | import androidx.compose.ui.unit.sp 12 | import com.devsadeq.composecinematicketsreservations.R 13 | 14 | @Composable 15 | fun MovieTitle() { 16 | Text( 17 | text = stringResource(R.string.fantastic_beasts_the_secrets_of_dumbledore).replaceFirstChar { it.uppercase() }, 18 | fontWeight = FontWeight.Medium, 19 | fontSize = 28.sp, 20 | lineHeight = 38.sp, 21 | modifier = Modifier.padding(vertical = 16.dp, horizontal = 24.dp), 22 | textAlign = TextAlign.Center, 23 | ) 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/MovieInfoText.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.material3.Text 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Alignment 9 | import androidx.compose.ui.text.font.FontWeight 10 | import androidx.compose.ui.text.style.TextAlign 11 | import androidx.compose.ui.unit.sp 12 | import com.devsadeq.composecinematicketsreservations.ui.theme.Black 13 | import com.devsadeq.composecinematicketsreservations.ui.theme.Grey 14 | import com.devsadeq.composecinematicketsreservations.ui.theme.OpenSans 15 | 16 | @Composable 17 | fun DetailsInfoText( 18 | title: String, 19 | subtitle: String, 20 | isRating: Boolean = false, 21 | ) { 22 | Column( 23 | horizontalAlignment = Alignment.CenterHorizontally, 24 | ) { 25 | Row( 26 | horizontalArrangement = Arrangement.Center, 27 | ) { 28 | Text( 29 | title, 30 | fontSize = 20.sp, 31 | fontWeight = FontWeight.Medium, 32 | color = Black, 33 | textAlign = TextAlign.Center, 34 | fontFamily = OpenSans 35 | ) 36 | if (isRating) Text( 37 | "/10", 38 | fontSize = 20.sp, 39 | fontWeight = FontWeight.Medium, 40 | color = Grey, 41 | fontFamily = OpenSans 42 | ) 43 | } 44 | Text( 45 | subtitle, 46 | fontSize = 16.sp, 47 | fontWeight = FontWeight.Normal, 48 | color = Grey, 49 | textAlign = TextAlign.Center, 50 | fontFamily = OpenSans 51 | ) 52 | } 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/MovieItem.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.layout.height 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.draw.clip 10 | import androidx.compose.ui.layout.ContentScale 11 | import androidx.compose.ui.res.painterResource 12 | import androidx.compose.ui.tooling.preview.Preview 13 | import androidx.compose.ui.unit.dp 14 | import com.devsadeq.composecinematicketsreservations.R 15 | 16 | @Composable 17 | fun MovieItem( 18 | image: Int, 19 | modifier: Modifier = Modifier, 20 | ) { 21 | Image( 22 | painter = painterResource(image), 23 | contentDescription = null, 24 | modifier = modifier 25 | .fillMaxWidth() 26 | .height(550.dp) 27 | .clip(RoundedCornerShape(30.dp)), 28 | contentScale = ContentScale.Crop, 29 | ) 30 | } 31 | 32 | @Preview(showBackground = true) 33 | @Composable 34 | fun MovieItemPreview() { 35 | MovieItem(image = R.drawable.movie1) 36 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/MovieStatistics.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.res.stringResource 10 | import androidx.compose.ui.unit.dp 11 | import com.devsadeq.composecinematicketsreservations.R 12 | 13 | @Composable 14 | fun MovieStatistics() { 15 | Row( 16 | modifier = Modifier 17 | .fillMaxWidth() 18 | .padding(horizontal = 24.dp), 19 | horizontalArrangement = Arrangement.SpaceEvenly, 20 | ) { 21 | DetailsInfoText(stringResource(R.string._6_8), stringResource(R.string.imdb), true) 22 | DetailsInfoText(stringResource(R.string._36), stringResource(R.string.rotten_tomatoes)) 23 | DetailsInfoText(stringResource(R.string._4), stringResource(R.string.ign), true) 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/ReservationDays.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.PaddingValues 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.foundation.lazy.LazyRow 7 | import androidx.compose.foundation.lazy.items 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.unit.dp 11 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.ReservationUIState 12 | 13 | @Composable 14 | fun ReservationDays(state: ReservationUIState) { 15 | LazyRow( 16 | modifier = Modifier.fillMaxWidth(), 17 | contentPadding = PaddingValues(horizontal = 18.dp, vertical = 5.dp), 18 | horizontalArrangement = Arrangement.spacedBy(4.dp), 19 | ) { 20 | items(state.days) { 21 | DayItem( 22 | dayOfMonth = it.dayOfMonth, 23 | dayOfWeek = it.dayOfWeek, 24 | isSelected = it.isSelected 25 | ) 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/ReservationFooter.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.shape.RoundedCornerShape 7 | import androidx.compose.material3.Card 8 | import androidx.compose.material3.CardDefaults 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.draw.clip 13 | import androidx.compose.ui.unit.dp 14 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 15 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.ReservationUIState 16 | 17 | @Composable 18 | fun ReservationScreenFooter( 19 | state: ReservationUIState, 20 | modifier: Modifier = Modifier, 21 | ) { 22 | Card( 23 | modifier = modifier 24 | .fillMaxWidth() 25 | .clip( 26 | RoundedCornerShape( 27 | topStart = 40.dp, 28 | topEnd = 40.dp, 29 | bottomStart = 0.dp, 30 | bottomEnd = 0.dp 31 | ) 32 | ), 33 | colors = CardDefaults.cardColors( 34 | containerColor = White 35 | ) 36 | ) { 37 | Column( 38 | modifier = Modifier 39 | .fillMaxWidth() 40 | .padding(vertical = 30.dp), 41 | horizontalAlignment = Alignment.CenterHorizontally, 42 | ) { 43 | ReservationDays(state) 44 | ReservationTimes(state) 45 | ReservationPriceAndSubmitButton() 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/ReservationPriceAndSubmitButton.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.fillMaxWidth 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.material3.Text 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.res.stringResource 13 | import androidx.compose.ui.text.font.FontWeight 14 | import androidx.compose.ui.tooling.preview.Preview 15 | import androidx.compose.ui.unit.dp 16 | import androidx.compose.ui.unit.sp 17 | import com.devsadeq.composecinematicketsreservations.R 18 | import com.devsadeq.composecinematicketsreservations.ui.theme.Black 19 | import com.devsadeq.composecinematicketsreservations.ui.theme.DarkGrey 20 | import com.devsadeq.composecinematicketsreservations.ui.theme.OpenSans 21 | 22 | @Composable 23 | fun ReservationPriceAndSubmitButton() { 24 | Row( 25 | modifier = Modifier 26 | .fillMaxWidth() 27 | .padding(horizontal = 16.dp) 28 | .padding(top = 20.dp, bottom = 10.dp), 29 | verticalAlignment = Alignment.CenterVertically, 30 | horizontalArrangement = Arrangement.SpaceBetween, 31 | ) { 32 | Column { 33 | Text( 34 | text = stringResource(R.string._100_00), 35 | fontSize = 36.sp, 36 | fontWeight = FontWeight.Medium, 37 | fontFamily = OpenSans, 38 | color = Black 39 | ) 40 | Text( 41 | text = stringResource(R.string._4_tickets), 42 | fontSize = 14.sp, 43 | fontWeight = FontWeight.Normal, 44 | fontFamily = OpenSans, 45 | color = DarkGrey 46 | ) 47 | } 48 | CustomButton( 49 | icon = R.drawable.ic_ticket, 50 | text = stringResource(R.string.buy_tickets), 51 | ) 52 | } 53 | } 54 | 55 | @Preview 56 | @Composable 57 | fun ReservationPriceAndSubmitButtonPreview() { 58 | ReservationPriceAndSubmitButton() 59 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/ReservationScreenHeader.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.clickable 5 | import androidx.compose.foundation.layout.Box 6 | import androidx.compose.foundation.layout.Column 7 | import androidx.compose.foundation.layout.padding 8 | import androidx.compose.foundation.layout.size 9 | import androidx.compose.foundation.shape.RoundedCornerShape 10 | import androidx.compose.material3.Icon 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.draw.clip 14 | import androidx.compose.ui.res.painterResource 15 | import androidx.compose.ui.unit.dp 16 | import com.devsadeq.composecinematicketsreservations.R 17 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 18 | 19 | 20 | @Composable 21 | fun ReservationScreenHeader(modifier: Modifier, onCloseClicked: () -> Unit) { 22 | Column( 23 | modifier = modifier.padding(vertical = 16.dp, horizontal = 16.dp), 24 | ) { 25 | Box( 26 | modifier = Modifier 27 | .clip(RoundedCornerShape(30.dp)) 28 | .background(White.copy(alpha = 0.3f)) 29 | .clickable { onCloseClicked() } 30 | ) { 31 | Icon( 32 | painter = painterResource(R.drawable.ic_close_circle), 33 | contentDescription = null, 34 | modifier = Modifier 35 | .padding(8.dp) 36 | .size(28.dp), 37 | tint = White, 38 | ) 39 | } 40 | RoundedBannerImage() 41 | } 42 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/ReservationSeats.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.ui.Modifier 6 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.SeatUIState 7 | 8 | @Composable 9 | fun ReservationScreenSeats( 10 | seatsPairs: List>, 11 | onSeatClick: (id: Int) -> Unit, 12 | modifier: Modifier = Modifier 13 | ) { 14 | Column( 15 | modifier = modifier 16 | ) { 17 | SeatsGrid(seatsPairs = seatsPairs, onSeatClick = onSeatClick) 18 | SeatsIndicators() 19 | } 20 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/ReservationTimes.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.PaddingValues 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.foundation.lazy.LazyRow 7 | import androidx.compose.foundation.lazy.items 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.unit.dp 11 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.ReservationUIState 12 | 13 | @Composable 14 | fun ReservationTimes(state: ReservationUIState) { 15 | LazyRow( 16 | modifier = Modifier.fillMaxWidth(), 17 | contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp), 18 | horizontalArrangement = Arrangement.spacedBy(4.dp), 19 | ) { 20 | items(state.times) { 21 | TimeItem(it.time, it.isSelected) 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/RoundedBannerImage.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.Image 4 | import androidx.compose.foundation.layout.fillMaxWidth 5 | import androidx.compose.foundation.layout.height 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.draw.drawWithContent 10 | import androidx.compose.ui.graphics.Path 11 | import androidx.compose.ui.graphics.drawscope.clipPath 12 | import androidx.compose.ui.graphics.graphicsLayer 13 | import androidx.compose.ui.layout.ContentScale 14 | import androidx.compose.ui.res.painterResource 15 | import androidx.compose.ui.unit.dp 16 | import com.devsadeq.composecinematicketsreservations.R 17 | 18 | @Composable 19 | fun RoundedBannerImage(modifier: Modifier = Modifier) { 20 | Image( 21 | painter = painterResource(id = R.drawable.banner), 22 | contentDescription = "", 23 | modifier = modifier 24 | .fillMaxWidth() 25 | .height(170.dp) 26 | .padding(16.dp) 27 | .drawWithContent { 28 | val path = Path().apply { 29 | val yRatio = 0.35f 30 | moveTo(0f, size.height * yRatio) 31 | lineTo(0f, size.height) 32 | quadraticBezierTo( 33 | size.width / 2, 34 | size.height * 0.6f, 35 | size.width, 36 | size.height 37 | ) 38 | lineTo(size.width, size.height * yRatio) 39 | quadraticBezierTo( 40 | size.width / 2, 41 | 0f, 42 | 0f, 43 | size.height * yRatio, 44 | ) 45 | } 46 | clipPath(path = path) { 47 | this@drawWithContent.drawContent() 48 | } 49 | } 50 | .graphicsLayer { this.rotationX = rotationX }, 51 | contentScale = ContentScale.FillWidth 52 | ) 53 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/SeatItem.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.clickable 4 | import androidx.compose.foundation.layout.size 5 | import androidx.compose.material.Icon 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Modifier 8 | import androidx.compose.ui.res.painterResource 9 | import androidx.compose.ui.unit.dp 10 | import com.devsadeq.composecinematicketsreservations.R 11 | import com.devsadeq.composecinematicketsreservations.ui.theme.DarkGrey 12 | import com.devsadeq.composecinematicketsreservations.ui.theme.Orange 13 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 14 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.SeatUIState 15 | 16 | @Composable 17 | fun SeatItem( 18 | seatUIState: SeatUIState, 19 | onSeatClick: (id: Int) -> Unit 20 | ) { 21 | Icon( 22 | painterResource(R.drawable.seat), 23 | contentDescription = null, 24 | tint = when { 25 | seatUIState.isReserved -> DarkGrey 26 | seatUIState.isSelected -> Orange 27 | else -> White 28 | }, 29 | modifier = Modifier 30 | .size(36.dp) 31 | .clickable(enabled = !seatUIState.isReserved) { 32 | onSeatClick(seatUIState.id) 33 | } 34 | ) 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/SeatPair.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.fillMaxWidth 8 | import androidx.compose.foundation.layout.height 9 | import androidx.compose.foundation.layout.padding 10 | import androidx.compose.foundation.layout.width 11 | import androidx.compose.material.Icon 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.Alignment 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.res.painterResource 16 | import androidx.compose.ui.unit.dp 17 | import com.devsadeq.composecinematicketsreservations.R 18 | import com.devsadeq.composecinematicketsreservations.ui.theme.DarkGrey 19 | import com.devsadeq.composecinematicketsreservations.ui.theme.LightOrange 20 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.SeatUIState 21 | 22 | 23 | @Composable 24 | fun SeatPair( 25 | seatPair: Pair, 26 | modifier: Modifier = Modifier, 27 | onSeatClick: (id: Int) -> Unit 28 | ) { 29 | Box( 30 | modifier = modifier, 31 | contentAlignment = Alignment.Center 32 | ) { 33 | Icon( 34 | painter = painterResource(R.drawable.seat_belt), contentDescription = null, 35 | tint = if (seatPair.first.isSelected && seatPair.second.isSelected) LightOrange else DarkGrey, 36 | modifier = Modifier 37 | .fillMaxWidth() 38 | .height(45.dp) 39 | .padding(top = 6.dp), 40 | ) 41 | Row( 42 | modifier = Modifier.fillMaxWidth(), 43 | horizontalArrangement = Arrangement.Center 44 | ) { 45 | SeatItem(seatPair.first, onSeatClick = onSeatClick) 46 | Spacer(modifier = Modifier.width(4.dp)) 47 | SeatItem(seatPair.second, onSeatClick = onSeatClick) 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/SeatStatusIndicator.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.Row 6 | import androidx.compose.foundation.layout.Spacer 7 | import androidx.compose.foundation.layout.size 8 | import androidx.compose.foundation.layout.width 9 | import androidx.compose.foundation.shape.CircleShape 10 | import androidx.compose.material3.Text 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Alignment 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.draw.clip 15 | import androidx.compose.ui.graphics.Color 16 | import androidx.compose.ui.text.font.FontWeight 17 | import androidx.compose.ui.unit.dp 18 | import androidx.compose.ui.unit.sp 19 | import com.devsadeq.composecinematicketsreservations.ui.theme.Grey 20 | import com.devsadeq.composecinematicketsreservations.ui.theme.OpenSans 21 | 22 | 23 | @Composable 24 | fun SeatStatusIndicator( 25 | label: String, 26 | color: Color 27 | ) { 28 | Row( 29 | verticalAlignment = Alignment.CenterVertically 30 | ) { 31 | Box( 32 | modifier = Modifier 33 | .size(14.dp) 34 | .clip(shape = CircleShape) 35 | .background(color) 36 | ) 37 | Spacer(modifier = Modifier.width(8.dp)) 38 | Text( 39 | text = label, 40 | color = Grey, 41 | fontFamily = OpenSans, 42 | fontWeight = FontWeight.Medium, 43 | fontSize = 14.sp 44 | ) 45 | } 46 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/SeatsGrid.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.PaddingValues 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.lazy.grid.GridCells 7 | import androidx.compose.foundation.lazy.grid.LazyVerticalGrid 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.draw.rotate 11 | import androidx.compose.ui.unit.dp 12 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.SeatUIState 13 | 14 | @Composable 15 | fun SeatsGrid( 16 | seatsPairs: List>, 17 | onSeatClick: (id: Int) -> Unit 18 | ) { 19 | val leftSeatsIndexes = listOf(0, 3, 6, 9, 12) 20 | val rightSeatsIndexes = listOf(2, 5, 8, 11, 14) 21 | 22 | LazyVerticalGrid( 23 | columns = GridCells.Fixed(3), 24 | contentPadding = PaddingValues(0.dp), 25 | verticalArrangement = Arrangement.spacedBy(8.dp), 26 | ) { 27 | items(seatsPairs.size) { index -> 28 | when (index) { 29 | in leftSeatsIndexes -> SeatPair( 30 | seatsPairs[index], 31 | modifier = Modifier.rotate(10f), 32 | onSeatClick = onSeatClick 33 | ) 34 | 35 | in rightSeatsIndexes -> SeatPair( 36 | seatsPairs[index], 37 | modifier = Modifier.rotate(-10f), 38 | onSeatClick = onSeatClick 39 | ) 40 | 41 | else -> SeatPair( 42 | seatsPairs[index], 43 | modifier = Modifier.padding(top = 9.dp), 44 | onSeatClick = onSeatClick 45 | ) 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/SeatsIndicators.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Row 5 | import androidx.compose.foundation.layout.fillMaxWidth 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.res.stringResource 10 | import androidx.compose.ui.unit.dp 11 | import com.devsadeq.composecinematicketsreservations.R 12 | import com.devsadeq.composecinematicketsreservations.ui.theme.DarkGrey 13 | import com.devsadeq.composecinematicketsreservations.ui.theme.Orange 14 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 15 | 16 | @Composable 17 | fun SeatsIndicators() { 18 | Row( 19 | modifier = Modifier 20 | .fillMaxWidth() 21 | .padding(30.dp), 22 | horizontalArrangement = Arrangement.SpaceBetween, 23 | ) { 24 | SeatStatusIndicator(stringResource(R.string.available), color = White) 25 | SeatStatusIndicator(stringResource(R.string.taken), color = DarkGrey) 26 | SeatStatusIndicator(stringResource(R.string.selected), color = Orange) 27 | } 28 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/composable/TimeItem.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.composable 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.border 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.foundation.shape.RoundedCornerShape 8 | import androidx.compose.material3.Text 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.draw.clip 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.text.font.FontWeight 15 | import androidx.compose.ui.unit.dp 16 | import androidx.compose.ui.unit.sp 17 | import com.devsadeq.composecinematicketsreservations.ui.theme.Black 18 | import com.devsadeq.composecinematicketsreservations.ui.theme.Grey 19 | import com.devsadeq.composecinematicketsreservations.ui.theme.LightGrey 20 | import com.devsadeq.composecinematicketsreservations.ui.theme.OpenSans 21 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 22 | 23 | 24 | @Composable 25 | fun TimeItem( 26 | time: String, 27 | isSelected: Boolean, 28 | ) { 29 | Column( 30 | modifier = Modifier 31 | .clip(RoundedCornerShape(24.dp)) 32 | .background(if (isSelected) Grey else Color.Transparent) 33 | .border(1.dp, if (isSelected) Grey else LightGrey, RoundedCornerShape(24.dp)), 34 | horizontalAlignment = Alignment.CenterHorizontally, 35 | ) { 36 | Text( 37 | modifier = Modifier.padding(vertical = 8.dp, horizontal = 12.dp), 38 | text = time, 39 | fontSize = 18.sp, 40 | fontFamily = OpenSans, 41 | fontWeight = FontWeight.Medium, 42 | color = if (isSelected) White else Black 43 | ) 44 | } 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/main/AppNavGraph.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.main 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.navigation.NavHostController 5 | import androidx.navigation.compose.NavHost 6 | import androidx.navigation.compose.composable 7 | import com.devsadeq.composecinematicketsreservations.ui.screen.cart.CartScreen 8 | import com.devsadeq.composecinematicketsreservations.ui.screen.details.DetailsScreen 9 | import com.devsadeq.composecinematicketsreservations.ui.screen.home.HomeScreen 10 | import com.devsadeq.composecinematicketsreservations.ui.screen.profile.ProfileScreen 11 | import com.devsadeq.composecinematicketsreservations.ui.screen.reservation.ReservationScreen 12 | import com.devsadeq.composecinematicketsreservations.ui.screen.search.SearchScreen 13 | 14 | @Composable 15 | fun AppNavGraph(navHostController: NavHostController) { 16 | NavHost( 17 | navController = navHostController, 18 | startDestination = Screen.Home.route, 19 | ) { 20 | composable(Screen.Home.route) { 21 | HomeScreen(navController = navHostController) 22 | } 23 | composable(Screen.Search.route) { 24 | SearchScreen() 25 | } 26 | composable(Screen.Cart.route) { 27 | CartScreen() 28 | } 29 | composable(Screen.Profile.route) { 30 | ProfileScreen() 31 | } 32 | composable(Screen.Details.route) { 33 | DetailsScreen(navController = navHostController) 34 | } 35 | composable(Screen.Reservation.route) { 36 | ReservationScreen(navController = navHostController) 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/main/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.main 2 | 3 | import android.os.Bundle 4 | import androidx.activity.ComponentActivity 5 | import androidx.activity.compose.setContent 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.tooling.preview.Preview 8 | import dagger.hilt.android.AndroidEntryPoint 9 | 10 | @AndroidEntryPoint 11 | class MainActivity : ComponentActivity() { 12 | override fun onCreate(savedInstanceState: Bundle?) { 13 | super.onCreate(savedInstanceState) 14 | setContent { 15 | MainScreen() 16 | } 17 | } 18 | } 19 | 20 | @Preview(showBackground = true) 21 | @Composable 22 | fun MainPreview() { 23 | MainScreen() 24 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/main/MainScreen.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.main 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.RowScope 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.foundation.layout.height 8 | import androidx.compose.foundation.layout.padding 9 | import androidx.compose.foundation.layout.size 10 | import androidx.compose.foundation.shape.RoundedCornerShape 11 | import androidx.compose.material.BottomNavigation 12 | import androidx.compose.material.BottomNavigationItem 13 | import androidx.compose.material3.ExperimentalMaterial3Api 14 | import androidx.compose.material3.Icon 15 | import androidx.compose.material3.MaterialTheme 16 | import androidx.compose.material3.Scaffold 17 | import androidx.compose.material3.Surface 18 | import androidx.compose.runtime.Composable 19 | import androidx.compose.runtime.getValue 20 | import androidx.compose.ui.Modifier 21 | import androidx.compose.ui.draw.clip 22 | import androidx.compose.ui.res.painterResource 23 | import androidx.compose.ui.tooling.preview.Preview 24 | import androidx.compose.ui.unit.dp 25 | import androidx.navigation.NavHostController 26 | import androidx.navigation.compose.currentBackStackEntryAsState 27 | import androidx.navigation.compose.rememberNavController 28 | import com.devsadeq.composecinematicketsreservations.ui.theme.ComposeCinemaTicketsReservationsTheme 29 | import com.devsadeq.composecinematicketsreservations.ui.theme.Orange 30 | import com.devsadeq.composecinematicketsreservations.ui.theme.White 31 | 32 | @Composable 33 | fun MainScreen() { 34 | ComposeCinemaTicketsReservationsTheme { 35 | Surface( 36 | modifier = Modifier.fillMaxSize(), 37 | color = MaterialTheme.colorScheme.background 38 | ) { 39 | MainScreenContent() 40 | } 41 | } 42 | } 43 | 44 | @OptIn(ExperimentalMaterial3Api::class) 45 | @Composable 46 | fun MainScreenContent() { 47 | val navController = rememberNavController() 48 | val navStackBackEntry by navController.currentBackStackEntryAsState() 49 | val currentDestination = navStackBackEntry?.destination 50 | 51 | Scaffold( 52 | bottomBar = if (currentDestination?.route != Screen.Details.route && currentDestination?.route != Screen.Reservation.route) { 53 | { BottomNavBar(navController) } 54 | } else { 55 | {} 56 | } 57 | ) { innerPadding -> 58 | Box(modifier = Modifier.padding(innerPadding)) { 59 | AppNavGraph(navController) 60 | } 61 | } 62 | } 63 | 64 | @Composable 65 | fun BottomNavBar(navController: NavHostController) { 66 | val screens = listOf( 67 | Screen.Home, 68 | Screen.Search, 69 | Screen.Cart, 70 | Screen.Profile 71 | ) 72 | val navBackStackEntry by navController.currentBackStackEntryAsState() 73 | val currentRoute = navBackStackEntry?.destination?.route 74 | 75 | BottomNavigation( 76 | modifier = Modifier.height(80.dp), 77 | backgroundColor = White, 78 | elevation = 0.dp, 79 | ) { 80 | screens.forEach { screen -> 81 | AddItem( 82 | selected = currentRoute == screen.route, 83 | screen = screen, 84 | navController = navController 85 | ) 86 | } 87 | } 88 | } 89 | 90 | @Composable 91 | fun RowScope.AddItem( 92 | selected: Boolean = false, 93 | screen: Screen, 94 | navController: NavHostController 95 | ) { 96 | BottomNavigationItem( 97 | icon = { 98 | if (selected) { 99 | Box( 100 | modifier = Modifier 101 | .clip(RoundedCornerShape(50.dp)) 102 | .background(Orange) 103 | .padding(16.dp), 104 | ) { 105 | Icon( 106 | painter = painterResource(id = screen.icon), 107 | contentDescription = screen.title, 108 | tint = MaterialTheme.colorScheme.onPrimary, 109 | modifier = Modifier.size(24.dp) 110 | ) 111 | } 112 | } else { 113 | Icon( 114 | painter = painterResource(id = screen.icon), 115 | contentDescription = screen.title, 116 | modifier = Modifier.size(24.dp) 117 | ) 118 | } 119 | }, 120 | selected = selected, 121 | onClick = { 122 | navController.navigate(screen.route) { 123 | popUpTo(navController.graph.startDestinationId) 124 | launchSingleTop = true 125 | } 126 | }, 127 | alwaysShowLabel = false, 128 | ) 129 | } 130 | 131 | @Preview(showBackground = true, showSystemUi = true, apiLevel = 31) 132 | @Composable 133 | fun MainScreenPreview() { 134 | MainScreen() 135 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/main/Screen.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.main 2 | 3 | import com.devsadeq.composecinematicketsreservations.R 4 | 5 | sealed class Screen( 6 | val route: String, 7 | val title: String, 8 | val icon: Int 9 | ) { 10 | object Home : Screen("home", "Home", R.drawable.ic_video_play) 11 | object Search : Screen("search", "Search", R.drawable.ic_search) 12 | object Cart : Screen("cart", "Cart", R.drawable.ic_ticket) 13 | object Profile : Screen("profile", "Profile", R.drawable.ic_profile) 14 | object Details : Screen("details", "Details", R.drawable.ic_profile) 15 | object Reservation : Screen("reservation", "Reservation", R.drawable.ic_profile) 16 | 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/screen/cart/CartScreen.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.screen.cart 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.material3.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Alignment 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.tooling.preview.Preview 10 | import androidx.compose.ui.unit.sp 11 | 12 | @Composable 13 | fun CartScreen() { 14 | CartScreenContent() 15 | } 16 | 17 | @Composable 18 | private fun CartScreenContent() { 19 | Column( 20 | modifier = Modifier, 21 | horizontalAlignment = Alignment.CenterHorizontally, 22 | verticalArrangement = Arrangement.Center, 23 | ) { 24 | Text(text = "Cart Screen", fontSize = 30.sp) 25 | } 26 | } 27 | 28 | @Preview(showBackground = true, showSystemUi = true, apiLevel = 31) 29 | @Composable 30 | fun CartScreenPreview() { 31 | CartScreen() 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/screen/details/DetailsScreen.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.screen.details 2 | 3 | import androidx.compose.foundation.layout.Box 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.runtime.Composable 6 | import androidx.compose.runtime.collectAsState 7 | import androidx.compose.runtime.getValue 8 | import androidx.compose.ui.Alignment 9 | import androidx.compose.ui.Modifier 10 | import androidx.hilt.navigation.compose.hiltViewModel 11 | import androidx.navigation.NavController 12 | import com.devsadeq.composecinematicketsreservations.ui.composable.DetailsBackgroundImage 13 | import com.devsadeq.composecinematicketsreservations.ui.composable.DetailsBottomSheet 14 | import com.devsadeq.composecinematicketsreservations.ui.composable.DetailsHeader 15 | import com.devsadeq.composecinematicketsreservations.ui.main.Screen 16 | import com.devsadeq.composecinematicketsreservations.viewmodel.details.DetailsUIState 17 | import com.devsadeq.composecinematicketsreservations.viewmodel.details.DetailsViewModel 18 | 19 | @Composable 20 | fun DetailsScreen( 21 | navController: NavController, 22 | viewModel: DetailsViewModel = hiltViewModel() 23 | ) { 24 | val state by viewModel.state.collectAsState() 25 | DetailsScreenContent( 26 | state, 27 | onCloseClicked = { navController.popBackStack() }, 28 | onReserveClicked = { navController.navigate(Screen.Reservation.route) } 29 | ) 30 | } 31 | 32 | @Composable 33 | fun DetailsScreenContent( 34 | state: DetailsUIState, 35 | onCloseClicked: () -> Unit, 36 | onReserveClicked: () -> Unit 37 | ) { 38 | Box( 39 | modifier = Modifier.fillMaxSize() 40 | ) { 41 | DetailsBackgroundImage(modifier = Modifier.align(Alignment.TopCenter)) 42 | DetailsHeader(onCloseClicked = onCloseClicked) 43 | DetailsBottomSheet( 44 | state, 45 | modifier = Modifier.align(Alignment.BottomCenter), 46 | onReserveClicked = onReserveClicked 47 | ) 48 | } 49 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/screen/home/HomeScreen.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.screen.home 2 | 3 | import androidx.compose.foundation.ExperimentalFoundationApi 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.Column 6 | import androidx.compose.foundation.layout.fillMaxSize 7 | import androidx.compose.foundation.pager.PagerState 8 | import androidx.compose.foundation.pager.rememberPagerState 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.collectAsState 11 | import androidx.compose.runtime.getValue 12 | import androidx.compose.ui.Alignment 13 | import androidx.compose.ui.Modifier 14 | import androidx.hilt.navigation.compose.hiltViewModel 15 | import androidx.navigation.NavController 16 | import com.devsadeq.composecinematicketsreservations.ui.composable.BlurBackground 17 | import com.devsadeq.composecinematicketsreservations.ui.composable.HomeFilterChips 18 | import com.devsadeq.composecinematicketsreservations.ui.composable.HomeOverview 19 | import com.devsadeq.composecinematicketsreservations.ui.composable.HomePager 20 | import com.devsadeq.composecinematicketsreservations.ui.main.Screen 21 | import com.devsadeq.composecinematicketsreservations.viewmodel.home.HomeUIState 22 | import com.devsadeq.composecinematicketsreservations.viewmodel.home.HomeViewModel 23 | 24 | 25 | @OptIn(ExperimentalFoundationApi::class) 26 | @Composable 27 | fun HomeScreen( 28 | navController: NavController, 29 | viewModel: HomeViewModel = hiltViewModel() 30 | ) { 31 | val state by viewModel.state.collectAsState() 32 | val pagerState = rememberPagerState(initialPage = 1) 33 | 34 | HomeScreenContent( 35 | state = state, 36 | pagerState = pagerState, 37 | onItemClicked = { navController.navigate(Screen.Details.route) } 38 | ) 39 | } 40 | 41 | @OptIn(ExperimentalFoundationApi::class) 42 | @Composable 43 | private fun HomeScreenContent( 44 | state: HomeUIState, 45 | pagerState: PagerState, 46 | onItemClicked: () -> Unit 47 | ) { 48 | 49 | Box( 50 | modifier = Modifier.fillMaxSize() 51 | ) { 52 | BlurBackground(image = state.movies[pagerState.currentPage].imageRes) 53 | Column( 54 | modifier = Modifier, 55 | horizontalAlignment = Alignment.CenterHorizontally, 56 | ) { 57 | HomeFilterChips() 58 | HomePager(state.movies, pagerState = pagerState, onItemClicked = onItemClicked) 59 | HomeOverview(state, pagerState) 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/screen/profile/ProfileScreen.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.screen.profile 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.material3.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Alignment 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.tooling.preview.Preview 10 | import androidx.compose.ui.unit.sp 11 | 12 | @Composable 13 | fun ProfileScreen() { 14 | ProfileScreenContent() 15 | } 16 | 17 | @Composable 18 | private fun ProfileScreenContent() { 19 | Column( 20 | modifier = Modifier, 21 | horizontalAlignment = Alignment.CenterHorizontally, 22 | verticalArrangement = Arrangement.Center, 23 | ) { 24 | Text(text = "Profile Screen", fontSize = 30.sp) 25 | } 26 | } 27 | 28 | @Preview(showBackground = true, showSystemUi = true, apiLevel = 31) 29 | @Composable 30 | fun ProfileScreenPreview() { 31 | ProfileScreen() 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/screen/reservation/ReservationScreen.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.screen.reservation 2 | 3 | import androidx.compose.foundation.background 4 | import androidx.compose.foundation.layout.Box 5 | import androidx.compose.foundation.layout.fillMaxSize 6 | import androidx.compose.foundation.layout.padding 7 | import androidx.compose.runtime.Composable 8 | import androidx.compose.runtime.collectAsState 9 | import androidx.compose.runtime.getValue 10 | import androidx.compose.ui.Alignment 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.unit.dp 13 | import androidx.hilt.navigation.compose.hiltViewModel 14 | import androidx.navigation.NavController 15 | import com.devsadeq.composecinematicketsreservations.ui.composable.ReservationScreenFooter 16 | import com.devsadeq.composecinematicketsreservations.ui.composable.ReservationScreenHeader 17 | import com.devsadeq.composecinematicketsreservations.ui.composable.ReservationScreenSeats 18 | import com.devsadeq.composecinematicketsreservations.ui.theme.Black 19 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.ReservationUIState 20 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.ReservationViewModel 21 | import com.devsadeq.composecinematicketsreservations.viewmodel.reservation.toSeatPairs 22 | 23 | @Composable 24 | fun ReservationScreen( 25 | navController: NavController, 26 | viewModel: ReservationViewModel = hiltViewModel() 27 | ) { 28 | val state by viewModel.state.collectAsState() 29 | ReservationScreenContent( 30 | state, 31 | onSeatClick = viewModel::onSeatSelected, 32 | onCloseClicked = { navController.popBackStack() } 33 | ) 34 | } 35 | 36 | @Composable 37 | private fun ReservationScreenContent( 38 | state: ReservationUIState, 39 | onSeatClick: (id: Int) -> Unit, 40 | onCloseClicked: () -> Unit 41 | ) { 42 | Box( 43 | modifier = Modifier 44 | .fillMaxSize() 45 | .background(Black) 46 | ) { 47 | ReservationScreenHeader( 48 | modifier = Modifier.align(Alignment.TopCenter), 49 | onCloseClicked = onCloseClicked 50 | ) 51 | ReservationScreenSeats( 52 | seatsPairs = state.seats.toSeatPairs(), 53 | onSeatClick = onSeatClick, 54 | modifier = Modifier 55 | .align(Alignment.TopCenter) 56 | .padding(top = 250.dp) 57 | ) 58 | ReservationScreenFooter( 59 | state = state, 60 | modifier = Modifier.align(Alignment.BottomCenter), 61 | ) 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/screen/search/SearchScreen.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.screen.search 2 | 3 | import androidx.compose.foundation.layout.Arrangement 4 | import androidx.compose.foundation.layout.Column 5 | import androidx.compose.material3.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Alignment 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.tooling.preview.Preview 10 | import androidx.compose.ui.unit.sp 11 | 12 | @Composable 13 | fun SearchScreen() { 14 | SearchScreenContent() 15 | } 16 | 17 | @Composable 18 | private fun SearchScreenContent() { 19 | Column( 20 | modifier = Modifier, 21 | horizontalAlignment = Alignment.CenterHorizontally, 22 | verticalArrangement = Arrangement.Center, 23 | ) { 24 | Text(text = "Search Screen", fontSize = 30.sp) 25 | } 26 | } 27 | 28 | @Preview(showBackground = true, showSystemUi = true, apiLevel = 31) 29 | @Composable 30 | fun SearchScreenPreview() { 31 | SearchScreen() 32 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) 12 | 13 | val White = Color(0xFFFFFFFF) 14 | val Black = Color(0xFF010101) 15 | val Grey = Color(0xFF989185) 16 | val DarkGrey = Color(0xFF404040) 17 | val Orange = Color(0xFFFF5524) 18 | val LightOrange = Color(0x80FF5524) 19 | val LightGrey = Color(0xFFEFEFEF) 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.toArgb 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.platform.LocalView 16 | import androidx.core.view.WindowCompat 17 | 18 | private val DarkColorScheme = darkColorScheme( 19 | primary = Orange, 20 | secondary = DarkGrey, 21 | tertiary = Grey, 22 | background = White, 23 | onPrimary = White, 24 | onBackground = Black, 25 | onTertiary = White, 26 | ) 27 | 28 | private val LightColorScheme = lightColorScheme( 29 | primary = Orange, 30 | secondary = DarkGrey, 31 | tertiary = Grey, 32 | background = White, 33 | onPrimary = White, 34 | onBackground = Black, 35 | onTertiary = White, 36 | ) 37 | 38 | @Composable 39 | fun ComposeCinemaTicketsReservationsTheme( 40 | darkTheme: Boolean = isSystemInDarkTheme(), 41 | // Dynamic color is available on Android 12+ 42 | dynamicColor: Boolean = true, 43 | content: @Composable () -> Unit 44 | ) { 45 | val colorScheme = when { 46 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 47 | val context = LocalContext.current 48 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 49 | } 50 | 51 | darkTheme -> DarkColorScheme 52 | else -> LightColorScheme 53 | } 54 | val view = LocalView.current 55 | if (!view.isInEditMode) { 56 | SideEffect { 57 | val window = (view.context as Activity).window 58 | window.statusBarColor = colorScheme.primary.toArgb() 59 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 60 | } 61 | } 62 | 63 | MaterialTheme( 64 | colorScheme = colorScheme, 65 | typography = Typography, 66 | content = content 67 | ) 68 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.Font 6 | import androidx.compose.ui.text.font.FontFamily 7 | import androidx.compose.ui.text.font.FontWeight 8 | import androidx.compose.ui.unit.sp 9 | import com.devsadeq.composecinematicketsreservations.R 10 | 11 | val OpenSans = FontFamily( 12 | Font(R.font.opensans_regular), 13 | Font(R.font.opensans_medium, FontWeight.W500), 14 | ) 15 | 16 | val Typography = Typography( 17 | headlineMedium = TextStyle( 18 | fontFamily = OpenSans, 19 | fontWeight = FontWeight.Medium, 20 | fontSize = 20.sp, 21 | lineHeight = 28.sp, 22 | letterSpacing = 0.sp 23 | ), 24 | bodyLarge = TextStyle( 25 | fontFamily = OpenSans, 26 | fontWeight = FontWeight.Normal, 27 | fontSize = 16.sp, 28 | lineHeight = 24.sp, 29 | letterSpacing = 0.5.sp 30 | ), 31 | bodyMedium = TextStyle( 32 | fontFamily = OpenSans, 33 | fontWeight = FontWeight.Normal, 34 | fontSize = 14.sp, 35 | lineHeight = 20.sp, 36 | letterSpacing = 0.25.sp 37 | ), 38 | bodySmall = TextStyle( 39 | fontFamily = OpenSans, 40 | fontWeight = FontWeight.Normal, 41 | fontSize = 12.sp, 42 | lineHeight = 20.sp, 43 | letterSpacing = 0.25.sp 44 | ), 45 | labelLarge = TextStyle( 46 | fontFamily = OpenSans, 47 | fontWeight = FontWeight.Medium, 48 | fontSize = 16.sp, 49 | lineHeight = 24.sp, 50 | letterSpacing = 0.5.sp 51 | ), 52 | labelMedium = TextStyle( 53 | fontFamily = OpenSans, 54 | fontWeight = FontWeight.Medium, 55 | fontSize = 14.sp, 56 | lineHeight = 20.sp, 57 | letterSpacing = 0.25.sp 58 | ), 59 | labelSmall = TextStyle( 60 | fontFamily = OpenSans, 61 | fontWeight = FontWeight.Medium, 62 | fontSize = 12.sp, 63 | lineHeight = 20.sp, 64 | letterSpacing = 0.25.sp 65 | ), 66 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/viewmodel/details/DetailsUIState.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.viewmodel.details 2 | 3 | data class DetailsUIState( 4 | val actors: List = emptyList(), 5 | ) 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/viewmodel/details/DetailsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.viewmodel.details 2 | 3 | import androidx.lifecycle.ViewModel 4 | import com.devsadeq.composecinematicketsreservations.R 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.flow.asStateFlow 8 | import kotlinx.coroutines.flow.update 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class DetailsViewModel @Inject constructor() : ViewModel() { 13 | private val _state = MutableStateFlow(DetailsUIState()) 14 | val state = _state.asStateFlow() 15 | 16 | init { 17 | getActors() 18 | } 19 | 20 | private fun getActors() { 21 | _state.update { 22 | it.copy( 23 | actors = listOf( 24 | R.drawable.actor_1, 25 | R.drawable.actor_2, 26 | R.drawable.actor_3, 27 | R.drawable.actor_4, 28 | R.drawable.actor_5, 29 | R.drawable.actor_6, 30 | R.drawable.actor_7, 31 | R.drawable.actor_8, 32 | R.drawable.actor_1, 33 | R.drawable.actor_2, 34 | R.drawable.actor_3, 35 | R.drawable.actor_4, 36 | R.drawable.actor_5, 37 | R.drawable.actor_6, 38 | R.drawable.actor_7, 39 | R.drawable.actor_8, 40 | ) 41 | ) 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/viewmodel/home/HomeUIState.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.viewmodel.home 2 | 3 | data class HomeUIState( 4 | val movies: List = emptyList(), 5 | ) { 6 | data class MovieUIState( 7 | val title: String = "", 8 | val imageRes: Int = 0, 9 | val duration: String = "", 10 | val genres: List = emptyList(), 11 | ) 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/viewmodel/home/HomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.viewmodel.home 2 | 3 | import androidx.lifecycle.ViewModel 4 | import com.devsadeq.composecinematicketsreservations.R 5 | import dagger.hilt.android.lifecycle.HiltViewModel 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.flow.asStateFlow 8 | import kotlinx.coroutines.flow.update 9 | import javax.inject.Inject 10 | 11 | @HiltViewModel 12 | class HomeViewModel @Inject constructor() : ViewModel() { 13 | private val _state = MutableStateFlow(HomeUIState()) 14 | val state = _state.asStateFlow() 15 | 16 | private val dummyMovies = listOf( 17 | HomeUIState.MovieUIState( 18 | title = "Morbius", 19 | imageRes = R.drawable.movie1, 20 | duration = "1h 50m", 21 | genres = listOf("Horror", "Action", "Thriller") 22 | ), 23 | HomeUIState.MovieUIState( 24 | title = "fantastic Beasts: The Secrets of Dumbledore", 25 | imageRes = R.drawable.movie2, 26 | duration = "2h 23m", 27 | genres = listOf("Adventure", "Fantasy", "Family") 28 | ), 29 | HomeUIState.MovieUIState( 30 | title = "Doctor Strange in the Multiverse of Madness", 31 | imageRes = R.drawable.movie3, 32 | duration = "3h 15m", 33 | genres = listOf("Action", "Adventure", "Fantasy") 34 | ) 35 | ) 36 | 37 | init { 38 | getDummyData() 39 | } 40 | 41 | private fun getDummyData() { 42 | _state.update { it.copy(movies = dummyMovies) } 43 | } 44 | 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/viewmodel/reservation/ReservationUIState.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.viewmodel.reservation 2 | 3 | data class ReservationUIState( 4 | val seats: List = emptyList(), 5 | val days: List = emptyList(), 6 | val times: List = emptyList(), 7 | ) 8 | 9 | data class SeatUIState( 10 | val id: Int, 11 | val isReserved: Boolean = false, 12 | val isSelected: Boolean = false, 13 | ) 14 | 15 | data class DayUIState( 16 | val id: Int, 17 | val dayOfWeek: String, 18 | val dayOfMonth: Int, 19 | val isSelected: Boolean = false, 20 | ) 21 | 22 | data class TimeUIState( 23 | val id: Int, 24 | val time: String, 25 | val isSelected: Boolean = false, 26 | ) 27 | 28 | fun List.toSeatPairs(): List> { 29 | return this.chunked(2).map { it.first() to it.last() } 30 | } -------------------------------------------------------------------------------- /app/src/main/java/com/devsadeq/composecinematicketsreservations/viewmodel/reservation/ReservationViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.devsadeq.composecinematicketsreservations.viewmodel.reservation 2 | 3 | import androidx.lifecycle.ViewModel 4 | import dagger.hilt.android.lifecycle.HiltViewModel 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.asStateFlow 7 | import kotlinx.coroutines.flow.update 8 | import javax.inject.Inject 9 | 10 | @HiltViewModel 11 | class ReservationViewModel @Inject constructor() : ViewModel() { 12 | private val _state = MutableStateFlow(ReservationUIState()) 13 | val state = _state.asStateFlow() 14 | 15 | init { 16 | getSeats() 17 | getDays() 18 | getTimes() 19 | } 20 | 21 | fun onSeatSelected(seatId: Int) { 22 | _state.update { 23 | it.copy( 24 | seats = it.seats.map { seat -> 25 | if (seat.id == seatId) { 26 | seat.copy(isSelected = !seat.isSelected) 27 | } else { 28 | seat 29 | } 30 | } 31 | ) 32 | } 33 | } 34 | 35 | private fun getSeats() { 36 | val seats = mutableListOf() 37 | for (i in 1..30) { 38 | seats.add( 39 | SeatUIState( 40 | id = i, 41 | isReserved = i % 3 == 0, 42 | isSelected = i == 5 43 | ) 44 | ) 45 | } 46 | _state.value = _state.value.copy(seats = seats) 47 | } 48 | 49 | private fun getDays() { 50 | val days = mutableListOf() 51 | for (i in 14..30) { 52 | days.add( 53 | DayUIState( 54 | id = i, 55 | dayOfWeek = if (i == 14) "Today" else "Day $i", 56 | dayOfMonth = i, 57 | isSelected = i == 16 58 | ) 59 | ) 60 | } 61 | _state.value = _state.value.copy(days = days) 62 | } 63 | 64 | private fun getTimes() { 65 | val times = mutableListOf() 66 | for (i in 1..7) { 67 | times.add( 68 | TimeUIState( 69 | id = i, 70 | time = "${i + 10}:00", 71 | isSelected = i == 1 72 | ) 73 | ) 74 | } 75 | _state.value = _state.value.copy(times = times) 76 | } 77 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/actor_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/actor_1.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/actor_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/actor_2.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/actor_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/actor_3.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/actor_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/actor_4.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/actor_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/actor_5.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/actor_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/actor_6.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/actor_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/actor_7.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/actor_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/actor_8.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/banner.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_clock.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_close_circle.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_play.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_profile.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_ticket.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 21 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_video_play.xml: -------------------------------------------------------------------------------- 1 | 6 | 13 | 20 | 27 | 34 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/movie1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/movie1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/movie2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/movie2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/movie3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/drawable/movie3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/seat.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 13 | 16 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/seat_belt.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/font/opensans_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/font/opensans_medium.ttf -------------------------------------------------------------------------------- /app/src/main/res/font/opensans_regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/font/opensans_regular.ttf -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devsadeq/ComposeCinemaTicketsReservations/00dc62ec7796ebcd50a7b2148831f5846defc947/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ComposeCinemaTicketsReservations 3 | Now Showing 4 | Coming Soon 5 | 2h 23m 6 | Fantasy 7 | Adventure 8 | Fantastic Beasts: The Secrets of Dumbledore 9 | Professor Albus Dumbledore knows the powerful Dark wizard Gellert Grindelwald is moving to seize control of the wizarding world. Unable to stop him alone, he entrusts Magizoologist Newt Scamander to lead an intrepid team of wizards, witches and one brave Muggle baker on a dangerous mission, where they encounter old and new foes. But winning this battle against Grindelwald means making the ultimate sacrifice. 10 | Booking 11 | 6.8 12 | IMDB 13 | Rotten Tomatoes 14 | 36% 15 | IGN 16 | 4 17 | Available 18 | Taken 19 | Selected 20 | Buy Tickets 21 | $100.00 22 | 4 tickets 23 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |