Skip to content

Commit 41579f6

Browse files
committed
update movie list, movie detail in compose
1 parent 70bb3fe commit 41579f6

File tree

11 files changed

+428
-95
lines changed

11 files changed

+428
-95
lines changed

app/build.gradle.kts

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -153,39 +153,6 @@ dependencies {
153153
implementation("org.jetbrains.kotlin:kotlin-reflect:1.7.20")
154154
implementation("androidx.multidex:multidex:2.0.1")
155155

156-
// compose
157-
// https://developer.android.com/jetpack/compose/interop/adding
158-
// https://developer.android.com/jetpack/compose/setup
159-
val composeBom = platform("androidx.compose:compose-bom:2022.10.00")
160-
implementation(composeBom)
161-
androidTestImplementation(composeBom)
162-
// Android Studio Preview support
163-
implementation("androidx.compose.ui:ui-tooling-preview")
164-
debugImplementation("androidx.compose.ui:ui-tooling")
165-
// Animations
166-
implementation("androidx.compose.animation:animation")
167-
// Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.)
168-
implementation("androidx.compose.foundation:foundation")
169-
// Material Design
170-
implementation("androidx.compose.material3:material3")
171-
// Optional - Included automatically by material, only add when you need
172-
// the icons but not the material library (e.g. when using Material3 or a
173-
// custom design system based on Foundation)
174-
implementation("androidx.compose.material:material-icons-core")
175-
// Optional - Add full set of material icons
176-
implementation("androidx.compose.material:material-icons-extended")
177-
// Optional - Add window size utils
178-
implementation("androidx.compose.material3:material3-window-size-class")
179-
// Optional - Integration with activities
180-
implementation("androidx.activity:activity-compose:1.6.1")
181-
// Optional - Integration with ViewModels
182-
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")
183-
// Optional - Integration with LiveData
184-
implementation("androidx.compose.runtime:runtime-livedata")
185-
// UI Tests
186-
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
187-
debugImplementation("androidx.compose.ui:ui-test-manifest")
188-
189156
// List of KTX extensions
190157
// https://developer.android.com/kotlin/ktx/extensions-list
191158
implementation("androidx.core:core-ktx:1.9.0")
@@ -401,6 +368,61 @@ dependencies {
401368
testImplementation(Libs.testCore)
402369
testImplementation(Libs.archCore)
403370
*/
371+
372+
// compose
373+
// https://developer.android.com/jetpack/compose/interop/adding
374+
// https://developer.android.com/jetpack/compose/setup
375+
val composeBom = platform("androidx.compose:compose-bom:2022.10.00")
376+
implementation(composeBom)
377+
androidTestImplementation(composeBom)
378+
// Android Studio Preview support
379+
implementation("androidx.compose.ui:ui-tooling-preview")
380+
debugImplementation("androidx.compose.ui:ui-tooling")
381+
// Animations
382+
implementation("androidx.compose.animation:animation")
383+
// Foundation (Border, Background, Box, Image, Scroll, shapes, animations, etc.)
384+
implementation("androidx.compose.foundation:foundation")
385+
// or Material Design 2
386+
implementation("androidx.compose.material:material")
387+
// Material Design
388+
implementation("androidx.compose.material3:material3")
389+
// Constraint layout
390+
implementation("androidx.constraintlayout:constraintlayout-compose:1.0.1")
391+
// Optional - Included automatically by material, only add when you need
392+
// the icons but not the material library (e.g. when using Material3 or a
393+
// custom design system based on Foundation)
394+
// implementation("androidx.compose.material:material-icons-core")
395+
// Optional - Add full set of material icons
396+
implementation("androidx.compose.material:material-icons-extended")
397+
// Optional - Add window size utils
398+
implementation("androidx.compose.material3:material3-window-size-class")
399+
// Optional - Integration with activities
400+
implementation("androidx.activity:activity-compose:1.6.1")
401+
// Optional - Integration with ViewModels
402+
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1")
403+
// Optional - Integration with LiveData
404+
implementation("androidx.compose.runtime:runtime-livedata")
405+
// UI Tests
406+
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
407+
debugImplementation("androidx.compose.ui:ui-test-manifest")
408+
// navigation
409+
implementation("androidx.navigation:navigation-compose:2.5.3")
410+
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
411+
// https://github.com/skydoves/landscapist
412+
implementation("com.github.skydoves:landscapist-bom:2.1.0")
413+
implementation("com.github.skydoves:landscapist-glide")
414+
implementation("com.github.skydoves:landscapist-placeholder")
415+
// https://google.github.io/accompanist/
416+
// https://github.com/google/accompanist
417+
val accompanistVersion = "0.28.0"
418+
implementation("com.google.accompanist:accompanist-systemuicontroller:$accompanistVersion")
419+
implementation("com.google.accompanist:accompanist-pager:$accompanistVersion")
420+
implementation("com.google.accompanist:accompanist-permissions:$accompanistVersion")
421+
implementation("com.google.accompanist:accompanist-placeholder:$accompanistVersion")
422+
implementation("com.google.accompanist:accompanist-navigation-animation:$accompanistVersion")
423+
implementation("com.google.accompanist:accompanist-navigation-material:$accompanistVersion")
424+
implementation("com.google.accompanist:accompanist-webview:$accompanistVersion")
425+
implementation("com.google.accompanist:accompanist-adaptive:$accompanistVersion")
404426
}
405427

406428
kapt {

app/src/main/AndroidManifest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
tools:targetApi="31">
2626

2727
<activity
28-
android:name="com.example.moviedb.compose.main.ComposeActivity"
28+
android:name="com.example.moviedb.compose.ComposeActivity"
2929
android:configChanges="orientation|screenSize"
3030
android:exported="true"
3131
android:theme="@style/Theme.MyApplication.NoActionBar">
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.example.moviedb.compose
2+
3+
import android.os.Bundle
4+
import androidx.activity.compose.setContent
5+
import androidx.appcompat.app.AppCompatActivity
6+
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.material3.MaterialTheme
8+
import androidx.compose.material3.Surface
9+
import androidx.compose.runtime.Composable
10+
import androidx.compose.ui.Modifier
11+
import com.example.moviedb.compose.theme.ComposeAppTheme
12+
import dagger.hilt.android.AndroidEntryPoint
13+
14+
@AndroidEntryPoint
15+
class ComposeActivity : AppCompatActivity() {
16+
override fun onCreate(savedInstanceState: Bundle?) {
17+
super.onCreate(savedInstanceState)
18+
setContent {
19+
ComposeApp()
20+
}
21+
}
22+
}
23+
24+
@Composable
25+
fun ComposeApp() {
26+
ComposeAppTheme {
27+
Surface(
28+
modifier = Modifier.fillMaxSize(),
29+
color = MaterialTheme.colorScheme.background
30+
) {
31+
MainNavigation()
32+
}
33+
}
34+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.example.moviedb.compose
2+
3+
import androidx.compose.runtime.Composable
4+
import androidx.navigation.NavController
5+
import androidx.navigation.NavType
6+
import androidx.navigation.compose.NavHost
7+
import androidx.navigation.compose.composable
8+
import androidx.navigation.compose.rememberNavController
9+
import androidx.navigation.navArgument
10+
import com.example.moviedb.compose.ui.detail.DetailScreen
11+
import com.example.moviedb.compose.ui.home.HomeScreen
12+
import okio.Path.Companion.toPath
13+
14+
@Composable
15+
fun MainNavigation() {
16+
val navController = rememberNavController()
17+
NavHost(navController = navController, startDestination = Route.MAIN) {
18+
composable(route = Route.MAIN) {
19+
HomeScreen(navController = navController)
20+
}
21+
composable(
22+
route = Route.MOVIE_DETAIL,
23+
arguments = listOf(navArgument(Route.Param.MOVIE_ID) {
24+
type = NavType.Companion.StringType
25+
})
26+
) { backStackEntry ->
27+
DetailScreen(
28+
navController = navController,
29+
movieId = backStackEntry.arguments?.getString(Route.Param.MOVIE_ID)
30+
)
31+
}
32+
}
33+
}
34+
35+
object Route {
36+
const val MAIN = "main"
37+
const val MOVIE_DETAIL = "movieDetail/{${Param.MOVIE_ID}}"
38+
39+
object Param {
40+
const val MOVIE_ID = "movieId"
41+
42+
fun toPath(param: String) = "{${param}}"
43+
}
44+
}
45+
46+
fun NavController.toMovieDetail(movieId: String?) {
47+
navigate(
48+
route = Route.MOVIE_DETAIL.replace(
49+
Route.Param.toPath(Route.Param.MOVIE_ID),
50+
movieId ?: ""
51+
)
52+
)
53+
}

app/src/main/java/com/example/moviedb/compose/main/ComposeActivity.kt

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
package com.example.moviedb.compose.ui.detail
2+
3+
import androidx.compose.foundation.Image
4+
import androidx.compose.foundation.background
5+
import androidx.compose.foundation.clickable
6+
import androidx.compose.foundation.layout.*
7+
import androidx.compose.foundation.shape.CircleShape
8+
import androidx.compose.material.icons.Icons
9+
import androidx.compose.material.icons.filled.Error
10+
import androidx.compose.material.icons.filled.Image
11+
import androidx.compose.material3.Text
12+
import androidx.compose.runtime.Composable
13+
import androidx.compose.runtime.LaunchedEffect
14+
import androidx.compose.runtime.collectAsState
15+
import androidx.compose.runtime.getValue
16+
import androidx.compose.ui.Modifier
17+
import androidx.compose.ui.draw.clip
18+
import androidx.compose.ui.graphics.Color
19+
import androidx.compose.ui.layout.ContentScale
20+
import androidx.compose.ui.res.painterResource
21+
import androidx.compose.ui.unit.dp
22+
import androidx.compose.ui.unit.sp
23+
import androidx.hilt.navigation.compose.hiltViewModel
24+
import androidx.lifecycle.viewModelScope
25+
import androidx.navigation.NavController
26+
import com.example.moviedb.R
27+
import com.example.moviedb.data.model.Movie
28+
import com.example.moviedb.data.repository.UserRepository
29+
import com.example.moviedb.ui.base.BaseViewModel
30+
import com.skydoves.landscapist.ImageOptions
31+
import com.skydoves.landscapist.components.rememberImageComponent
32+
import com.skydoves.landscapist.glide.GlideImage
33+
import com.skydoves.landscapist.placeholder.placeholder.PlaceholderPlugin
34+
import dagger.hilt.android.lifecycle.HiltViewModel
35+
import kotlinx.coroutines.flow.MutableStateFlow
36+
import kotlinx.coroutines.flow.StateFlow
37+
import kotlinx.coroutines.launch
38+
import javax.inject.Inject
39+
40+
@Composable
41+
fun DetailScreen(
42+
navController: NavController,
43+
movieId: String?,
44+
viewModel: DetailViewModel = hiltViewModel()
45+
) {
46+
val movie by viewModel.movie.collectAsState()
47+
LaunchedEffect(key1 = movieId, block = {
48+
viewModel.getMovieDetail(movieId = movieId)
49+
})
50+
MovieDetailBody(movie = movie, onClickBack = { navController.popBackStack() })
51+
}
52+
53+
@Composable
54+
fun MovieDetailBody(
55+
movie: Movie?,
56+
onClickBack: () -> Unit
57+
) {
58+
if (movie != null) {
59+
Column(
60+
modifier = Modifier
61+
.fillMaxSize()
62+
.background(Color.Black)
63+
) {
64+
Box(modifier = Modifier.fillMaxWidth()) {
65+
GlideImage(
66+
imageModel = { movie.getFullBackdropPath() ?: "" },
67+
modifier = Modifier.fillMaxWidth(),
68+
component = rememberImageComponent {
69+
+PlaceholderPlugin.Loading(Icons.Filled.Image)
70+
+PlaceholderPlugin.Failure(Icons.Filled.Error)
71+
},
72+
imageOptions = ImageOptions(),
73+
)
74+
Image(
75+
painterResource(R.drawable.ic_arrow_back_white_24dp),
76+
contentDescription = "",
77+
contentScale = ContentScale.Crop,
78+
modifier = Modifier
79+
.size(48.dp)
80+
.clip(CircleShape)
81+
.clickable {
82+
onClickBack.invoke()
83+
}
84+
.padding(12.dp),
85+
)
86+
}
87+
Text(
88+
movie.title ?: "",
89+
color = Color.White,
90+
modifier = Modifier.padding(16.dp),
91+
fontSize = 20.sp,
92+
)
93+
Text(movie.releaseDate ?: "", color = Color.White)
94+
Text(movie.overview ?: "", color = Color.White)
95+
}
96+
} else {
97+
98+
}
99+
}
100+
101+
@HiltViewModel
102+
class DetailViewModel @Inject constructor(
103+
private val userRepository: UserRepository
104+
) : BaseViewModel() {
105+
private val _movie = MutableStateFlow<Movie?>(null)
106+
val movie: StateFlow<Movie?> = _movie
107+
108+
fun getMovieDetail(movieId: String?) {
109+
if (movieId.isNullOrBlank()) return
110+
viewModelScope.launch {
111+
try {
112+
_movie.value = userRepository.getMovieById(movieId)
113+
} catch (e: Throwable) {
114+
onError(e)
115+
}
116+
}
117+
}
118+
}

0 commit comments

Comments
 (0)