diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/.name b/.idea/.name new file mode 100644 index 0000000..0f301f5 --- /dev/null +++ b/.idea/.name @@ -0,0 +1 @@ +TmsTemp \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml new file mode 100644 index 0000000..fb7f4a8 --- /dev/null +++ b/.idea/compiler.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml new file mode 100644 index 0000000..a2d7c21 --- /dev/null +++ b/.idea/gradle.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..509af26 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 93d31ce..4c257f0 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,8 @@ plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'androidx.navigation.safeargs.kotlin' + id 'kotlinx-serialization' } android { @@ -29,13 +31,44 @@ android { kotlinOptions { jvmTarget = '1.8' } + viewBinding { + enabled = true + } } dependencies { + def koin_version= "3.2.0" +// Koin Core features + implementation "io.insert-koin:koin-android:$koin_version" + implementation "io.insert-koin:koin-core:$koin_version" +// Navigation Graph + implementation "io.insert-koin:koin-androidx-navigation:$koin_version" +// Koin Test features + testImplementation "io.insert-koin:koin-test:$koin_version" + + implementation "com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:0.8.0" + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation "com.squareup.okhttp3:okhttp:4.9.3" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.2" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.1" + + implementation "androidx.navigation:navigation-fragment-ktx:2.4.2" + implementation "androidx.navigation:navigation-ui-ktx:2.4.2" + + implementation "androidx.fragment:fragment-ktx:1.4.1" + implementation "androidx.activity:activity-ktx:1.4.0" + + implementation "androidx.recyclerview:recyclerview:1.2.1" + + implementation "io.coil-kt:coil:2.0.0-rc03" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1" + implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1' + implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.1' implementation 'androidx.core:core-ktx:1.7.0' implementation 'androidx.appcompat:appcompat:1.4.1' - implementation 'com.google.android.material:material:1.5.0' + implementation 'com.google.android.material:material:1.6.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.3' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.3' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a551aff..00adc67 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,9 @@ + diff --git a/app/src/main/java/com/github/krottv/tmstemp/MainActivity.kt b/app/src/main/java/com/github/krottv/tmstemp/MainActivity.kt deleted file mode 100644 index 8952f64..0000000 --- a/app/src/main/java/com/github/krottv/tmstemp/MainActivity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.krottv.tmstemp - -import androidx.appcompat.app.AppCompatActivity -import android.os.Bundle - -class MainActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/MyApp.kt b/app/src/main/java/com/github/krottv/tmstemp/MyApp.kt new file mode 100644 index 0000000..4d56ec7 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/MyApp.kt @@ -0,0 +1,39 @@ +package com.github.krottv.tmstemp + +import android.app.Application +import com.github.krottv.tmstemp.data.ITunesRemoteDataSourceRetrofit +import com.github.krottv.tmstemp.data.LibraryRemoteDataSourceRetrofit +import com.github.krottv.tmstemp.data.MusicApi +import com.github.krottv.tmstemp.presentation.AlbumsViewModel +import com.github.krottv.tmstemp.presentation.TracksViewModel +import org.koin.android.ext.koin.androidContext +import org.koin.androidx.viewmodel.dsl.viewModel +import org.koin.core.context.startKoin +import org.koin.core.module.Module +import org.koin.core.module.dsl.factoryOf +import org.koin.dsl.module + +class MyApp : Application() { + + private val itunesModule: Module + get() = module { + factory { ITunesRemoteDataSourceRetrofit() } + } + private val libraryModule: Module + get() = module { + factory { LibraryRemoteDataSourceRetrofit() } + } + private val viewModelModule: Module + get() = module { + viewModel { AlbumsViewModel(get()) } + viewModel { TracksViewModel(get()) } + } + + override fun onCreate() { + super.onCreate() + startKoin { + androidContext(this@MyApp) + modules(libraryModule, itunesModule, viewModelModule) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/data/DataSourceFake.kt b/app/src/main/java/com/github/krottv/tmstemp/data/DataSourceFake.kt new file mode 100644 index 0000000..ebbbd0d --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/data/DataSourceFake.kt @@ -0,0 +1,20 @@ +package com.github.krottv.tmstemp.data + +import com.github.krottv.tmstemp.domain.AlbumModel + +class DataSourceFake { + + fun getITunesAlbums(): List{ + val model = AlbumModel(0, + "https://inspiry-2ee60.web.app/music/images/itunes/hip_hop.jpg", + "Some Text", + 10) + + val mutableListOf = ArrayList(10) + for (i in 0..10){ + mutableListOf.add(model.copy(name = "Some Text $i")) + } + + return mutableListOf + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/data/ITunesRemoteDataSourceRetrofit.kt b/app/src/main/java/com/github/krottv/tmstemp/data/ITunesRemoteDataSourceRetrofit.kt new file mode 100644 index 0000000..f019a0b --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/data/ITunesRemoteDataSourceRetrofit.kt @@ -0,0 +1,35 @@ +package com.github.krottv.tmstemp.data + +import android.util.Log +import com.github.krottv.tmstemp.domain.AlbumModel +import com.github.krottv.tmstemp.domain.Tracks +import com.github.krottv.tmstemp.domain.TracksModel +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import retrofit2.Retrofit +import retrofit2.create + +class ITunesRemoteDataSourceRetrofit : MusicApi { + + override suspend fun getAlbums(): List { + val retrofit = Retrofit.Builder() + .baseUrl("https://us-central1-inspiry-2ee60.cloudfunctions.net/getItunesAlbums/") + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + + val musicApi: MusicApi = retrofit.create() + return musicApi.getAlbums() + } + + override suspend fun getTracks(albumId: Long): TracksModel { + val retrofit = Retrofit.Builder() + .baseUrl("https://us-central1-inspiry-2ee60.cloudfunctions.net/getItunesTracks/") + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + + val musicApi: MusicApi = retrofit.create() + + return musicApi.getTracks(1) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/data/LibraryRemoteDataSourceRetrofit.kt b/app/src/main/java/com/github/krottv/tmstemp/data/LibraryRemoteDataSourceRetrofit.kt new file mode 100644 index 0000000..ebe48e9 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/data/LibraryRemoteDataSourceRetrofit.kt @@ -0,0 +1,34 @@ +package com.github.krottv.tmstemp.data + +import com.github.krottv.tmstemp.domain.AlbumModel +import com.github.krottv.tmstemp.domain.TracksModel +import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory +import kotlinx.serialization.json.Json +import okhttp3.MediaType.Companion.toMediaType +import retrofit2.Retrofit +import retrofit2.create + +class LibraryRemoteDataSourceRetrofit : MusicApi { + + override suspend fun getAlbums(): List { + val retrofit = Retrofit.Builder() + .baseUrl("https://us-central1-inspiry-2ee60.cloudfunctions.net/getLibraryAlbums/") + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + + val musicApi: MusicApi = retrofit.create() + + return musicApi.getAlbums() + } + + override suspend fun getTracks(albumId: Long): TracksModel { + val retrofit = Retrofit.Builder() + .baseUrl("https://us-central1-inspiry-2ee60.cloudfunctions.net/getLibraryTracks/") + .addConverterFactory(Json.asConverterFactory("application/json".toMediaType())) + .build() + + val musicApi: MusicApi = retrofit.create() + + return musicApi.getTracks(1) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/data/MusicApi.kt b/app/src/main/java/com/github/krottv/tmstemp/data/MusicApi.kt new file mode 100644 index 0000000..374da12 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/data/MusicApi.kt @@ -0,0 +1,14 @@ +package com.github.krottv.tmstemp.data + +import com.github.krottv.tmstemp.domain.AlbumModel +import com.github.krottv.tmstemp.domain.TracksModel +import retrofit2.http.GET +import retrofit2.http.Query + +interface MusicApi { + @GET("getAlbums") + suspend fun getAlbums(): List + + @GET("getTrack") + suspend fun getTracks(@Query("albumId") albumId: Long): TracksModel +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/domain/Album.kt b/app/src/main/java/com/github/krottv/tmstemp/domain/Album.kt new file mode 100644 index 0000000..a054ef1 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/domain/Album.kt @@ -0,0 +1,11 @@ +package com.github.krottv.tmstemp.domain + +import kotlinx.serialization.Serializable + +@Serializable +data class Album( + var id: Int, + var image: String, + var name: String, + var trackCount: Int +) diff --git a/app/src/main/java/com/github/krottv/tmstemp/domain/AlbumModel.kt b/app/src/main/java/com/github/krottv/tmstemp/domain/AlbumModel.kt new file mode 100644 index 0000000..4c5e726 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/domain/AlbumModel.kt @@ -0,0 +1,13 @@ +package com.github.krottv.tmstemp.domain + +import android.media.Image +import kotlinx.serialization.Serializable + +@Serializable +data class AlbumModel( + val id: Long, + val image: String, + val name: String, + val trackCount: Int) { + +} diff --git a/app/src/main/java/com/github/krottv/tmstemp/domain/Tracks.kt b/app/src/main/java/com/github/krottv/tmstemp/domain/Tracks.kt new file mode 100644 index 0000000..9db0d25 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/domain/Tracks.kt @@ -0,0 +1,11 @@ +package com.github.krottv.tmstemp.domain + +import kotlinx.serialization.Serializable + +@Serializable +data class Tracks( + var artist: String, + var image: String, + var title: String, + var url: String +) diff --git a/app/src/main/java/com/github/krottv/tmstemp/domain/TracksModel.kt b/app/src/main/java/com/github/krottv/tmstemp/domain/TracksModel.kt new file mode 100644 index 0000000..ad98d78 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/domain/TracksModel.kt @@ -0,0 +1,9 @@ +package com.github.krottv.tmstemp.domain + +import kotlinx.serialization.Serializable + +@Serializable +data class TracksModel( + var album: Album , + var tracks: ArrayList +) diff --git a/app/src/main/java/com/github/krottv/tmstemp/presentation/AlbumsViewModel.kt b/app/src/main/java/com/github/krottv/tmstemp/presentation/AlbumsViewModel.kt new file mode 100644 index 0000000..b996866 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/presentation/AlbumsViewModel.kt @@ -0,0 +1,25 @@ +package com.github.krottv.tmstemp.presentation + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.github.krottv.tmstemp.data.ITunesRemoteDataSourceRetrofit +import com.github.krottv.tmstemp.data.MusicApi +import com.github.krottv.tmstemp.domain.AlbumModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +class AlbumsViewModel(val musicApi: MusicApi): ViewModel(){ + + private val _stateITunes = MutableStateFlow?>(null) + val stateITunes: StateFlow?> = _stateITunes + + fun loadData() { + viewModelScope.launch(Dispatchers.IO) { + _stateITunes.emit(musicApi.getAlbums()) + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/presentation/TracksViewModel.kt b/app/src/main/java/com/github/krottv/tmstemp/presentation/TracksViewModel.kt new file mode 100644 index 0000000..600d4be --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/presentation/TracksViewModel.kt @@ -0,0 +1,24 @@ +package com.github.krottv.tmstemp.presentation + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.github.krottv.tmstemp.data.ITunesRemoteDataSourceRetrofit +import com.github.krottv.tmstemp.data.MusicApi +import com.github.krottv.tmstemp.domain.AlbumModel +import com.github.krottv.tmstemp.domain.Tracks +import com.github.krottv.tmstemp.domain.TracksModel +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.launch + +class TracksViewModel(val musicApi: MusicApi): ViewModel() { + private val _stateITunes = MutableStateFlow(null) + val stateITunes: StateFlow = _stateITunes + + fun loadTracks() { + viewModelScope.launch(Dispatchers.IO) { + _stateITunes.emit(musicApi.getTracks(1)) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/AlbumsAdapter.kt b/app/src/main/java/com/github/krottv/tmstemp/view/AlbumsAdapter.kt new file mode 100644 index 0000000..5fc0164 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/AlbumsAdapter.kt @@ -0,0 +1,46 @@ +package com.github.krottv.tmstemp.view + +import android.graphics.Outline +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.ViewOutlineProvider +import androidx.recyclerview.widget.RecyclerView +import coil.load +import com.github.krottv.tmstemp.R +import com.github.krottv.tmstemp.domain.AlbumModel +import com.github.krottv.tmstemp.domain.Tracks + +class AlbumsAdapter(data: List): RecyclerView.Adapter() { + + var data: List = data + set(value) { + field = value + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlbumsViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_album, parent, false) + + return AlbumsViewHolder(view) + } + + override fun onBindViewHolder(holder: AlbumsViewHolder, position: Int) { + val item = data[position] + holder.imageAlbum.load(item.image) + holder.textAlbum.text = item.name + + holder.imageAlbum.clipToOutline = true + holder.imageAlbum.outlineProvider = object: ViewOutlineProvider() { + override fun getOutline(p0: View, p1: Outline) { + p1.setRoundRect(0, 0, p0.width, p0.height, 10.0F) + } + } + } + + override fun getItemCount(): Int { + return data.size + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/AlbumsViewHolder.kt b/app/src/main/java/com/github/krottv/tmstemp/view/AlbumsViewHolder.kt new file mode 100644 index 0000000..a2518e0 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/AlbumsViewHolder.kt @@ -0,0 +1,13 @@ +package com.github.krottv.tmstemp.view + +import android.media.Image +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.github.krottv.tmstemp.R + +class AlbumsViewHolder(view: View): RecyclerView.ViewHolder(view){ + val imageAlbum = view.findViewById(R.id.imageAlbum) + val textAlbum = view.findViewById(R.id.albumText) +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/HostFragment.kt b/app/src/main/java/com/github/krottv/tmstemp/view/HostFragment.kt new file mode 100644 index 0000000..adcd278 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/HostFragment.kt @@ -0,0 +1,26 @@ +package com.github.krottv.tmstemp.view + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment + +class HostFragment : Fragment() { + + lateinit var viewBinder: HostFragmentBinder + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + viewBinder = HostFragmentBinder(this) + + return viewBinder.onCreateView(inflater, container, savedInstanceState) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/HostFragmentBinder.kt b/app/src/main/java/com/github/krottv/tmstemp/view/HostFragmentBinder.kt new file mode 100644 index 0000000..2307581 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/HostFragmentBinder.kt @@ -0,0 +1,21 @@ +package com.github.krottv.tmstemp.view + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.github.krottv.tmstemp.databinding.HostFragmentBinding + +class HostFragmentBinder(val fragment: HostFragment) { + + lateinit var binding: HostFragmentBinding + + fun onCreateView(inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = HostFragmentBinding.inflate(inflater, container, false) + + return binding.root + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/ITunesMusicFragment.kt b/app/src/main/java/com/github/krottv/tmstemp/view/ITunesMusicFragment.kt new file mode 100644 index 0000000..adcd884 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/ITunesMusicFragment.kt @@ -0,0 +1,70 @@ +package com.github.krottv.tmstemp.view + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.fragment.findNavController +import com.github.krottv.tmstemp.R +import com.github.krottv.tmstemp.data.ITunesRemoteDataSourceRetrofit +import com.github.krottv.tmstemp.presentation.AlbumsViewModel +import com.github.krottv.tmstemp.presentation.TracksViewModel +import kotlinx.coroutines.launch +import org.koin.android.ext.android.inject + +class ITunesMusicFragment : Fragment() { + + lateinit var viewBinder: ITunesMusicFragmentBinder + private val viewModel: AlbumsViewModel by inject() + private val tracksITunesViewModel: TracksViewModel by inject() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + viewBinder = ITunesMusicFragmentBinder(this) + + return viewBinder.onCreateView(inflater, container, savedInstanceState) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + + (parentFragment as NavHostFragment).parentFragment?.view?.findViewById(R.id.library) + ?.setOnClickListener { + val navController = findNavController() + + val action = + ITunesMusicFragmentDirections.actionITunesMusicFragmentToLibraryMusicFragment() + navController.navigate(action) + } + + viewModel.loadData() + + lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.stateITunes.collect { + viewBinder.onDataLoaded(it) + } + } + } + + tracksITunesViewModel.loadTracks() + + lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + tracksITunesViewModel.stateITunes.collect { + + viewBinder.tracksLoaded(it) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/ITunesMusicFragmentBinder.kt b/app/src/main/java/com/github/krottv/tmstemp/view/ITunesMusicFragmentBinder.kt new file mode 100644 index 0000000..94905e2 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/ITunesMusicFragmentBinder.kt @@ -0,0 +1,55 @@ +package com.github.krottv.tmstemp.view + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.github.krottv.tmstemp.databinding.ItunesMusicFragmentBinding +import com.github.krottv.tmstemp.domain.AlbumModel +import com.github.krottv.tmstemp.domain.Tracks +import com.github.krottv.tmstemp.domain.TracksModel + +class ITunesMusicFragmentBinder(val fragment: ITunesMusicFragment) { + + lateinit var binding: ItunesMusicFragmentBinding + + fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = ItunesMusicFragmentBinding.inflate(inflater, container, false) + + var layoutManager = + LinearLayoutManager(fragment.requireActivity(), LinearLayoutManager.HORIZONTAL, false) + + binding.albumsRecyclerITunes.layoutManager = layoutManager + + layoutManager = + LinearLayoutManager(fragment.requireActivity()) + + binding.tracksRecyclerITunes.layoutManager = layoutManager + + return binding.root + } + + fun onDataLoaded(list: List?) { + if (list != null) + if (binding.albumsRecyclerITunes.adapter == null) + binding.albumsRecyclerITunes.adapter = AlbumsAdapter(list) + else + (binding.albumsRecyclerITunes.adapter as AlbumsAdapter).data = list + } + + fun tracksLoaded(list: TracksModel?) { + + if (list != null) { + if (binding.tracksRecyclerITunes.adapter == null) + binding.tracksRecyclerITunes.adapter = TracksAdapter(list.tracks) + else + (binding.tracksRecyclerITunes.adapter as TracksAdapter).data = list.tracks + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/LibraryMusicFragment.kt b/app/src/main/java/com/github/krottv/tmstemp/view/LibraryMusicFragment.kt new file mode 100644 index 0000000..1fccc6c --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/LibraryMusicFragment.kt @@ -0,0 +1,65 @@ +package com.github.krottv.tmstemp.view + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import androidx.lifecycle.repeatOnLifecycle +import androidx.navigation.fragment.NavHostFragment +import androidx.navigation.fragment.findNavController +import com.github.krottv.tmstemp.R +import com.github.krottv.tmstemp.data.LibraryRemoteDataSourceRetrofit +import com.github.krottv.tmstemp.presentation.AlbumsViewModel +import com.github.krottv.tmstemp.presentation.TracksViewModel +import kotlinx.coroutines.launch + +class LibraryMusicFragment : Fragment() { + lateinit var viewBinder: LibraryMusicFragmentBinder + private val viewModel: AlbumsViewModel = AlbumsViewModel(LibraryRemoteDataSourceRetrofit()) + private val tracksLibraryViewModel: TracksViewModel = TracksViewModel(LibraryRemoteDataSourceRetrofit()) + + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + viewBinder = LibraryMusicFragmentBinder(this) + + return viewBinder.onCreateView(inflater, container, savedInstanceState) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + (parentFragment as NavHostFragment).parentFragment?.view?.findViewById(R.id.iTunes)?.setOnClickListener{ + val navController = findNavController() + + val action = LibraryMusicFragmentDirections.actionLibraryMusicFragmentToITunesMusicFragment() + navController.navigate(action) + } + + viewModel.loadData() + + lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + viewModel.stateITunes.collect { + viewBinder.onDataLoaded(it) + } + } + } + + tracksLibraryViewModel.loadTracks() + + lifecycleScope.launch { + viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { + tracksLibraryViewModel.stateITunes.collect { + viewBinder.tracksLoaded(it) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/LibraryMusicFragmentBinder.kt b/app/src/main/java/com/github/krottv/tmstemp/view/LibraryMusicFragmentBinder.kt new file mode 100644 index 0000000..abd6018 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/LibraryMusicFragmentBinder.kt @@ -0,0 +1,53 @@ +package com.github.krottv.tmstemp.view + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import com.github.krottv.tmstemp.databinding.LibraryMusicFragmentBinding +import com.github.krottv.tmstemp.domain.AlbumModel +import com.github.krottv.tmstemp.domain.TracksModel + +class LibraryMusicFragmentBinder (val fragment: LibraryMusicFragment) { + + lateinit var binding: LibraryMusicFragmentBinding + + fun onCreateView(inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = LibraryMusicFragmentBinding.inflate(inflater, container, false) + + var layoutManager = + LinearLayoutManager(fragment.requireActivity(), LinearLayoutManager.HORIZONTAL, false) + + binding.albumsRecyclerLibrary.layoutManager = layoutManager + + layoutManager = + LinearLayoutManager(fragment.requireActivity()) + + binding.tracksRecyclerAlbum.layoutManager = layoutManager + + return binding.root + } + + fun onDataLoaded(list: List?) { + + if (list != null) + if (binding.albumsRecyclerLibrary.adapter == null) + binding.albumsRecyclerLibrary.adapter = AlbumsAdapter(list) + else + (binding.albumsRecyclerLibrary.adapter as AlbumsAdapter).data = list + } + + fun tracksLoaded(list: TracksModel?) { + + if (list != null) { + if (binding.tracksRecyclerAlbum.adapter == null) + binding.tracksRecyclerAlbum.adapter = TracksAdapter(list.tracks) + else + (binding.tracksRecyclerAlbum.adapter as TracksAdapter).data = list.tracks + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/MainActivity.kt b/app/src/main/java/com/github/krottv/tmstemp/view/MainActivity.kt new file mode 100644 index 0000000..b87627c --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/MainActivity.kt @@ -0,0 +1,18 @@ +package com.github.krottv.tmstemp.view + +import android.os.Bundle +import android.view.LayoutInflater +import androidx.appcompat.app.AppCompatActivity +import com.github.krottv.tmstemp.databinding.ActivityMainBinding + +class MainActivity : AppCompatActivity() { + + lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + binding = ActivityMainBinding.inflate(LayoutInflater.from(this)) + setContentView(binding.root) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/TracksAdapter.kt b/app/src/main/java/com/github/krottv/tmstemp/view/TracksAdapter.kt new file mode 100644 index 0000000..6afca88 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/TracksAdapter.kt @@ -0,0 +1,46 @@ +package com.github.krottv.tmstemp.view + +import android.graphics.Outline +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.ViewOutlineProvider +import androidx.recyclerview.widget.RecyclerView +import coil.load +import com.github.krottv.tmstemp.R +import com.github.krottv.tmstemp.domain.AlbumModel +import com.github.krottv.tmstemp.domain.Tracks + +class TracksAdapter(data: List) : RecyclerView.Adapter() { + + var data: List = data + set(value) { + field = value + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TracksViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_track, parent, false) + + return TracksViewHolder(view) + } + + override fun onBindViewHolder(holder: TracksViewHolder, position: Int) { + val item = data[position] + holder.imageTrack.load(item.image.replace("{w}", "200").replace("{h}", "200")) + holder.titleTrack.text = item.title + holder.textTrack.text = item.artist + + holder.imageTrack.clipToOutline = true + holder.imageTrack.outlineProvider = object: ViewOutlineProvider() { + override fun getOutline(p0: View, p1: Outline) { + p1.setRoundRect(0, 0, p0.width, p0.height, 8.0F) + } + } + } + + override fun getItemCount(): Int { + return data.size + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/github/krottv/tmstemp/view/TracksViewHolder.kt b/app/src/main/java/com/github/krottv/tmstemp/view/TracksViewHolder.kt new file mode 100644 index 0000000..020c074 --- /dev/null +++ b/app/src/main/java/com/github/krottv/tmstemp/view/TracksViewHolder.kt @@ -0,0 +1,13 @@ +package com.github.krottv.tmstemp.view + +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.github.krottv.tmstemp.R + +class TracksViewHolder(view: View): RecyclerView.ViewHolder(view){ + val imageTrack = view.findViewById(R.id.imageTrack) + val titleTrack = view.findViewById(R.id.titleTrack) + val textTrack = view.findViewById(R.id.textTrack) +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 4fc2444..851bb24 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -4,15 +4,19 @@ xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" - tools:context=".MainActivity"> + tools:context=".view.MainActivity"> - + \ No newline at end of file diff --git a/app/src/main/res/layout/host_fragment.xml b/app/src/main/res/layout/host_fragment.xml new file mode 100644 index 0000000..8d4f6b0 --- /dev/null +++ b/app/src/main/res/layout/host_fragment.xml @@ -0,0 +1,52 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_album.xml b/app/src/main/res/layout/item_album.xml new file mode 100644 index 0000000..497d15f --- /dev/null +++ b/app/src/main/res/layout/item_album.xml @@ -0,0 +1,29 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_track.xml b/app/src/main/res/layout/item_track.xml new file mode 100644 index 0000000..f3e65fd --- /dev/null +++ b/app/src/main/res/layout/item_track.xml @@ -0,0 +1,40 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/itunes_music_fragment.xml b/app/src/main/res/layout/itunes_music_fragment.xml new file mode 100644 index 0000000..63fff7a --- /dev/null +++ b/app/src/main/res/layout/itunes_music_fragment.xml @@ -0,0 +1,26 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/library_music_fragment.xml b/app/src/main/res/layout/library_music_fragment.xml new file mode 100644 index 0000000..b8d448f --- /dev/null +++ b/app/src/main/res/layout/library_music_fragment.xml @@ -0,0 +1,24 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/itunes_library_graph.xml b/app/src/main/res/navigation/itunes_library_graph.xml new file mode 100644 index 0000000..bff66a4 --- /dev/null +++ b/app/src/main/res/navigation/itunes_library_graph.xml @@ -0,0 +1,23 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml new file mode 100644 index 0000000..2ffd182 --- /dev/null +++ b/app/src/main/res/navigation/nav_graph.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-night/themes.xml b/app/src/main/res/values-night/themes.xml index 48853ef..2e9bfbf 100644 --- a/app/src/main/res/values-night/themes.xml +++ b/app/src/main/res/values-night/themes.xml @@ -1,9 +1,9 @@ -