diff --git a/app/src/main/java/com/uzlov/moviefind/fragments/FilmFragment.kt b/app/src/main/java/com/uzlov/moviefind/fragments/FilmFragment.kt index fb50aaa..a3ed3a2 100644 --- a/app/src/main/java/com/uzlov/moviefind/fragments/FilmFragment.kt +++ b/app/src/main/java/com/uzlov/moviefind/fragments/FilmFragment.kt @@ -5,22 +5,26 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.lifecycle.ViewModelProvider import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.uzlov.moviefind.databinding.FragmentFilmBinding -import com.uzlov.moviefind.model.TestFilm import com.uzlov.moviefind.ui.ActorFilmsAdapter import com.uzlov.moviefind.ui.MyItemDecorator +import com.uzlov.moviefind.viewmodels.FilmsViewModel class FilmFragment : Fragment() { private var _viewBinding: FragmentFilmBinding?=null private val viewBinding get() = _viewBinding!! - private lateinit var film : TestFilm + private val viewModel: FilmsViewModel by lazy { + ViewModelProvider(this).get(FilmsViewModel::class.java) + } + private var id_film = 0 companion object { - fun newInstance(testFilm: TestFilm): FilmFragment { + fun newInstance(id: Int): FilmFragment { val data = Bundle().apply { - putParcelable("film_key", testFilm) + putInt("film_key", id) } return FilmFragment().apply { arguments = data } } @@ -28,9 +32,10 @@ class FilmFragment : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - film = arguments?.getParcelable("film_key") ?: TestFilm() + id_film = arguments?.getInt("film_key") ?: 0 } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -42,24 +47,27 @@ class FilmFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - with(viewBinding) { - titleTv.text = film.name - ratingFilm.rating = film.rating.toFloat() - descriptionTV.text = film.description - genreFilmTv.text = film.genre - studioFilm.text = film.studio - yearFilmTv.text = film.year - recyclerViewActor.apply { - adapter = ActorFilmsAdapter() - layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false) - addItemDecoration(MyItemDecorator(1), RecyclerView.HORIZONTAL) - } + viewModel.getFilmById(id_film).observe(viewLifecycleOwner, { + with(viewBinding) { + titleTv.text = it.title + ratingFilm.rating = it.vote_average.div(2).toFloat() + descriptionTV.text = it.overview + genreFilmTv.text = it.genres.joinToString(", ") + studioFilm.text = it.production_companies[0].name + yearFilmTv.text = it.release_date - backButton.setOnClickListener { - parentFragmentManager.popBackStack() + recyclerViewActor.apply { + adapter = ActorFilmsAdapter() + layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false) + addItemDecoration(MyItemDecorator(1), RecyclerView.HORIZONTAL) + } + + backButton.setOnClickListener { + parentFragmentManager.popBackStack() + } } - } + }) } override fun onDestroyView() { diff --git a/app/src/main/java/com/uzlov/moviefind/fragments/HomeFragment.kt b/app/src/main/java/com/uzlov/moviefind/fragments/HomeFragment.kt index fd723ce..d0c722b 100644 --- a/app/src/main/java/com/uzlov/moviefind/fragments/HomeFragment.kt +++ b/app/src/main/java/com/uzlov/moviefind/fragments/HomeFragment.kt @@ -5,25 +5,62 @@ import androidx.fragment.app.Fragment import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Button -import android.widget.Toast -import com.uzlov.moviefind.R +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager -class HomeFragment : Fragment() { - companion object{ - fun newInstance() : HomeFragment { - return HomeFragment() - } +import com.uzlov.moviefind.R +import com.uzlov.moviefind.databinding.FragmentHomeBinding +import com.uzlov.moviefind.interfaces.IOnClickListenerAdapter +import com.uzlov.moviefind.ui.FilmAdapter +import com.uzlov.moviefind.viewmodels.FilmsViewModel + +class HomeFragment : Fragment() , IOnClickListenerAdapter{ + private var _viewBinding: FragmentHomeBinding ?= null + private val viewBinding get() = _viewBinding!! + private val viewModel: FilmsViewModel by lazy { + ViewModelProvider(this).get(FilmsViewModel::class.java) } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_home, container, false) + ): View { + _viewBinding = FragmentHomeBinding.inflate(layoutInflater, container, false) + return viewBinding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + loadData() + } + + private fun loadData(){ + val adapterTopFilms = FilmAdapter(this) + viewModel.getTopRatedFilms().observe(viewLifecycleOwner, { + adapterTopFilms.setFilms(it.results) + viewBinding.recommendRV.apply { + adapter = adapterTopFilms + layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + } + }) + + val adapterPopularFilms = FilmAdapter(this) + viewModel.getPopularFilms().observe(viewLifecycleOwner, { + viewBinding.popularRV.apply { + adapterPopularFilms.setFilms(it.results) + adapter = adapterPopularFilms + layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) + } + }) + } + + override fun onClick(position: Int, id: Int) { + parentFragmentManager.beginTransaction().run { + hide(this@HomeFragment) + add(R.id.fragment_container, FilmFragment.newInstance(id)) + addToBackStack(null) + commit() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/uzlov/moviefind/interfaces/Loadable.kt b/app/src/main/java/com/uzlov/moviefind/interfaces/Loadable.kt index 97a3228..7cc5f1d 100644 --- a/app/src/main/java/com/uzlov/moviefind/interfaces/Loadable.kt +++ b/app/src/main/java/com/uzlov/moviefind/interfaces/Loadable.kt @@ -1,6 +1,12 @@ package com.uzlov.moviefind.interfaces +import androidx.lifecycle.MutableLiveData +import com.uzlov.moviefind.model.Film +import com.uzlov.moviefind.model.PopularFilms + @FunctionalInterface -public interface Loadable { - fun loadPopular(films: (List) -> Unit) +public interface Loadable { + fun loadPopular() : MutableLiveData + fun loadFilmById(id: Int) : MutableLiveData + fun loadTopRatedFilms(): MutableLiveData } \ No newline at end of file diff --git a/app/src/main/java/com/uzlov/moviefind/model/Film.kt b/app/src/main/java/com/uzlov/moviefind/model/Film.kt index b7035b9..f69e425 100644 --- a/app/src/main/java/com/uzlov/moviefind/model/Film.kt +++ b/app/src/main/java/com/uzlov/moviefind/model/Film.kt @@ -26,4 +26,11 @@ data class Film( val video: Boolean, val vote_average: Double, val vote_count: Int -) \ No newline at end of file +) { + fun getImage50() : String { + return "https://image.tmdb.org/t/p/w500$poster_path" + } + fun getImageOriginal() : String { + return "https://image.tmdb.org/t/p/original$poster_path" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/uzlov/moviefind/model/Result.kt b/app/src/main/java/com/uzlov/moviefind/model/Result.kt index da140af..a6c158a 100644 --- a/app/src/main/java/com/uzlov/moviefind/model/Result.kt +++ b/app/src/main/java/com/uzlov/moviefind/model/Result.kt @@ -15,4 +15,11 @@ data class Result( val video: Boolean, val vote_average: Double, val vote_count: Int -) \ No newline at end of file +) { + fun getImage50() : String { + return "https://image.tmdb.org/t/p/w500$poster_path" + } + fun getImageOriginal() : String { + return "https://image.tmdb.org/t/p/original$poster_path" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/uzlov/moviefind/repository/RepositoryPopularImpl.kt b/app/src/main/java/com/uzlov/moviefind/repository/RepositoryPopularImpl.kt index 688bff2..edddd34 100644 --- a/app/src/main/java/com/uzlov/moviefind/repository/RepositoryPopularImpl.kt +++ b/app/src/main/java/com/uzlov/moviefind/repository/RepositoryPopularImpl.kt @@ -1,11 +1,134 @@ package com.uzlov.moviefind.repository +import android.os.Build +import androidx.lifecycle.MutableLiveData +import com.google.gson.Gson +import com.uzlov.moviefind.interfaces.Constants import com.uzlov.moviefind.interfaces.Loadable +import com.uzlov.moviefind.model.Film import com.uzlov.moviefind.model.PopularFilms +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStreamReader +import java.net.MalformedURLException +import java.net.URL +import java.util.stream.Collectors +import javax.net.ssl.HttpsURLConnection -object RepositoryPopularImpl : Loadable { - override fun loadPopular(films: (List) -> Unit) { +object RepositoryPopularImpl : Loadable { + override fun loadPopular() : MutableLiveData{ + val uriPopularFilms = URL("https://api.themoviedb.org/3/movie/popular?api_key=${Constants.API_KEY}&language=ru-RU") + val responseLiveData: MutableLiveData = MutableLiveData() + + + try { + Thread { + lateinit var urlConnection: HttpsURLConnection + try { + urlConnection = (uriPopularFilms.openConnection() as HttpsURLConnection).apply { + requestMethod = "GET" + readTimeout = 10_000 + } + val bufferedReader = + BufferedReader(InputStreamReader(urlConnection.inputStream)) + + val popularFilms = + Gson().fromJson(bufferedReader.getLines(), PopularFilms::class.java) + responseLiveData.postValue(popularFilms) + } catch (e: Exception) { + e.printStackTrace() + } finally { + urlConnection.disconnect() + } + }.start() + } catch (e: MalformedURLException){ + e.printStackTrace() + } + return responseLiveData + } + + override fun loadFilmById(id: Int): MutableLiveData { + val uriDetailFilm = URL("https://api.themoviedb.org/3/movie/$id?api_key=${Constants.API_KEY}&language=ru-RU") + val responseLiveData: MutableLiveData = MutableLiveData() + + try { + Thread { + lateinit var urlConnection: HttpsURLConnection + try { + urlConnection = (uriDetailFilm.openConnection() as HttpsURLConnection).apply { + requestMethod = "GET" + readTimeout = 10_000 + } + val bufferedReader = + BufferedReader(InputStreamReader(urlConnection.inputStream)) + + val film = + Gson().fromJson(bufferedReader.getLines(), Film::class.java) + responseLiveData.postValue(film) + + } catch (e: Exception) { + e.printStackTrace() + } finally { + urlConnection.disconnect() + } + }.start() + } catch (e: MalformedURLException){ + e.printStackTrace() + } + return responseLiveData + } + + + override fun loadTopRatedFilms(): MutableLiveData { + val uriDetailFilm = URL("https://api.themoviedb.org/3/movie/top_rated?api_key=${Constants.API_KEY}&language=ru-RU") + val responseLiveData: MutableLiveData = MutableLiveData() + + try { + Thread { + lateinit var urlConnection: HttpsURLConnection + try { + urlConnection = (uriDetailFilm.openConnection() as HttpsURLConnection).apply { + requestMethod = "GET" + readTimeout = 10_000 + } + val bufferedReader = + BufferedReader(InputStreamReader(urlConnection.inputStream)) + + val film = + Gson().fromJson(bufferedReader.getLines(), PopularFilms::class.java) + responseLiveData.postValue(film) + + } catch (e: Exception) { + e.printStackTrace() + } finally { + urlConnection.disconnect() + } + }.start() + } catch (e: MalformedURLException){ + e.printStackTrace() + } + return responseLiveData + } + + + + + private fun BufferedReader.getLines() : String { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + lines().collect(Collectors.joining("\n")) + } else { + val builder = StringBuilder() + try { + var line: String? + while (readLine().also { line = it } != null) { + builder.append(line).append("\n") + } + } catch (e: IOException) { + e.printStackTrace() + } + return builder.toString() + } } } \ No newline at end of file diff --git a/app/src/main/java/com/uzlov/moviefind/ui/FilmAdapter.kt b/app/src/main/java/com/uzlov/moviefind/ui/FilmAdapter.kt new file mode 100644 index 0000000..f9cb5b2 --- /dev/null +++ b/app/src/main/java/com/uzlov/moviefind/ui/FilmAdapter.kt @@ -0,0 +1,47 @@ +package com.uzlov.moviefind.ui + +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import com.bumptech.glide.Glide +import com.uzlov.moviefind.databinding.ItemFilmBinding +import com.uzlov.moviefind.interfaces.IOnClickListenerAdapter +import com.uzlov.moviefind.model.Result + +class FilmAdapter(private val onClick: IOnClickListenerAdapter) : + RecyclerView.Adapter() { + private val mListFilms: MutableList = mutableListOf() + + fun setFilms(films: List) { + mListFilms.clear() + mListFilms.addAll(films) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FilmsHolder { + val binding = ItemFilmBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return FilmsHolder(binding) + } + + override fun onBindViewHolder(holder: FilmsHolder, position: Int) { + holder.onBind(mListFilms[position]) + } + + override fun getItemCount(): Int = mListFilms.size + + inner class FilmsHolder(private val binding: ItemFilmBinding) : + RecyclerView.ViewHolder(binding.root) { + fun onBind(film: Result) { + with(binding) { + Glide + .with(root.context) + .load(film.getImage50()) + .into(imageFilm) + titleFilmTV.text = film.title + ratingFilm.rating = film.vote_average.toFloat().div(2) + root.setOnClickListener { + onClick.onClick(adapterPosition, film.id) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/uzlov/moviefind/viewmodels/FilmsViewModel.kt b/app/src/main/java/com/uzlov/moviefind/viewmodels/FilmsViewModel.kt index e545024..30e1e6e 100644 --- a/app/src/main/java/com/uzlov/moviefind/viewmodels/FilmsViewModel.kt +++ b/app/src/main/java/com/uzlov/moviefind/viewmodels/FilmsViewModel.kt @@ -3,43 +3,28 @@ package com.uzlov.moviefind.viewmodels import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel -import com.uzlov.moviefind.model.TestFilm -import java.lang.Thread.sleep +import com.uzlov.moviefind.model.Film +import com.uzlov.moviefind.model.PopularFilms -class FilmsViewModel(private val liveDataToObserve: MutableLiveData> = MutableLiveData()) : ViewModel() { +import com.uzlov.moviefind.repository.RepositoryPopularImpl +class FilmsViewModel : ViewModel() { + private var liveDataToObserve: MutableLiveData = MutableLiveData() - fun getPopularFilms() : LiveData>{ + fun getPopularFilms() : LiveData{ getLocalDataFilms() return liveDataToObserve } + fun getFilmById(id: Int) : LiveData { + return RepositoryPopularImpl.loadFilmById(id) + } + private fun getLocalDataFilms(){ - Thread { - sleep(1000) - liveDataToObserve.postValue(listOf( - TestFilm("Бойцовский клуб","Боевик", 4.9, - "Тон фильму задают начальные кадры: показано неистовое движение нервного импульса (который можно трактовать как боль, страх или мысль) по лабиринту нервных волокон в мозговой ткани Рассказчика[3]. Уже в этих кадрах содержится намёк на суть показанного далее.", - "20 Century Fox", - "1999"), - TestFilm("Пила 2","Ужасы",3.5, - "Майкл приходит в себя в комнате, на голове у него раскрытая маска с шипами внутри («Маска смерти»). Включается телевизор, на экране появляется кукла Билли и она говорит Майклу, что тот — доносчик и стукач. Маска захлопнется и убьёт его, если он за минуту не успеет отыскать ключ, зашитый в его правую глазницу, скальпель прилагается. Сделав несколько попыток, Майкл понимает, что не сможет выколоть свой глаз, и маска, захлопнувшись, убивает его.", - "Lionsgate Films", - "2005"), - TestFilm("Большой куш","Боевик, комедия", 4.1, - "В Антверпене банда грабителей, переодетых в религиозных евреев, один из которых — Фрэнки «Четыре пальца» (Бенисио дель Торо), похищает из еврейской ювелирной конторы множество драгоценностей, среди которых бриллиант в 86 карат. Фрэнки предстоит доставить это сокровище в пристёгиваемом к руке бронированном дипломате с кодовой защитой прикрывающему эту банду ювелиру Ави Деновицу (Дэннис Фарина) в Нью-Йорк, транзитом через Лондон. ", - "Columbia Pictures", - "2000"), - TestFilm("1+1","Комедия, драма", 4.3, - "Парализованный богатый аристократ Филипп, ставший инвалидом после того, как разбился на параплане, ищет себе помощника, который должен за ним ухаживать. Одного из кандидатов, чернокожего Дрисса, работа не интересует — ему нужен формальный письменный отказ, чтобы продолжать получать пособие по безработице. Но неожиданно именно его Филипп берёт на работу.", - "Gaumont", - "2011" - ), - TestFilm("Терминатор","Боевик", 4.5, - "История противостояния солдата Кайла Риза и киборга-терминатора, прибывших в 1984-й год из пост-апокалиптического будущего, где миром правят машины-убийцы, а человечество находится на грани вымирания. Цель киборга: убить девушку по имени Сара Коннор, чей ещё нерождённый сын к 2029 году выиграет войну человечества с машинами. Цель Риза: спасти Сару и остановить Терминатора любой ценой.", - "Warner", - "1984")) - ) - }.start() + liveDataToObserve = RepositoryPopularImpl.loadPopular() + } + + fun getTopRatedFilms() : LiveData { + return RepositoryPopularImpl.loadTopRatedFilms() } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_film.xml b/app/src/main/res/layout/fragment_film.xml index 9178a63..9796f6a 100644 --- a/app/src/main/res/layout/fragment_film.xml +++ b/app/src/main/res/layout/fragment_film.xml @@ -22,8 +22,7 @@ app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" - app:shapeAppearanceOverlay="@style/BottomCornerRaduis" - app:srcCompat="@drawable/ter" /> + app:shapeAppearanceOverlay="@style/BottomCornerRaduis" /> + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_actor.xml b/app/src/main/res/layout/item_actor.xml index 16ed1b0..989a2ba 100644 --- a/app/src/main/res/layout/item_actor.xml +++ b/app/src/main/res/layout/item_actor.xml @@ -3,7 +3,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" xmlns:app="http://schemas.android.com/apk/res-auto" - android:orientation="vertical"> + android:orientation="vertical" + android:layout_margin="2dp"> - + android:layout_width="wrap_content" + android:orientation="vertical"> + + + - + app:layout_constraintLeft_toRightOf="@id/imageFilm" + app:layout_constraintTop_toTopOf="@id/imageFilm" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintBottom_toTopOf="@id/genreFilmTVLabel" + android:layout_margin="2dp"/> - - + style="@style/Widget.AppCompat.RatingBar.Small" + android:numStars="5" + android:isIndicator="true" + android:backgroundTint="@color/purple_500" + android:layout_margin="2dp"/> + - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6f4fa3e..7b3d361 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -110,4 +110,5 @@ Загружено! Ок Ещё... + Рекомендуем \ No newline at end of file