From adf8a48251d7926b47b670c2c8f9981ec93227e7 Mon Sep 17 00:00:00 2001 From: Jannik Date: Thu, 15 Oct 2020 13:00:44 +0200 Subject: [PATCH] replace GridView in library and search fragment with RecyclerView closes #8 --- .../java/org/mosad/teapod/MainActivity.kt | 7 ++- .../teapod/ui/library/LibraryFragment.kt | 29 +++++----- .../mosad/teapod/ui/search/SearchFragment.kt | 25 +++++---- .../mosad/teapod/util/MediaItemDecoration.kt | 16 ++++++ .../teapod/util/adapter/MediaItemAdapter.kt | 55 ++++++++++--------- app/src/main/res/layout/fragment_library.xml | 15 +++-- app/src/main/res/layout/fragment_search.xml | 15 ++--- app/src/main/res/layout/item_media.xml | 5 +- 8 files changed, 94 insertions(+), 73 deletions(-) create mode 100644 app/src/main/java/org/mosad/teapod/util/MediaItemDecoration.kt diff --git a/app/src/main/java/org/mosad/teapod/MainActivity.kt b/app/src/main/java/org/mosad/teapod/MainActivity.kt index d14c577..de2747d 100644 --- a/app/src/main/java/org/mosad/teapod/MainActivity.kt +++ b/app/src/main/java/org/mosad/teapod/MainActivity.kt @@ -103,12 +103,13 @@ class MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemS private fun load() { EncryptedPreferences.readCredentials(this) - // make sure credentials are set and valid + // make sure credentials are set if (EncryptedPreferences.password.isEmpty()) { showLoginDialog(true) - } else if (!AoDParser().login()) { - showLoginDialog(false) } + + // TODO save last loginSuccess, if false show login dialog even if credentials are present + AoDParser().listAnimes() } /** diff --git a/app/src/main/java/org/mosad/teapod/ui/library/LibraryFragment.kt b/app/src/main/java/org/mosad/teapod/ui/library/LibraryFragment.kt index bd2f0b8..1b3c185 100644 --- a/app/src/main/java/org/mosad/teapod/ui/library/LibraryFragment.kt +++ b/app/src/main/java/org/mosad/teapod/ui/library/LibraryFragment.kt @@ -5,17 +5,19 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.GridLayoutManager import kotlinx.android.synthetic.main.fragment_library.* import kotlinx.coroutines.* import org.mosad.teapod.MainActivity import org.mosad.teapod.R import org.mosad.teapod.parser.AoDParser +import org.mosad.teapod.util.MediaItemDecoration import org.mosad.teapod.util.adapter.MediaItemAdapter -import org.mosad.teapod.util.Media class LibraryFragment : Fragment() { - private lateinit var adapter : MediaItemAdapter + private lateinit var adapter: MediaItemAdapter + private lateinit var layoutManager: GridLayoutManager override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_library, container, false) @@ -24,6 +26,7 @@ class LibraryFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + // init async GlobalScope.launch { if (AoDParser.mediaList.isEmpty()) { AoDParser().listAnimes() @@ -32,22 +35,18 @@ class LibraryFragment : Fragment() { // create and set the adapter, needs context withContext(Dispatchers.Main) { context?.let { - adapter = MediaItemAdapter(it, AoDParser.mediaList) - grid_media_library.adapter = adapter + layoutManager = GridLayoutManager(context, 2) + adapter = MediaItemAdapter(AoDParser.mediaList) + adapter.onItemClick = { media, _ -> + (activity as MainActivity).showMediaFragment(media) + } + + recycler_media_library.layoutManager = layoutManager + recycler_media_library.adapter = adapter + recycler_media_library.addItemDecoration(MediaItemDecoration(9)) } } - } - initActions() - } - - private fun initActions() { - grid_media_library.setOnItemClickListener { _, _, position, _ -> - val media = adapter.getItem(position) as Media - println("selected item is: ${media.title}") - - (activity as MainActivity).showMediaFragment(media) } } - } \ No newline at end of file diff --git a/app/src/main/java/org/mosad/teapod/ui/search/SearchFragment.kt b/app/src/main/java/org/mosad/teapod/ui/search/SearchFragment.kt index 5e32581..7757e2f 100644 --- a/app/src/main/java/org/mosad/teapod/ui/search/SearchFragment.kt +++ b/app/src/main/java/org/mosad/teapod/ui/search/SearchFragment.kt @@ -6,17 +6,20 @@ import android.view.View import android.view.ViewGroup import android.widget.SearchView import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.GridLayoutManager import kotlinx.android.synthetic.main.fragment_search.* import kotlinx.coroutines.* import org.mosad.teapod.MainActivity import org.mosad.teapod.R import org.mosad.teapod.parser.AoDParser +import org.mosad.teapod.util.MediaItemDecoration import org.mosad.teapod.util.adapter.MediaItemAdapter -import org.mosad.teapod.util.Media class SearchFragment : Fragment() { private var adapter : MediaItemAdapter? = null + private lateinit var layoutManager: GridLayoutManager + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { return inflater.inflate(R.layout.fragment_search, container, false) @@ -33,8 +36,15 @@ class SearchFragment : Fragment() { // create and set the adapter, needs context withContext(Dispatchers.Main) { context?.let { - adapter = MediaItemAdapter(it, AoDParser.mediaList) - grid_media_search.adapter = adapter + layoutManager = GridLayoutManager(context, 2) + adapter = MediaItemAdapter(AoDParser.mediaList) + adapter!!.onItemClick = { media, _ -> + (activity as MainActivity).showMediaFragment(media) + } + + recycler_media_search.layoutManager = layoutManager + recycler_media_search.adapter = adapter + recycler_media_search.addItemDecoration(MediaItemDecoration(9)) } } } @@ -56,14 +66,5 @@ class SearchFragment : Fragment() { return false } }) - - grid_media_search.setOnItemClickListener { _, _, position, _ -> - search_text.clearFocus() // remove focus from the SearchView - - val media = adapter?.getItem(position) as Media - println("selected item is: ${media.title}") - - (activity as MainActivity).showMediaFragment(media) - } } } \ No newline at end of file diff --git a/app/src/main/java/org/mosad/teapod/util/MediaItemDecoration.kt b/app/src/main/java/org/mosad/teapod/util/MediaItemDecoration.kt new file mode 100644 index 0000000..0643a62 --- /dev/null +++ b/app/src/main/java/org/mosad/teapod/util/MediaItemDecoration.kt @@ -0,0 +1,16 @@ +package org.mosad.teapod.util + +import android.graphics.Rect +import android.view.View +import androidx.recyclerview.widget.RecyclerView + +class MediaItemDecoration(private val spacing: Int) : RecyclerView.ItemDecoration() { + + override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { + outRect.left = spacing + outRect.right = spacing + outRect.bottom = spacing + outRect.top = spacing + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/mosad/teapod/util/adapter/MediaItemAdapter.kt b/app/src/main/java/org/mosad/teapod/util/adapter/MediaItemAdapter.kt index 00753e1..215225a 100644 --- a/app/src/main/java/org/mosad/teapod/util/adapter/MediaItemAdapter.kt +++ b/app/src/main/java/org/mosad/teapod/util/adapter/MediaItemAdapter.kt @@ -1,58 +1,62 @@ package org.mosad.teapod.util.adapter -import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.* +import android.widget.Filter +import android.widget.Filterable +import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.Glide +import kotlinx.android.synthetic.main.item_media.view.* import org.mosad.teapod.R import org.mosad.teapod.util.Media import java.util.* +import kotlin.collections.ArrayList -class MediaItemAdapter(val context: Context, private val originalMedia: ArrayList) : BaseAdapter(), Filterable { +class MediaItemAdapter(private val media: ArrayList) : RecyclerView.Adapter(), Filterable { - private var filteredMedia = originalMedia.map { it.copy() } - private val filer = MediaFilter() + var onItemClick: ((Media, Int) -> Unit)? = null + private val filter = MediaFilter() + private var filteredMedia = media.map { it.copy() } - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.item_media, parent, false) + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MediaItemAdapter.ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.item_media, parent, false) - val textTitle = view.findViewById(R.id.text_title) - val imagePoster = view.findViewById(R.id.image_poster) - - textTitle.text = filteredMedia[position].title - Glide.with(context).load(filteredMedia[position].info.posterLink).into(imagePoster) - - return view + return ViewHolder(view) } - override fun getFilter(): Filter { - return filer + override fun onBindViewHolder(holder: MediaItemAdapter.ViewHolder, position: Int) { + holder.view.apply { + text_title.text = filteredMedia[position].title + Glide.with(context).load(filteredMedia[position].info.posterLink).into(image_poster) + } } - override fun getCount(): Int { + override fun getItemCount(): Int { return filteredMedia.size } - override fun getItem(position: Int): Any { - return filteredMedia[position] + override fun getFilter(): Filter { + return filter } - override fun getItemId(position: Int): Long { - return position.toLong() + inner class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) { + init { + view.setOnClickListener { + onItemClick?.invoke(filteredMedia[adapterPosition], adapterPosition) + } + } } - inner class MediaFilter : Filter() { override fun performFiltering(constraint: CharSequence?): FilterResults { val filterTerm = constraint.toString().toLowerCase(Locale.ROOT) val results = FilterResults() val filteredList = if (filterTerm.isEmpty()) { - originalMedia + media } else { - originalMedia.filter { + media.filter { it.title.toLowerCase(Locale.ROOT).contains(filterTerm) } } @@ -68,10 +72,9 @@ class MediaItemAdapter(val context: Context, private val originalMedia: ArrayLis * suppressing unchecked cast is safe, since we only use Media */ override fun publishResults(constraint: CharSequence?, results: FilterResults?) { - filteredMedia = results?.values as ArrayList + filteredMedia = results?.values as java.util.ArrayList notifyDataSetChanged() } - } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_library.xml b/app/src/main/res/layout/fragment_library.xml index 561a05e..55fc031 100644 --- a/app/src/main/res/layout/fragment_library.xml +++ b/app/src/main/res/layout/fragment_library.xml @@ -7,16 +7,15 @@ android:background="#f5f5f5" tools:context=".ui.library.LibraryFragment"> - + android:padding="3dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_search.xml b/app/src/main/res/layout/fragment_search.xml index 1e64366..bd6944b 100644 --- a/app/src/main/res/layout/fragment_search.xml +++ b/app/src/main/res/layout/fragment_search.xml @@ -20,16 +20,17 @@ - + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/search_text"> + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_media.xml b/app/src/main/res/layout/item_media.xml index 2667843..4d4c08d 100644 --- a/app/src/main/res/layout/item_media.xml +++ b/app/src/main/res/layout/item_media.xml @@ -3,7 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent" + android:layout_height="wrap_content" + android:backgroundTint="#FFFFFF" app:cardCornerRadius="7dp" app:cardElevation="4dp"> @@ -18,7 +19,7 @@ android:layout_height="112.5dp" android:contentDescription="@string/media_poster_desc" android:maxWidth="200dp" - android:scaleType="fitXY" + android:scaleType="centerCrop" app:srcCompat="@color/md_disabled_text_dark_theme" />