parent
fc371bf386
commit
e29247f3ce
@ -1,131 +1,88 @@
|
||||
package fr.iut.pm.movieapplication.repository
|
||||
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.liveData
|
||||
import fr.iut.pm.movieapplication.api.RetrofitInstance
|
||||
import fr.iut.pm.movieapplication.api.dtos.PopularDTO
|
||||
import fr.iut.pm.movieapplication.model.media.MediaResult
|
||||
import fr.iut.pm.movieapplication.model.media.movie.Movie
|
||||
import fr.iut.pm.movieapplication.utils.MediaResultMapper
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
||||
class MovieRepository() {
|
||||
class MovieRepository {
|
||||
|
||||
fun getPopularMovies(page : Int = 1 ,callback: (List<Movie>) -> Unit ) {
|
||||
suspend fun getPopularMovies(page : Int = 1) : List<Movie>
|
||||
{
|
||||
|
||||
val listMovie : MutableList<Movie> = mutableListOf()
|
||||
|
||||
RetrofitInstance.api.getPopularMovies(page = page).enqueue(object :
|
||||
Callback<PopularDTO> {
|
||||
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
|
||||
if (response.isSuccessful) {
|
||||
Log.d("List :", response.body().toString())
|
||||
val popularDTO = response.body()
|
||||
val listMoviesDTO = popularDTO?.results
|
||||
listMoviesDTO?.forEach {
|
||||
|
||||
val movie = MediaResultMapper.mapToMovie(it)
|
||||
listMovie.add(movie)
|
||||
Log.d("Movie ", movie.title!! )
|
||||
}
|
||||
|
||||
}
|
||||
callback(listMovie)
|
||||
val response = RetrofitInstance.api.getPopularMovies(page = page)
|
||||
if(response.isSuccessful) {
|
||||
val listMediaResultDTO = response.body()?.results
|
||||
listMediaResultDTO?.forEach {
|
||||
val movie = MediaResultMapper.mapToMovie(it)
|
||||
listMovie.add(movie)
|
||||
Log.d("Movie ", movie.title!!)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
|
||||
Log.d("Error failure", t.message.toString())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
else Log.d("ERROR FAILED", response.message())
|
||||
return listMovie
|
||||
}
|
||||
|
||||
fun getNowPlayingMovies(page : Int = 1, callback: (List<Movie>) -> Unit) {
|
||||
suspend fun getNowPlayingMovies(page : Int = 1) : List<Movie>
|
||||
{
|
||||
|
||||
val listMovie : MutableList<Movie> = mutableListOf()
|
||||
|
||||
RetrofitInstance.api.getNowPlayingMovies(page = page).enqueue(object :
|
||||
Callback<PopularDTO> {
|
||||
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
|
||||
if (response.isSuccessful) {
|
||||
Log.d("List :", response.body().toString())
|
||||
val popularDTO = response.body()
|
||||
val listMoviesDTO = popularDTO?.results
|
||||
listMoviesDTO?.forEach {
|
||||
|
||||
val movie = MediaResultMapper.mapToMovie(it)
|
||||
listMovie.add(movie)
|
||||
Log.d("Movie ", movie.title!! )
|
||||
}
|
||||
|
||||
}
|
||||
callback(listMovie)
|
||||
val response = RetrofitInstance.api.getNowPlayingMovies(page = page)
|
||||
if(response.isSuccessful) {
|
||||
val listMediaResultDTO = response.body()?.results
|
||||
listMediaResultDTO?.forEach {
|
||||
val movie = MediaResultMapper.mapToMovie(it)
|
||||
listMovie.add(movie)
|
||||
Log.d("Movie ", movie.title!!)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
|
||||
Log.d("Error failure", t.message.toString())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
else Log.d("ERROR FAILED", response.message())
|
||||
return listMovie
|
||||
}
|
||||
|
||||
fun getUpcomingMovies(page : Int = 1, callback: (List<Movie>) -> Unit) {
|
||||
suspend fun getUpcomingMovies(page : Int = 1) : List<Movie>
|
||||
{
|
||||
|
||||
val listMovie : MutableList<Movie> = mutableListOf()
|
||||
|
||||
RetrofitInstance.api.getUpcomingMovies(page = page).enqueue(object :
|
||||
Callback<PopularDTO> {
|
||||
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
|
||||
if (response.isSuccessful) {
|
||||
Log.d("List :", response.body().toString())
|
||||
val popularDTO = response.body()
|
||||
val listMoviesDTO = popularDTO?.results
|
||||
listMoviesDTO?.forEach {
|
||||
|
||||
val movie = MediaResultMapper.mapToMovie(it)
|
||||
listMovie.add(movie)
|
||||
Log.d("Movie ", movie.title!! )
|
||||
}
|
||||
|
||||
}
|
||||
callback(listMovie)
|
||||
val response = RetrofitInstance.api.getUpcomingMovies(page = page)
|
||||
if(response.isSuccessful) {
|
||||
val listMediaResultDTO = response.body()?.results
|
||||
listMediaResultDTO?.forEach {
|
||||
val movie = MediaResultMapper.mapToMovie(it)
|
||||
listMovie.add(movie)
|
||||
Log.d("Movie ", movie.title!!)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
|
||||
Log.d("Error failure", t.message.toString())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
else Log.d("ERROR FAILED", response.message())
|
||||
return listMovie
|
||||
}
|
||||
|
||||
fun getTopRatedMovies(page : Int = 1, callback: (List<Movie>) -> Unit) {
|
||||
suspend fun getTopRatedMovies(page : Int = 1) : List<Movie>
|
||||
{
|
||||
|
||||
val listMovie : MutableList<Movie> = mutableListOf()
|
||||
|
||||
RetrofitInstance.api.getTopRatedMovies(page = page).enqueue(object :
|
||||
Callback<PopularDTO> {
|
||||
override fun onResponse(call: Call<PopularDTO>, response: Response<PopularDTO>) {
|
||||
if (response.isSuccessful) {
|
||||
Log.d("List :", response.body().toString())
|
||||
val popularDTO = response.body()
|
||||
val listMoviesDTO = popularDTO?.results
|
||||
listMoviesDTO?.forEach {
|
||||
|
||||
val movie = MediaResultMapper.mapToMovie(it)
|
||||
listMovie.add(movie)
|
||||
Log.d("Movie ", movie.title!! )
|
||||
}
|
||||
|
||||
}
|
||||
callback(listMovie)
|
||||
val response = RetrofitInstance.api.getTopRatedMovies(page = page)
|
||||
if(response.isSuccessful) {
|
||||
val listMediaResultDTO = response.body()?.results
|
||||
listMediaResultDTO?.forEach {
|
||||
val movie = MediaResultMapper.mapToMovie(it)
|
||||
listMovie.add(movie)
|
||||
Log.d("Movie ", movie.title!!)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<PopularDTO>, t: Throwable) {
|
||||
Log.d("Error failure", t.message.toString())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
else Log.d("ERROR FAILED", response.message())
|
||||
return listMovie
|
||||
}
|
||||
|
||||
|
||||
|
@ -1,70 +0,0 @@
|
||||
package fr.iut.pm.movieapplication.ui.adapter
|
||||
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.net.toUri
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import fr.iut.pm.movieapplication.R
|
||||
import fr.iut.pm.movieapplication.model.media.movie.Movie
|
||||
import fr.iut.pm.movieapplication.model.media.tvshow.TvShow
|
||||
import fr.iut.pm.movieapplication.ui.activity.MainActivity
|
||||
import fr.iut.pm.movieapplication.utils.Constants.Companion.IMG_URL
|
||||
|
||||
class CategoryAdapter<T>(
|
||||
private val context: MainActivity,
|
||||
private val layoutId: Int,
|
||||
private val list: List<T>
|
||||
) : RecyclerView.Adapter<MediaAdapter.ViewHolder>(){
|
||||
|
||||
class ViewHolder(view : View) : RecyclerView.ViewHolder(view) {
|
||||
val itemImage: ImageView = view.findViewById<ImageView>(R.id.item_image)
|
||||
val itemName: TextView = view.findViewById<TextView>(R.id.item_name)
|
||||
val itemDate: TextView = view.findViewById<TextView>(R.id.item_date)
|
||||
}
|
||||
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaAdapter.ViewHolder {
|
||||
val view = LayoutInflater
|
||||
.from(parent.context)
|
||||
.inflate(layoutId, parent, false)
|
||||
return MediaAdapter.ViewHolder(view)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = list.size
|
||||
|
||||
|
||||
override fun onBindViewHolder(holder: MediaAdapter.ViewHolder, position: Int) {
|
||||
val currentItem = list[position]
|
||||
if(currentItem != null) {
|
||||
|
||||
when(currentItem!!::class.java) {
|
||||
Movie::class.java -> bindMovie(holder, currentItem as Movie)
|
||||
TvShow::class.java -> bindTvShow(holder, currentItem as TvShow)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindTvShow(holder: MediaAdapter.ViewHolder, tvShow: TvShow) {
|
||||
val imgUri = tvShow.posterPath?.let { (IMG_URL+it).toUri().buildUpon().scheme("https").build() }
|
||||
holder.itemImage.load(imgUri)
|
||||
holder.itemName.text = tvShow.name
|
||||
holder.itemDate.text = tvShow.firstAirDate
|
||||
}
|
||||
|
||||
private fun bindMovie(holder: MediaAdapter.ViewHolder, movie: Movie) {
|
||||
val imgUri = movie.posterPath?.let { (IMG_URL+it).toUri().buildUpon().scheme("https").build() }
|
||||
holder.itemImage.load(imgUri)
|
||||
holder.itemName.text = movie.title
|
||||
holder.itemDate.text = movie.releaseDate
|
||||
}
|
||||
|
||||
private fun onItemClick(item : Movie) {
|
||||
Log.d("item clicked", item.toString())
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
package fr.iut.pm.movieapplication.ui.adapter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.net.toUri
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import coil.load
|
||||
import fr.iut.pm.movieapplication.R
|
||||
import fr.iut.pm.movieapplication.databinding.ItemMovieCategoryBinding
|
||||
import fr.iut.pm.movieapplication.model.media.movie.Movie
|
||||
import fr.iut.pm.movieapplication.utils.Constants
|
||||
|
||||
class MovieAdapter : ListAdapter<Movie, MovieAdapter.ViewHolder>(DiffUtilDogCallback) {
|
||||
|
||||
private object DiffUtilDogCallback : DiffUtil.ItemCallback<Movie>() {
|
||||
override fun areItemsTheSame(oldItem: Movie, newItem: Movie) = oldItem.id == newItem.id
|
||||
override fun areContentsTheSame(oldItem: Movie, newItem: Movie) = oldItem == newItem
|
||||
}
|
||||
|
||||
class ViewHolder(private val binding : ItemMovieCategoryBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
val movie : Movie? get() = binding.movie
|
||||
|
||||
init {
|
||||
itemView.setOnClickListener {}
|
||||
}
|
||||
|
||||
fun bind(movie : Movie) {
|
||||
binding.movie = movie
|
||||
val imgUri = movie.posterPath?.let { (Constants.IMG_URL +it).toUri().buildUpon().scheme("https").build() }
|
||||
binding.itemImage.load(imgUri)
|
||||
binding.executePendingBindings()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder =
|
||||
ViewHolder(ItemMovieCategoryBinding.inflate(LayoutInflater.from(parent.context)))
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) = holder.bind(getItem(position))
|
||||
}
|
@ -1,27 +1,93 @@
|
||||
package fr.iut.pm.movieapplication.ui.viewmodel
|
||||
|
||||
import androidx.lifecycle.*
|
||||
import fr.iut.pm.movieapplication.model.media.movie.MovieDetails
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import fr.iut.pm.movieapplication.model.media.movie.Movie
|
||||
import fr.iut.pm.movieapplication.repository.MovieRepository
|
||||
import fr.iut.pm.movieapplication.ui.adapter.MovieAdapter
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class MoviesVM(private val repository: MovieRepository) : ViewModel() {
|
||||
class MoviesVM() : ViewModel() {
|
||||
//Movie repository
|
||||
private val repository = MovieRepository()
|
||||
//Live data
|
||||
private var _moviesLiveData : MutableLiveData<List<Movie>> = MutableLiveData<List<Movie>>()
|
||||
fun getMoviesLiveData() : LiveData<List<Movie>> = _moviesLiveData
|
||||
|
||||
private val _popularMovies = MutableLiveData<List<MovieDetails>>()
|
||||
val popularMovies : LiveData<List<MovieDetails>> = _popularMovies
|
||||
private var currentFilter = ""
|
||||
val moviesAdapter = MovieAdapter()
|
||||
val scrollListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
val layoutManager = recyclerView.layoutManager as GridLayoutManager
|
||||
val lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition()
|
||||
val totalItemCount = layoutManager.itemCount
|
||||
|
||||
init {
|
||||
//loadData()
|
||||
if(lastVisibleItemPosition == totalItemCount -1) {
|
||||
|
||||
++currentPage
|
||||
getMoreData(currentPage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var currentPage = 1
|
||||
|
||||
|
||||
fun getDataFilter(filter : String) {
|
||||
|
||||
//_moviesLiveData.value = mutableListOf()
|
||||
currentFilter = filter
|
||||
currentPage = 1
|
||||
|
||||
when(currentFilter) {
|
||||
"Populaires" -> viewModelScope.launch {
|
||||
_moviesLiveData.postValue(repository.getPopularMovies())
|
||||
}
|
||||
"Du moment" -> viewModelScope.launch {
|
||||
_moviesLiveData.postValue(repository.getNowPlayingMovies())
|
||||
}
|
||||
"À venir" -> viewModelScope.launch {
|
||||
_moviesLiveData.postValue(repository.getUpcomingMovies())
|
||||
}
|
||||
"Les mieux notés" -> viewModelScope.launch {
|
||||
_moviesLiveData.postValue(repository.getTopRatedMovies())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun getMoreData(page : Int = 1) {
|
||||
var movies : List<Movie>
|
||||
when(currentFilter) {
|
||||
"Populaires" -> viewModelScope.launch {
|
||||
movies = _moviesLiveData.value?.plus(repository.getPopularMovies(page)) ?: listOf()
|
||||
_moviesLiveData.postValue(movies)
|
||||
}
|
||||
"Du moment" -> viewModelScope.launch {
|
||||
movies = _moviesLiveData.value?.plus(repository.getNowPlayingMovies(page)) ?: listOf()
|
||||
_moviesLiveData.postValue(movies)
|
||||
}
|
||||
"À venir" -> viewModelScope.launch {
|
||||
movies = _moviesLiveData.value?.plus(repository.getUpcomingMovies(page)) ?: listOf()
|
||||
_moviesLiveData.postValue(movies)
|
||||
}
|
||||
"Les mieux notés" -> viewModelScope.launch {
|
||||
movies = _moviesLiveData.value?.plus(repository.getTopRatedMovies(page)) ?: listOf()
|
||||
_moviesLiveData.postValue(movies)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class MoviesVMFactory(
|
||||
private val repository: MovieRepository
|
||||
) : ViewModelProvider.Factory {
|
||||
class MoviesVMFactory : ViewModelProvider.Factory {
|
||||
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return MoviesVM(repository) as T
|
||||
return MoviesVM() as T
|
||||
}
|
||||
|
||||
}
|
@ -1,26 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/category_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginLeft="@dimen/default_margin"
|
||||
android:layout_marginStart="@dimen/default_margin"
|
||||
android:layout_marginEnd="@dimen/default_margin"
|
||||
android:layout_marginTop="@dimen/default_margin"
|
||||
android:layout_marginBottom="@dimen/default_margin"
|
||||
android:background="@color/light_grey"
|
||||
android:layout_marginRight="@dimen/default_margin" />
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/movies_item_recycler_view"
|
||||
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
<import type="android.view.View" />
|
||||
|
||||
<variable
|
||||
name="moviesVM"
|
||||
type="fr.iut.pm.movieapplication.ui.viewmodel.MoviesVM" />
|
||||
</data>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="@dimen/default_margin"
|
||||
/>
|
||||
android:orientation="vertical">
|
||||
|
||||
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/category_spinner"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginLeft="@dimen/default_margin"
|
||||
android:layout_marginStart="@dimen/default_margin"
|
||||
android:layout_marginEnd="@dimen/default_margin"
|
||||
android:layout_marginTop="@dimen/default_margin"
|
||||
android:layout_marginBottom="@dimen/default_margin"
|
||||
android:background="@color/light_grey"
|
||||
android:layout_marginRight="@dimen/default_margin" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/movie_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/movies_item_recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="@dimen/default_margin"
|
||||
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
|
||||
app:spanCount="3"
|
||||
android:adapter="@{moviesVM.moviesAdapter}"
|
||||
app:onScrollListener = "@{moviesVM.scrollListener}"
|
||||
tools:listitem="@layout/item_movie_category"
|
||||
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</layout>
|
@ -0,0 +1,67 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools" >
|
||||
|
||||
<data>
|
||||
|
||||
<variable
|
||||
name="movie"
|
||||
type="fr.iut.pm.movieapplication.model.media.movie.Movie" />
|
||||
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_marginLeft="4dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="4dp"
|
||||
android:layout_marginRight="4dp"
|
||||
android:layout_marginBottom="8dp"
|
||||
app:cardCornerRadius="10dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/item_image"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="180dp"
|
||||
android:background="@color/black"
|
||||
android:scaleType="centerCrop"
|
||||
/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:layout_gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:text="@{movie.title}"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/item_date"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:layout_gravity="center"
|
||||
android:textAlignment="center"
|
||||
android:text="@{movie.releaseDate}"/>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
Loading…
Reference in new issue