From 73e8e3e0cbc841bf7dd6b830c784879aea1cb618 Mon Sep 17 00:00:00 2001 From: minhnhatdeii <125042319+minhnhatdeii@users.noreply.github.com> Date: Tue, 26 Nov 2024 18:23:22 +0700 Subject: [PATCH 1/3] add view model --- app/build.gradle.kts | 3 + .../com/example/harmonyhub/HarmonyHubApp.kt | 8 +- .../com/example/harmonyhub/MainActivity.kt | 3 +- .../com/example/harmonyhub/data/APIService.kt | 86 +++++++++++++++++ .../data/HomeOverViewDataClassIn.kt | 77 +++++++++++++++ .../example/harmonyhub/data/HomeUIState.kt | 11 +++ .../example/harmonyhub/ui/HomeViewModel.kt | 45 +++++++++ .../harmonyhub/ui/components/ArtistCard.kt | 31 +++--- .../example/harmonyhub/ui/home/HomeScreen.kt | 70 ++++++++++++-- .../ui/library/ArtistsFollowingScreen.kt | 8 +- app/src/main/res/drawable/ic_broken_image.xml | 25 +++++ .../main/res/drawable/ic_connection_error.xml | 25 +++++ app/src/main/res/drawable/loading_img.xml | 94 +++++++++++++++++++ app/src/main/res/values/strings.xml | 3 + 14 files changed, 457 insertions(+), 32 deletions(-) create mode 100644 app/src/main/java/com/example/harmonyhub/data/APIService.kt create mode 100644 app/src/main/java/com/example/harmonyhub/data/HomeOverViewDataClassIn.kt create mode 100644 app/src/main/java/com/example/harmonyhub/data/HomeUIState.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/HomeViewModel.kt create mode 100644 app/src/main/res/drawable/ic_broken_image.xml create mode 100644 app/src/main/res/drawable/ic_connection_error.xml create mode 100644 app/src/main/res/drawable/loading_img.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7bebd26..71a11bd 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -75,4 +75,7 @@ dependencies { debugImplementation(libs.androidx.ui.tooling) debugImplementation(libs.androidx.ui.test.manifest) + implementation("io.coil-kt:coil-compose:2.2.2") // Phiên bản mới nhất + + } \ No newline at end of file diff --git a/app/src/main/java/com/example/harmonyhub/HarmonyHubApp.kt b/app/src/main/java/com/example/harmonyhub/HarmonyHubApp.kt index ce04c6a..aee7d74 100644 --- a/app/src/main/java/com/example/harmonyhub/HarmonyHubApp.kt +++ b/app/src/main/java/com/example/harmonyhub/HarmonyHubApp.kt @@ -1,5 +1,6 @@ package com.example.harmonyhub +import androidx.activity.viewModels import androidx.annotation.StringRes import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box @@ -27,12 +28,14 @@ import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.stringResource +import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavDestination.Companion.hierarchy import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.rememberNavController +import com.example.harmonyhub.ui.HomeViewModel import com.example.harmonyhub.ui.home.HomeScreen import com.example.harmonyhub.ui.library.ArtistsFollowingScreen import com.example.harmonyhub.ui.library.DownloadScreen @@ -68,7 +71,7 @@ private val gradientBackground = Brush.verticalGradient( Color.Black ) ) - +private val homeViewModel = HomeViewModel() @Composable fun HarmonyHubApp() { val navController = rememberNavController() @@ -148,7 +151,8 @@ fun HarmonyHubApp() { // }, onLogoutButtonClicked = { navController.navigate(HarmonyHubScreen.Login.name) - } + }, + viewModel = homeViewModel ) } composable(route = HarmonyHubScreen.Search.name) { diff --git a/app/src/main/java/com/example/harmonyhub/MainActivity.kt b/app/src/main/java/com/example/harmonyhub/MainActivity.kt index 1c0049e..573ec1b 100644 --- a/app/src/main/java/com/example/harmonyhub/MainActivity.kt +++ b/app/src/main/java/com/example/harmonyhub/MainActivity.kt @@ -16,8 +16,7 @@ class MainActivity : ComponentActivity() { setLightStatusBarIcons(this, lightIcons = false) setContent { HarmonyHubTheme { - //HarmonyHubApp() - PlayScreen({}) + HarmonyHubApp() } } } diff --git a/app/src/main/java/com/example/harmonyhub/data/APIService.kt b/app/src/main/java/com/example/harmonyhub/data/APIService.kt new file mode 100644 index 0000000..0f7e43d --- /dev/null +++ b/app/src/main/java/com/example/harmonyhub/data/APIService.kt @@ -0,0 +1,86 @@ +package com.example.harmonyhub.data + +import com.example.harmonyhub.ui.components.Song +import com.google.gson.Gson +import okhttp3.OkHttpClient +import okhttp3.Request + + +object APIService { + + val listPopularArtist : MutableList? = mutableListOf() + //println("${artist.type} - ${artist.id} - ${artist.name} - ${artist.visuals.avatar[1].url}") + + val listPopularAlbums: MutableList? = mutableListOf() + + fun getHomePageOverview() { + val client = OkHttpClient() + + val request = Request.Builder() + .url("https://spotify-scraper.p.rapidapi.com/v1/home") + .get() + .addHeader("x-rapidapi-key", "c18da195b0mshcdebcf46df53015p1a1b64jsn33955d1b96fc") + .addHeader("x-rapidapi-host", "spotify-scraper.p.rapidapi.com") + .build() + + val response = client.newCall(request).execute() + // Kiểm tra xem phản hồi có thành công không + if (response.isSuccessful) { + val jsonResponse = response.body?.string() + //println("Response: $jsonResponse") + + // Kiểm tra nếu jsonResponse không null và là chuỗi hợp lệ + if (jsonResponse != null) { + try { + val gson = Gson() + val responseData = gson.fromJson(jsonResponse, Response::class.java) + + // Trích xuất dữ liệu + if (responseData.status) { + //trich xuat Popular Artise + val PopularArtist = responseData.sections.items[0].contents.items + val PopularAlbums = responseData.sections.items[1].contents.items + for (i in PopularArtist!!) { + val subArtist = ArtistOut(i.id, i.name, i.visuals?.avatar?.get(1)?.url) + listPopularArtist?.add(subArtist) + } + + for (i in PopularAlbums!!) {//duyet phan tu album + var listArtistInAlbum : MutableList = mutableListOf() + for( j in i.artists) {// truy van nghe si cua album + listArtistInAlbum.add(j.name) + } + val subAlbum = AlbumOut(i.id, i.name, i.cover[1].url, listArtistInAlbum) + listPopularAlbums?.add(subAlbum) + } +// for (i in listPopularArtist!!) { +// println("${i.id} - ${i.name} - ${i.image}") +// } +// for (i in listPopularAlbums!!) { +// println("${i.id} - ${i.name} - ${i.image} ") +// for( j in i.listArtist) { +// println("${j}") +// } +// } + + } else { + println("Error: Request was not successful") + } + } catch (e: Exception) { + println("Error parsing JSON: ${e.message}") + } + } else { + println("Error: JSON response is null.") + } + } else { + println("Request failed with code: ${response.code}") + } + } + +} +fun main() { + + val api = APIService + api.getHomePageOverview() + println("${api.listPopularArtist?.size}") +} \ No newline at end of file diff --git a/app/src/main/java/com/example/harmonyhub/data/HomeOverViewDataClassIn.kt b/app/src/main/java/com/example/harmonyhub/data/HomeOverViewDataClassIn.kt new file mode 100644 index 0000000..35e32f6 --- /dev/null +++ b/app/src/main/java/com/example/harmonyhub/data/HomeOverViewDataClassIn.kt @@ -0,0 +1,77 @@ +package com.example.harmonyhub.data + +data class Avatar( + val url: String, + val width: Int, + val height: Int +) + +data class Avatars( + val avatar: List +) + +data class Album( + val type: String, + val id: String, + val name: String, + val cover: List, + val artists: List +) + +data class AlbumOut( + val id: String, + val name: String, + val image: String, + val listArtist: List, +) + +data class Artist( + val type: String, + val id: String, + val name: String, + val visuals: Avatars? = null +) + +data class ArtistOut( + val id: String, + val name: String, + val image: String? +) + + + + +data class PopularItem( + val type: String, + val id: String, + val name: String, + val cover: List,//album + val artists: List,//album + val visuals: Avatars? = null,//Artist + + +) + +data class Contents( + val totalCount: Int, + val items: List +) + +data class SectionItem( + val type: String, + val id: String, + val title: String, + val contents: Contents +) + +data class Sections( + val totalCount: Int, + val items: List +) + +data class Response( + val status: Boolean, + val errorId: String, + val sections: Sections +) + diff --git a/app/src/main/java/com/example/harmonyhub/data/HomeUIState.kt b/app/src/main/java/com/example/harmonyhub/data/HomeUIState.kt new file mode 100644 index 0000000..88c859a --- /dev/null +++ b/app/src/main/java/com/example/harmonyhub/data/HomeUIState.kt @@ -0,0 +1,11 @@ +package com.example.harmonyhub.data + +import com.example.harmonyhub.ui.components.Song + +data class HomeUIState( + val listPopularArtist : MutableList? = mutableListOf(), + val listPopularAlbums: MutableList? = mutableListOf(), + val listRecommendSong: MutableList? = mutableListOf(), + val isLoading: Boolean = false, + val errorMessage: String? = null +) diff --git a/app/src/main/java/com/example/harmonyhub/ui/HomeViewModel.kt b/app/src/main/java/com/example/harmonyhub/ui/HomeViewModel.kt new file mode 100644 index 0000000..6e44b1e --- /dev/null +++ b/app/src/main/java/com/example/harmonyhub/ui/HomeViewModel.kt @@ -0,0 +1,45 @@ +package com.example.harmonyhub.ui + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.example.harmonyhub.data.APIService +import com.example.harmonyhub.data.APIService.getHomePageOverview +import com.example.harmonyhub.data.HomeUIState +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +class HomeViewModel : ViewModel() { + private val _state = MutableStateFlow(HomeUIState()) + val state: StateFlow = _state.asStateFlow() + + init { + fetchHomePageData() + } + + fun fetchHomePageData() { + viewModelScope.launch { + _state.value = _state.value.copy(isLoading = true) + + try { + // Gọi API + val api = APIService + api.getHomePageOverview() + Log.d("HomeVMScreen", "Popular Artists: ${api.listPopularArtist?.size}") + // Cập nhật state với dữ liệu từ API + _state.value = _state.value.copy( + listPopularArtist = api.listPopularArtist, + listPopularAlbums = api.listPopularAlbums, + isLoading = false + ) + } catch (e: Exception) { + _state.value = _state.value.copy( + isLoading = false, + errorMessage = e.message + ) + } + } + } +} diff --git a/app/src/main/java/com/example/harmonyhub/ui/components/ArtistCard.kt b/app/src/main/java/com/example/harmonyhub/ui/components/ArtistCard.kt index 9d1ad6d..36d49e7 100644 --- a/app/src/main/java/com/example/harmonyhub/ui/components/ArtistCard.kt +++ b/app/src/main/java/com/example/harmonyhub/ui/components/ArtistCard.kt @@ -1,40 +1,39 @@ package com.example.harmonyhub.ui.components -import androidx.compose.foundation.Image import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage +import coil.request.ImageRequest import com.example.harmonyhub.ui.theme.NotoSans -data class Artist(val name: String, val img: Int) +data class Artist(val name: String, val img: Int, val id: String = "") fun Artist.contains(query: String, ignoreCase: Boolean = true): Boolean { return this.name.contains(query, ignoreCase) } @Composable -fun ArtistsCard(artistName: String, artistImg: Int, modifier: Modifier = Modifier) { +fun ArtistsCard(artistName: String, artistImg: String? = null, idArtist:String? = null, modifier: Modifier = Modifier) { Surface( modifier = Modifier .size(width = 130.dp, height = 170.dp) @@ -50,13 +49,15 @@ fun ArtistsCard(artistName: String, artistImg: Int, modifier: Modifier = Modifie Box( modifier = Modifier.size(width = 130.dp, height = 130.dp), ) { - Image( - painter = painterResource(id = artistImg), - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier - .fillMaxSize() - .clip(CircleShape), + AsyncImage( + model = ImageRequest.Builder(context = LocalContext.current) + .data(artistImg) + .crossfade(true) + .build(), + error = painterResource(com.example.harmonyhub.R.drawable.ic_broken_image), + placeholder = painterResource(id = com.example.harmonyhub.R.drawable.loading_img), + contentDescription = "Photo", + contentScale = ContentScale.FillBounds ) } Spacer(modifier = Modifier.height(4.dp)) @@ -74,4 +75,6 @@ fun ArtistsCard(artistName: String, artistImg: Int, modifier: Modifier = Modifie } } -} \ No newline at end of file + +} + diff --git a/app/src/main/java/com/example/harmonyhub/ui/home/HomeScreen.kt b/app/src/main/java/com/example/harmonyhub/ui/home/HomeScreen.kt index 5215124..5529836 100644 --- a/app/src/main/java/com/example/harmonyhub/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/example/harmonyhub/ui/home/HomeScreen.kt @@ -1,5 +1,6 @@ package com.example.harmonyhub.ui.home +import android.util.Log import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable @@ -37,12 +38,18 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis +import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.harmonyhub.R +import com.example.harmonyhub.ui.HomeViewModel import com.example.harmonyhub.ui.components.AppScaffoldWithDrawer import com.example.harmonyhub.ui.components.ArtistsCard import com.example.harmonyhub.ui.theme.NotoSans +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue + private val gradientBackground = Brush.verticalGradient( colors = listOf( @@ -58,8 +65,41 @@ fun HomeScreen( onLibraryButtonClicked: () -> Unit, onProfileButtonClicked: () -> Unit, onLogoutButtonClicked: () -> Unit, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + viewModel: HomeViewModel = androidx.lifecycle.viewmodel.compose.viewModel() // Khởi tạo ViewModel ) { + + //add view model + val state by viewModel.state.collectAsState() + + LaunchedEffect(Unit) { + viewModel.fetchHomePageData() + Log.d("HomeScreen", "Popular Artists: ${state.listPopularArtist?.size}") + } + + // Loading UI + if (state.isLoading) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text(text = "Loading...") + } + return + } + + // Error UI + state.errorMessage?.let { error -> + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Text(text = "Error: $error") + } + return + } + + //Main UI AppScaffoldWithDrawer( onProfileClicked = onProfileButtonClicked, onSettingsClicked = {}, @@ -159,16 +199,14 @@ fun HomeScreen( modifier = Modifier.padding(vertical = 8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - items( - listOf( - "Alan Walker" to R.drawable.v, - "Ed Sheeran" to R.drawable.v, - "The Weeknd" to R.drawable.v, - "Adele" to R.drawable.v, - "Taylor Swift" to R.drawable.v + items(state.listPopularArtist?: emptyList()) { artist -> + // Lấy dữ liệu từ mỗi item và truyền vào ArtistsCard + ArtistsCard( + artist.name, // Tên nghệ sĩ + artist.image, // URL ảnh + artist.id // ID nghệ sĩ ) - ) { - ArtistsCard(it.first, it.second) + } } @@ -237,6 +275,8 @@ fun HomeScreen( } } + + @Composable fun GenreCard(genre: String) { Surface( @@ -325,3 +365,13 @@ fun ChartsCard(chartImg: Int) { } } } + + + +@Preview +@Composable +fun HomeScreenPre() { + val homeViewModel = HomeViewModel() + HomeScreen({},{},{},{},{}, modifier = Modifier, viewModel = homeViewModel + ) +} diff --git a/app/src/main/java/com/example/harmonyhub/ui/library/ArtistsFollowingScreen.kt b/app/src/main/java/com/example/harmonyhub/ui/library/ArtistsFollowingScreen.kt index 1a09d53..5616dbf 100644 --- a/app/src/main/java/com/example/harmonyhub/ui/library/ArtistsFollowingScreen.kt +++ b/app/src/main/java/com/example/harmonyhub/ui/library/ArtistsFollowingScreen.kt @@ -38,7 +38,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalFocusManager @@ -63,7 +62,7 @@ fun ArtistsFollowingScreen( val focusManager = LocalFocusManager.current val allArtists = listOf( - Artist("The Chainsmokers", R.drawable.v), + Artist("The Chainsmokers", R.drawable.v,), Artist("Sia", R.drawable.v), Artist("Adele", R.drawable.v) ) @@ -115,7 +114,7 @@ fun ArtistsFollowingScreen( } - // Ô tìm kiếm + // Ô tìm kiếm nghee six TextField( value = query, onValueChange = { query = it }, @@ -190,7 +189,8 @@ fun ArtistsFollowingScreen( artistPair.forEach { artist -> ArtistsCard( artistName = artist.name, - artistImg = artist.img, + artistImg = "https://i.scdn.co/image/ab67616d00001e02fd8d7a8d96871e791cb1f626", + idArtist = "111111", modifier = Modifier .weight(1f) .padding(8.dp) diff --git a/app/src/main/res/drawable/ic_broken_image.xml b/app/src/main/res/drawable/ic_broken_image.xml new file mode 100644 index 0000000..ca041ad --- /dev/null +++ b/app/src/main/res/drawable/ic_broken_image.xml @@ -0,0 +1,25 @@ + + + + diff --git a/app/src/main/res/drawable/ic_connection_error.xml b/app/src/main/res/drawable/ic_connection_error.xml new file mode 100644 index 0000000..68ccb04 --- /dev/null +++ b/app/src/main/res/drawable/ic_connection_error.xml @@ -0,0 +1,25 @@ + + + + diff --git a/app/src/main/res/drawable/loading_img.xml b/app/src/main/res/drawable/loading_img.xml new file mode 100644 index 0000000..99f9f94 --- /dev/null +++ b/app/src/main/res/drawable/loading_img.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2d94ec4..f8675ea 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,4 +13,7 @@ Playlist Download Artists Following + Connection error + Refresh + \ No newline at end of file From 87eb515622149eb482356673e3bfe794be14315f Mon Sep 17 00:00:00 2001 From: minhnhatdeii <125042319+minhnhatdeii@users.noreply.github.com> Date: Wed, 27 Nov 2024 02:50:09 +0700 Subject: [PATCH 2/3] add viewmodel 2 --- app/build.gradle.kts | 6 + app/src/main/AndroidManifest.xml | 1 + .../com/example/harmonyhub/HarmonyHubApp.kt | 5 +- .../harmonyhub/HarmonyHubApplication.kt | 15 ++ .../example/harmonyhub/data/HomeUIState.kt | 11 -- .../data/{ => network}/APIService.kt | 7 +- .../{ => network}/HomeOverViewDataClassIn.kt | 19 ++- .../data/repository/DefaultHomeScreenRepo.kt | 95 +++++++++++ .../data/repository/HomeScreenRepo.kt | 7 + .../example/harmonyhub/ui/HomeViewModel.kt | 45 ------ .../example/harmonyhub/ui/home/HomeScreen.kt | 150 ++++++++++++------ .../example/harmonyhub/ui/home/HomeUIState.kt | 12 ++ .../harmonyhub/ui/home/HomeViewModel.kt | 49 ++++++ 13 files changed, 301 insertions(+), 121 deletions(-) create mode 100644 app/src/main/java/com/example/harmonyhub/HarmonyHubApplication.kt delete mode 100644 app/src/main/java/com/example/harmonyhub/data/HomeUIState.kt rename app/src/main/java/com/example/harmonyhub/data/{ => network}/APIService.kt (95%) rename app/src/main/java/com/example/harmonyhub/data/{ => network}/HomeOverViewDataClassIn.kt (73%) create mode 100644 app/src/main/java/com/example/harmonyhub/data/repository/DefaultHomeScreenRepo.kt create mode 100644 app/src/main/java/com/example/harmonyhub/data/repository/HomeScreenRepo.kt delete mode 100644 app/src/main/java/com/example/harmonyhub/ui/HomeViewModel.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/home/HomeUIState.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/home/HomeViewModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 71a11bd..60ce295 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -76,6 +76,12 @@ dependencies { debugImplementation(libs.androidx.ui.test.manifest) implementation("io.coil-kt:coil-compose:2.2.2") // Phiên bản mới nhất + implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1") + implementation ("androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha03") + // Retrofit + implementation ("com.squareup.retrofit2:retrofit:2.9.0") + implementation ("com.squareup.retrofit2:converter-gson:2.9.0") + } \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ee84758..d8a2630 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -4,6 +4,7 @@ ? = mutableListOf(), - val listPopularAlbums: MutableList? = mutableListOf(), - val listRecommendSong: MutableList? = mutableListOf(), - val isLoading: Boolean = false, - val errorMessage: String? = null -) diff --git a/app/src/main/java/com/example/harmonyhub/data/APIService.kt b/app/src/main/java/com/example/harmonyhub/data/network/APIService.kt similarity index 95% rename from app/src/main/java/com/example/harmonyhub/data/APIService.kt rename to app/src/main/java/com/example/harmonyhub/data/network/APIService.kt index 0f7e43d..2f913c6 100644 --- a/app/src/main/java/com/example/harmonyhub/data/APIService.kt +++ b/app/src/main/java/com/example/harmonyhub/data/network/APIService.kt @@ -1,6 +1,5 @@ -package com.example.harmonyhub.data +package com.example.harmonyhub.data.network -import com.example.harmonyhub.ui.components.Song import com.google.gson.Gson import okhttp3.OkHttpClient import okhttp3.Request @@ -81,6 +80,6 @@ object APIService { fun main() { val api = APIService - api.getHomePageOverview() - println("${api.listPopularArtist?.size}") + APIService.getHomePageOverview() + println("${APIService.listPopularArtist?.size}") } \ No newline at end of file diff --git a/app/src/main/java/com/example/harmonyhub/data/HomeOverViewDataClassIn.kt b/app/src/main/java/com/example/harmonyhub/data/network/HomeOverViewDataClassIn.kt similarity index 73% rename from app/src/main/java/com/example/harmonyhub/data/HomeOverViewDataClassIn.kt rename to app/src/main/java/com/example/harmonyhub/data/network/HomeOverViewDataClassIn.kt index 35e32f6..c0943fc 100644 --- a/app/src/main/java/com/example/harmonyhub/data/HomeOverViewDataClassIn.kt +++ b/app/src/main/java/com/example/harmonyhub/data/network/HomeOverViewDataClassIn.kt @@ -1,4 +1,4 @@ -package com.example.harmonyhub.data +package com.example.harmonyhub.data.network data class Avatar( val url: String, @@ -42,12 +42,12 @@ data class ArtistOut( data class PopularItem( - val type: String, - val id: String, - val name: String, - val cover: List,//album - val artists: List,//album - val visuals: Avatars? = null,//Artist + val type: String, + val id: String, + val name: String, + val cover: List,//album + val artists: List,//album + val visuals: Avatars? = null,//Artist ) @@ -75,3 +75,8 @@ data class Response( val sections: Sections ) +data class ResponseHomeScreenData( + val listPopularArtist: MutableList?, + val listPopularAlbums: MutableList? +) + diff --git a/app/src/main/java/com/example/harmonyhub/data/repository/DefaultHomeScreenRepo.kt b/app/src/main/java/com/example/harmonyhub/data/repository/DefaultHomeScreenRepo.kt new file mode 100644 index 0000000..825f271 --- /dev/null +++ b/app/src/main/java/com/example/harmonyhub/data/repository/DefaultHomeScreenRepo.kt @@ -0,0 +1,95 @@ +package com.example.harmonyhub.data.repository + + +import com.example.harmonyhub.data.network.AlbumOut +import com.example.harmonyhub.data.network.ArtistOut + +import com.example.harmonyhub.data.network.Response +import com.example.harmonyhub.data.network.ResponseHomeScreenData +import com.google.gson.Gson +import okhttp3.OkHttpClient +import okhttp3.Request + +class DefaultHomeScreenRepo : HomeScreenRepo { + override suspend fun updatePopularItem(): ResponseHomeScreenData? { + val listPopularArtist : MutableList? = mutableListOf() + + val listPopularAlbums: MutableList? = mutableListOf() + + var result : ResponseHomeScreenData? = null + try { + + val client = OkHttpClient() + + val request = Request.Builder() + .url("https://spotify-scraper.p.rapidapi.com/v1/home") + .get() + .addHeader("x-rapidapi-key", "c18da195b0mshcdebcf46df53015p1a1b64jsn33955d1b96fc") + .addHeader("x-rapidapi-host", "spotify-scraper.p.rapidapi.com") + .build() + + val response = client.newCall(request).execute() + // Kiểm tra xem phản hồi có thành công không + if (response.isSuccessful) { + val jsonResponse = response.body?.string() + //println("Response: $jsonResponse") + + // Kiểm tra nếu jsonResponse không null và là chuỗi hợp lệ + if (jsonResponse != null) { + try { + val gson = Gson() + val responseData = gson.fromJson(jsonResponse, Response::class.java) + + // Trích xuất dữ liệu + if (responseData.status) { + //trich xuat Popular Artise + val PopularArtist = responseData.sections.items[0].contents.items + val PopularAlbums = responseData.sections.items[1].contents.items + for (i in PopularArtist!!) { + val subArtist = + ArtistOut(i.id, i.name, i.visuals?.avatar?.get(1)?.url) + listPopularArtist?.add(subArtist) + } + + for (i in PopularAlbums!!) {//duyet phan tu album + var listArtistInAlbum: MutableList = mutableListOf() + for (j in i.artists) {// truy van nghe si cua album + listArtistInAlbum.add(j.name) + } + val subAlbum = + AlbumOut(i.id, i.name, i.cover[1].url, listArtistInAlbum) + listPopularAlbums?.add(subAlbum) + } + result = ResponseHomeScreenData(listPopularArtist, listPopularAlbums) + return result +// for (i in listPopularArtist!!) { +// println("${i.id} - ${i.name} - ${i.image}") +// } +// for (i in listPopularAlbums!!) { +// println("${i.id} - ${i.name} - ${i.image} ") +// for( j in i.listArtist) { +// println("${j}") +// } +// } + + } else { + println("Error: Request was not successful") + } + } catch (e: Exception) { + println("Error parsing JSON: ${e.message}") + } + } else { + println("Error: JSON response is null.") + } + } else { + println("Request failed with code: ${response.code}") + } + } catch (e: Exception) { + e.printStackTrace() + null + } finally { + return result + } + + } +} diff --git a/app/src/main/java/com/example/harmonyhub/data/repository/HomeScreenRepo.kt b/app/src/main/java/com/example/harmonyhub/data/repository/HomeScreenRepo.kt new file mode 100644 index 0000000..cf9359a --- /dev/null +++ b/app/src/main/java/com/example/harmonyhub/data/repository/HomeScreenRepo.kt @@ -0,0 +1,7 @@ +package com.example.harmonyhub.data.repository + +import com.example.harmonyhub.data.network.ResponseHomeScreenData + +interface HomeScreenRepo { + suspend fun updatePopularItem() : ResponseHomeScreenData? +} \ No newline at end of file diff --git a/app/src/main/java/com/example/harmonyhub/ui/HomeViewModel.kt b/app/src/main/java/com/example/harmonyhub/ui/HomeViewModel.kt deleted file mode 100644 index 6e44b1e..0000000 --- a/app/src/main/java/com/example/harmonyhub/ui/HomeViewModel.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.example.harmonyhub.ui - -import android.util.Log -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope -import com.example.harmonyhub.data.APIService -import com.example.harmonyhub.data.APIService.getHomePageOverview -import com.example.harmonyhub.data.HomeUIState -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.launch - -class HomeViewModel : ViewModel() { - private val _state = MutableStateFlow(HomeUIState()) - val state: StateFlow = _state.asStateFlow() - - init { - fetchHomePageData() - } - - fun fetchHomePageData() { - viewModelScope.launch { - _state.value = _state.value.copy(isLoading = true) - - try { - // Gọi API - val api = APIService - api.getHomePageOverview() - Log.d("HomeVMScreen", "Popular Artists: ${api.listPopularArtist?.size}") - // Cập nhật state với dữ liệu từ API - _state.value = _state.value.copy( - listPopularArtist = api.listPopularArtist, - listPopularAlbums = api.listPopularAlbums, - isLoading = false - ) - } catch (e: Exception) { - _state.value = _state.value.copy( - isLoading = false, - errorMessage = e.message - ) - } - } - } -} diff --git a/app/src/main/java/com/example/harmonyhub/ui/home/HomeScreen.kt b/app/src/main/java/com/example/harmonyhub/ui/home/HomeScreen.kt index 5529836..8522c0a 100644 --- a/app/src/main/java/com/example/harmonyhub/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/example/harmonyhub/ui/home/HomeScreen.kt @@ -22,7 +22,9 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Notifications +import androidx.compose.material.icons.filled.Refresh import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Surface @@ -38,17 +40,17 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow.Companion.Ellipsis -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.harmonyhub.R -import com.example.harmonyhub.ui.HomeViewModel import com.example.harmonyhub.ui.components.AppScaffoldWithDrawer import com.example.harmonyhub.ui.components.ArtistsCard import com.example.harmonyhub.ui.theme.NotoSans -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue +import androidx.compose.ui.res.stringResource +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import com.example.harmonyhub.data.network.ArtistOut +import com.example.harmonyhub.data.network.ResponseHomeScreenData private val gradientBackground = Brush.verticalGradient( @@ -58,6 +60,42 @@ private val gradientBackground = Brush.verticalGradient( ) ) +@Composable +fun LoadingScreen() { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator(color = Color.Blue) + } +} + +@Composable +fun ErrorScreen( + onRefreshContent: () -> Unit +) { + Box( + modifier = Modifier.fillMaxSize(), + contentAlignment = Alignment.Center + ) { + Column( + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Image( + painter = painterResource(id = R.drawable.ic_connection_error), + contentDescription = stringResource(R.string.connection_error) + ) + IconButton(onClick = onRefreshContent) { + Icon( + imageVector = Icons.Default.Refresh, + contentDescription = stringResource(R.string.refresh) + ) + } + } + } +} + @Composable fun HomeScreen( onSearchButtonClicked: () -> Unit, @@ -66,39 +104,47 @@ fun HomeScreen( onProfileButtonClicked: () -> Unit, onLogoutButtonClicked: () -> Unit, modifier: Modifier = Modifier, - viewModel: HomeViewModel = androidx.lifecycle.viewmodel.compose.viewModel() // Khởi tạo ViewModel + viewModel: HomeViewModel = viewModel(factory = HomeViewModel.Factory) ) { //add view model - val state by viewModel.state.collectAsState() - - LaunchedEffect(Unit) { - viewModel.fetchHomePageData() - Log.d("HomeScreen", "Popular Artists: ${state.listPopularArtist?.size}") - } - - // Loading UI - if (state.isLoading) { - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Text(text = "Loading...") + val homeUiState = viewModel.state.collectAsStateWithLifecycle().value + when (homeUiState) { + is HomeUIState.Loading -> { + LoadingScreen() } - return - } - - // Error UI - state.errorMessage?.let { error -> - Box( - modifier = Modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Text(text = "Error: $error") + is HomeUIState.Error -> { + ErrorScreen( + onRefreshContent = { viewModel.fetchHomePageData() } + ) + } + is HomeUIState.Success -> { + // Truy cập vào thuộc tính popularItem khi trạng thái là Success + val popularItems = homeUiState.popularItem + MainHomeScreen( + onSearchButtonClicked, + onPlayButtonClicked, + onLibraryButtonClicked, + onProfileButtonClicked, + onLogoutButtonClicked, + modifier, + popularItems + ) } - return } +} + +@Composable +fun MainHomeScreen( + onSearchButtonClicked: () -> Unit, + onPlayButtonClicked: () -> Unit, + onLibraryButtonClicked: () -> Unit, + onProfileButtonClicked: () -> Unit, + onLogoutButtonClicked: () -> Unit, + modifier: Modifier = Modifier, + resPopularItem: ResponseHomeScreenData +) { //Main UI AppScaffoldWithDrawer( onProfileClicked = onProfileButtonClicked, @@ -195,20 +241,8 @@ fun HomeScreen( fontSize = 24.sp ) ) - LazyRow( - modifier = Modifier.padding(vertical = 8.dp), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - items(state.listPopularArtist?: emptyList()) { artist -> - // Lấy dữ liệu từ mỗi item và truyền vào ArtistsCard - ArtistsCard( - artist.name, // Tên nghệ sĩ - artist.image, // URL ảnh - artist.id // ID nghệ sĩ - ) - } - } + LazyRowArtist(resPopularItem.listPopularArtist) Spacer(modifier = Modifier.height(16.dp)) @@ -276,7 +310,6 @@ fun HomeScreen( } - @Composable fun GenreCard(genre: String) { Surface( @@ -367,11 +400,26 @@ fun ChartsCard(chartImg: Int) { } - -@Preview @Composable -fun HomeScreenPre() { - val homeViewModel = HomeViewModel() - HomeScreen({},{},{},{},{}, modifier = Modifier, viewModel = homeViewModel - ) +fun LazyRowArtist(temple: MutableList?) { + LazyRow( + modifier = Modifier.padding(vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + val listArtist: MutableList? = temple + if (listArtist != null) { + Log.d("CheckMDKMWKAMD", "${listArtist.size}") + items(listArtist) { artist -> + // Lấy dữ liệu từ mỗi item và truyền vào ArtistsCard + ArtistsCard( + artist.name, // Tên nghệ sĩ + artist.image, // URL ảnh + artist.id // ID nghệ sĩ + ) + } + } + + } + } + diff --git a/app/src/main/java/com/example/harmonyhub/ui/home/HomeUIState.kt b/app/src/main/java/com/example/harmonyhub/ui/home/HomeUIState.kt new file mode 100644 index 0000000..7403de9 --- /dev/null +++ b/app/src/main/java/com/example/harmonyhub/ui/home/HomeUIState.kt @@ -0,0 +1,12 @@ +package com.example.harmonyhub.ui.home + +import com.example.harmonyhub.data.network.AlbumOut +import com.example.harmonyhub.data.network.ArtistOut +import com.example.harmonyhub.data.network.ResponseHomeScreenData +import com.example.harmonyhub.ui.components.Song + +sealed interface HomeUIState { + object Loading : HomeUIState + object Error : HomeUIState + data class Success(val popularItem: ResponseHomeScreenData) : HomeUIState +} diff --git a/app/src/main/java/com/example/harmonyhub/ui/home/HomeViewModel.kt b/app/src/main/java/com/example/harmonyhub/ui/home/HomeViewModel.kt new file mode 100644 index 0000000..53dc889 --- /dev/null +++ b/app/src/main/java/com/example/harmonyhub/ui/home/HomeViewModel.kt @@ -0,0 +1,49 @@ +package com.example.harmonyhub.ui.home + +import android.util.Log +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY +import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory +import com.example.harmonyhub.HarmonyHubApplication +import com.example.harmonyhub.data.network.APIService +import com.example.harmonyhub.data.repository.HomeScreenRepo + +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.launch + +class HomeViewModel( + private val homeScreenRepo : HomeScreenRepo +) : ViewModel() { + private val _state = MutableStateFlow(HomeUIState.Loading) + val state = _state.asStateFlow() + + init { + fetchHomePageData() + } + + fun fetchHomePageData() { + viewModelScope.launch { + _state.value = HomeUIState.Loading + val result = homeScreenRepo.updatePopularItem() + + _state.value = result?.let { + HomeUIState.Success(it) + } ?: HomeUIState.Error + + } + } + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val application = (this[APPLICATION_KEY] as HarmonyHubApplication) + val homeRepository = application.container + HomeViewModel(homeScreenRepo = homeRepository) + } + } + } +} From e84a8acf70364570a86dbc393a3e5f46030ca03b Mon Sep 17 00:00:00 2001 From: minhnhatdeii <125042319+minhnhatdeii@users.noreply.github.com> Date: Thu, 28 Nov 2024 05:08:40 +0700 Subject: [PATCH 3/3] add data to HomeScreen --- .github/workflows/deploy.yml | 40 +++ .github/workflows/lint_debug.yml | 34 +++ .github/workflows/pull_request_push.yml | 16 ++ .github/workflows/test_debug.yml | 26 ++ .gitignore | 1 + .idea/androidTestResultsUserPreferences.xml | 139 ++++++++++ .idea/appInsightsSettings.xml | 8 +- .idea/inspectionProfiles/Project_Default.xml | 4 + .idea/kotlinc.xml | 2 +- README.md | 10 + app/.gitignore | 2 +- app/build.gradle.kts | 48 ++-- .../components_test/NavigationDrawerTest.kt | 53 ++++ .../harmonyhub/home_test/HomeScreenTest.kt | 85 ++++++ app/src/main/AndroidManifest.xml | 2 +- .../com/example/harmonyhub/HarmonyHubApp.kt | 145 ++++++++-- .../com/example/harmonyhub/MainActivity.kt | 3 +- ...monyHubApplication.kt => MyApplication.kt} | 6 +- .../com/example/harmonyhub/SongRepository.kt | 30 -- .../example/harmonyhub/data/SongRepository.kt | 34 +++ .../harmonyhub/data/network/APIService.kt | 85 ------ .../data/network/HomeOverViewDataClassIn.kt | 33 ++- .../data/repository/DefaultHomeScreenRepo.kt | 34 ++- .../data/repository/UserDataRepoImpl.kt | 73 +++++ .../com/example/harmonyhub/di/AppModule.kt | 46 ++++ .../domain/repository/UserDataRepo.kt | 9 + .../viewmodel/AuthenticationViewModel.kt | 116 ++++++++ .../viewmodel/UserDataViewModel.kt | 32 +++ .../ui/account/ForgotPasswordScreen.kt | 239 ++++++++++++++++ .../ui/{login => account}/LoginScreen.kt | 119 ++++---- .../ui/account/NewPasswordScreen.kt | 221 +++++++++++++++ .../ui/{login => account}/RegisterScreen.kt | 174 ++++++++++-- .../ui/account/VerificationScreen.kt | 242 ++++++++++++++++ .../harmonyhub/ui/components/AlbumCard.kt | 78 ++++++ .../harmonyhub/ui/components/ArtistCard.kt | 13 +- .../harmonyhub/ui/components/ChartCard.kt | 69 +++++ .../harmonyhub/ui/components/GenreCard.kt | 53 ++++ .../ui/components/NavigationDrawer.kt | 10 +- .../harmonyhub/ui/components/PlaylistCard.kt | 10 +- .../harmonyhub/ui/components/SongCard.kt | 35 ++- .../ui/components/SuggestionCard.kt | 80 ++++++ .../example/harmonyhub/ui/home/HomeScreen.kt | 224 ++++++--------- .../harmonyhub/ui/home/HomeViewModel.kt | 8 +- .../ui/library/AddSongToPlaylistScreen.kt | 170 ++++++++++++ .../harmonyhub/ui/library/ArtistScreen.kt | 174 ++++++++++++ .../ui/library/ArtistsFollowingScreen.kt | 7 +- .../harmonyhub/ui/library/DownloadScreen.kt | 6 +- .../harmonyhub/ui/library/FavoriteScreen.kt | 6 +- .../harmonyhub/ui/library/HistoryScreen.kt | 8 +- .../harmonyhub/ui/library/LibraryScreen.kt | 258 ++++++++++-------- .../harmonyhub/ui/library/PlaylistSongList.kt | 207 ++++++++++++++ .../harmonyhub/ui/library/PlaylistsScreen.kt | 104 ++++++- .../example/harmonyhub/ui/library/SongList.kt | 5 +- .../harmonyhub/ui/play/NowPlayingBar.kt | 112 ++++++++ .../example/harmonyhub/ui/play/PlayScreen.kt | 36 ++- .../harmonyhub/ui/profile/ProfileScreen.kt | 160 ++++++++++- .../harmonyhub/ui/search/SearchScreen.kt | 8 +- .../harmonyhub/ui/settings/SettingsScreen.kt | 72 ++++- app/src/main/res/values/strings.xml | 5 + build.gradle.kts | 18 ++ google-services.json | 29 ++ gradle/libs.versions.toml | 10 +- gradle/wrapper/gradle-wrapper.properties | 4 +- 63 files changed, 3495 insertions(+), 595 deletions(-) create mode 100644 .github/workflows/deploy.yml create mode 100644 .github/workflows/lint_debug.yml create mode 100644 .github/workflows/pull_request_push.yml create mode 100644 .github/workflows/test_debug.yml create mode 100644 .idea/androidTestResultsUserPreferences.xml create mode 100644 README.md create mode 100644 app/src/androidTest/java/com/example/harmonyhub/components_test/NavigationDrawerTest.kt create mode 100644 app/src/androidTest/java/com/example/harmonyhub/home_test/HomeScreenTest.kt rename app/src/main/java/com/example/harmonyhub/{HarmonyHubApplication.kt => MyApplication.kt} (78%) delete mode 100644 app/src/main/java/com/example/harmonyhub/SongRepository.kt create mode 100644 app/src/main/java/com/example/harmonyhub/data/SongRepository.kt delete mode 100644 app/src/main/java/com/example/harmonyhub/data/network/APIService.kt create mode 100644 app/src/main/java/com/example/harmonyhub/data/repository/UserDataRepoImpl.kt create mode 100644 app/src/main/java/com/example/harmonyhub/di/AppModule.kt create mode 100644 app/src/main/java/com/example/harmonyhub/domain/repository/UserDataRepo.kt create mode 100644 app/src/main/java/com/example/harmonyhub/presentation/viewmodel/AuthenticationViewModel.kt create mode 100644 app/src/main/java/com/example/harmonyhub/presentation/viewmodel/UserDataViewModel.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/account/ForgotPasswordScreen.kt rename app/src/main/java/com/example/harmonyhub/ui/{login => account}/LoginScreen.kt (68%) create mode 100644 app/src/main/java/com/example/harmonyhub/ui/account/NewPasswordScreen.kt rename app/src/main/java/com/example/harmonyhub/ui/{login => account}/RegisterScreen.kt (50%) create mode 100644 app/src/main/java/com/example/harmonyhub/ui/account/VerificationScreen.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/components/AlbumCard.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/components/ChartCard.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/components/GenreCard.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/components/SuggestionCard.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/library/AddSongToPlaylistScreen.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/library/ArtistScreen.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/library/PlaylistSongList.kt create mode 100644 app/src/main/java/com/example/harmonyhub/ui/play/NowPlayingBar.kt create mode 100644 google-services.json diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..9676f45 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,40 @@ +name: Deploy to Firebase + +on: + workflow_dispatch: + inputs: + release_notes: + type: string + required: true + default: 'Test Deploy to Firebase' + description: 'Release Notes' + +jobs: + build: + name: Building and distributing app + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.2.2 + + - uses: actions/setup-java@v4.5.0 + with: + distribution: 'temurin' + java-version: '17' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Execute Gradle command - assembleDebug + run: ./gradlew assembleDebug + + - name: Upload Artifact to Firebase App Distribution + uses: wzieba/Firebase-Distribution-Github-Action@v1 + with: + appId: ${{ secrets.FIREBASE_APP_ID }} + serviceCredentialsFileContent: ${{ secrets.CREDENTIAL_FILE_CONTENT }} + groups: testers + file: app/build/apk/debug/app-debug.apk + releaseNotes: ${{ inputs.release_notes }} diff --git a/.github/workflows/lint_debug.yml b/.github/workflows/lint_debug.yml new file mode 100644 index 0000000..ee58732 --- /dev/null +++ b/.github/workflows/lint_debug.yml @@ -0,0 +1,34 @@ +# This is a basic workflow to help you get started with Actions + +name: Lint debug + +# Controls when the workflow will run +on: + workflow_call: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + lint: + name: Linting debug variant + # The type of runner that the job will run on + runs-on: ubuntu-latest + + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v4.2.2 + + - uses: actions/setup-java@v4.5.0 + with: + distribution: 'temurin' + java-version: '17' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Execute Gradle command - lintDebug + run: ./gradlew lintDebug diff --git a/.github/workflows/pull_request_push.yml b/.github/workflows/pull_request_push.yml new file mode 100644 index 0000000..88ce46b --- /dev/null +++ b/.github/workflows/pull_request_push.yml @@ -0,0 +1,16 @@ +name: Pull Request And Push + +on: + pull_request: + branches: [ main, frontend, backend, entry, minhnhat_branch] + push: + branches: [ main, frontend, backend, entry, minhnhat_branch] + +jobs: + lint: + name: Lint Debug Variant + uses: ./.github/workflows/lint_debug.yml + + test: + name: Test Debug Variant + uses: ./.github/workflows/test_debug.yml diff --git a/.github/workflows/test_debug.yml b/.github/workflows/test_debug.yml new file mode 100644 index 0000000..58c66cd --- /dev/null +++ b/.github/workflows/test_debug.yml @@ -0,0 +1,26 @@ +name: Test debug + +on: + workflow_call: + +jobs: + run_tests: + name: Testing debug variant + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4.2.2 + + - uses: actions/setup-java@v4.5.0 + with: + distribution: 'temurin' + java-version: '17' + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v4 + + - name: Make gradlew executable + run: chmod +x ./gradlew + + - name: Execute Gradle command - testDebugUnitTest + run: ./gradlew testDebugUnitTest diff --git a/.gitignore b/.gitignore index 226d61c..1b648e0 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ .externalNativeBuild .cxx local.properties +.idea/deploymentTargetSelector.xml diff --git a/.idea/androidTestResultsUserPreferences.xml b/.idea/androidTestResultsUserPreferences.xml new file mode 100644 index 0000000..90fe323 --- /dev/null +++ b/.idea/androidTestResultsUserPreferences.xml @@ -0,0 +1,139 @@ + + + + + + \ No newline at end of file diff --git a/.idea/appInsightsSettings.xml b/.idea/appInsightsSettings.xml index 371f2e2..33144a3 100644 --- a/.idea/appInsightsSettings.xml +++ b/.idea/appInsightsSettings.xml @@ -8,10 +8,10 @@