Skip to content

Commit

Permalink
Merge pull request #983 from UweTrottmann/movie-list-paging
Browse files Browse the repository at this point in the history
Use paging for remaining movie lists
  • Loading branch information
UweTrottmann authored Mar 14, 2024
2 parents f563a95 + e29a48c commit 0fe4726
Show file tree
Hide file tree
Showing 21 changed files with 186 additions and 359 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2020, 2022-2024 Uwe Trottmann

package com.battlelancer.seriesguide.movies

Expand All @@ -14,7 +14,7 @@ import com.battlelancer.seriesguide.movies.tools.MovieTools
import com.battlelancer.seriesguide.provider.SgRoomDatabase
import com.battlelancer.seriesguide.util.Utils

internal open class MovieClickListenerImpl(val context: Context) : MovieClickListener {
open class MovieClickListenerImpl(val context: Context) : MovieClickListener {

override fun onClickMovie(movieTmdbId: Int, posterView: ImageView) {
if (movieTmdbId == -1) return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2019-2024 Uwe Trottmann

package com.battlelancer.seriesguide.movies

Expand All @@ -9,15 +9,15 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import com.battlelancer.seriesguide.R
import com.battlelancer.seriesguide.databinding.ItemDiscoverMovieBinding
import com.battlelancer.seriesguide.databinding.ItemMovieBinding
import com.battlelancer.seriesguide.movies.database.SgMovie
import com.battlelancer.seriesguide.util.ImageTools
import com.squareup.picasso.Picasso
import com.uwetrottmann.tmdb2.entities.BaseMovie
import java.text.DateFormat

class MovieViewHolder(
val binding: ItemDiscoverMovieBinding,
val binding: ItemMovieBinding,
itemClickListener: MovieClickListener?
) : RecyclerView.ViewHolder(binding.root) {

Expand Down Expand Up @@ -107,7 +107,7 @@ class MovieViewHolder(
@JvmStatic
fun inflate(parent: ViewGroup, itemClickListener: MovieClickListener?): MovieViewHolder {
return MovieViewHolder(
ItemDiscoverMovieBinding.inflate(
ItemMovieBinding.inflate(
LayoutInflater.from(parent.context), parent, false
),
itemClickListener
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Copyright 2013-2023 Uwe Trottmann
// Copyright 2017 Christophe Beyls
// SPDX-License-Identifier: Apache-2.0
// Copyright 2013-2024 Uwe Trottmann
// Copyright 2017 Christophe Beyls

package com.battlelancer.seriesguide.movies

Expand Down Expand Up @@ -172,11 +172,8 @@ open class MoviesActivityImpl : BaseTopActivity() {
}

companion object {
// const val SEARCH_LOADER_ID = 100
const val NOW_TRAKT_USER_LOADER_ID = 101
const val NOW_TRAKT_FRIENDS_LOADER_ID = 102
const val WATCHLIST_LOADER_ID = 103
const val COLLECTION_LOADER_ID = 104

const val TAB_POSITION_DISCOVER = 0
const val TAB_POSITION_WATCHLIST_DEFAULT = 1
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2019-2024 Uwe Trottmann

package com.battlelancer.seriesguide.movies

Expand All @@ -12,7 +12,10 @@ import com.battlelancer.seriesguide.movies.tools.MovieTools
import com.battlelancer.seriesguide.settings.TmdbSettings
import java.text.DateFormat

internal class MoviesWatchedAdapter(
/**
* Binds pages of [SgMovie] to [MovieViewHolder].
*/
class MoviesAdapter(
context: Context,
val itemClickListener: MovieClickListener
) : PagingDataAdapter<SgMovie, MovieViewHolder>(DIFF_CALLBACK) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,94 +1,105 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2019-2024 Uwe Trottmann

package com.battlelancer.seriesguide.movies

import android.database.Cursor
import android.os.Bundle
import android.view.View
import android.widget.GridView
import android.widget.TextView
import androidx.annotation.StringRes
import androidx.core.view.ViewCompat
import androidx.core.view.isGone
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
import androidx.loader.app.LoaderManager
import androidx.loader.content.Loader
import com.battlelancer.seriesguide.movies.MoviesDistillationSettings.MoviesSortOrderChangedEvent
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.RecyclerView
import com.battlelancer.seriesguide.R
import com.battlelancer.seriesguide.ui.AutoGridLayoutManager
import com.battlelancer.seriesguide.ui.MoviesActivity
import com.battlelancer.seriesguide.ui.widgets.SgFastScroller
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate
import kotlinx.coroutines.launch
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode

/**
* A shell for a fragment displaying a number of movies.
* Base class for a fragment displaying movies.
*/
abstract class MoviesBaseFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
abstract class MoviesBaseFragment : Fragment() {

private lateinit var adapter: MoviesCursorAdapter
private lateinit var adapter: MoviesAdapter

/**
* Return a loader id different from any other used within [com.battlelancer.seriesguide.ui.MoviesActivity].
*/
abstract val loaderId: Int
abstract val model: MoviesWatchedViewModel

@get:StringRes
abstract val emptyViewTextResId: Int

abstract val gridView: GridView
abstract val recyclerView: RecyclerView

abstract val emptyView: TextView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// note: fragment is in static view pager tab so will never be destroyed if swiped away
EventBus.getDefault().register(this)
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// enable app bar scrolling out of view
ViewCompat.setNestedScrollingEnabled(gridView, true)

emptyView.setText(emptyViewTextResId)
gridView.emptyView = emptyView

ViewModelProvider(requireActivity()).get(MoviesActivityViewModel::class.java)
adapter = MoviesAdapter(requireContext(), MovieClickListenerImpl(requireContext()))

recyclerView.also {
it.setHasFixedSize(true)
it.layoutManager =
AutoGridLayoutManager(
context, R.dimen.movie_grid_columnWidth, 1, 3
)
it.adapter = adapter
SgFastScroller(requireContext(), it)
}

requireActivity().addMenuProvider(
MoviesOptionsMenu(requireActivity()),
viewLifecycleOwner,
Lifecycle.State.RESUMED
)

ViewModelProvider(requireActivity())[MoviesActivityViewModel::class.java]
.scrollTabToTopLiveData
.observe(viewLifecycleOwner) {
if (it != null) {
if (it.tabPosition == getTabPosition(it.isShowingNowTab)) {
gridView.smoothScrollToPosition(0)
recyclerView.scrollToPosition(0)
}
}
}

adapter = MoviesCursorAdapter(
context, MovieClickListenerImpl(requireContext()),
loaderId
)
gridView.adapter = adapter

requireActivity().addMenuProvider(
MoviesOptionsMenu(requireActivity()),
viewLifecycleOwner,
Lifecycle.State.RESUMED
)
viewLifecycleOwner.lifecycleScope.launch {
adapter.onPagesUpdatedFlow.conflate().collectLatest {
emptyView.isGone = adapter.itemCount > 0
}
}

LoaderManager.getInstance(this).initLoader(loaderId, null, this)
viewLifecycleOwner.lifecycleScope.launch {
model.items.collectLatest {
adapter.submitData(it)
}
}
}

override fun onDestroy() {
super.onDestroy()

EventBus.getDefault().unregister(this)
}

@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(@Suppress("UNUSED_PARAMETER") event: MoviesSortOrderChangedEvent) {
LoaderManager.getInstance(this).restartLoader(loaderId, null, this)
fun onEventMainThread(@Suppress("UNUSED_PARAMETER") event: MoviesDistillationSettings.MoviesSortOrderChangedEvent) {
model.updateQueryString()
}

/**
Expand All @@ -97,12 +108,4 @@ abstract class MoviesBaseFragment : Fragment(), LoaderManager.LoaderCallbacks<Cu
*/
internal abstract fun getTabPosition(showingNowTab: Boolean): Int

override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
adapter.swapCursor(data)
}

override fun onLoaderReset(loader: Loader<Cursor>) {
adapter.swapCursor(null)
}

}
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2019-2020, 2022-2024 Uwe Trottmann

package com.battlelancer.seriesguide.movies

import android.database.Cursor
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.GridView
import android.widget.TextView
import androidx.loader.content.CursorLoader
import androidx.loader.content.Loader
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.RecyclerView
import com.battlelancer.seriesguide.R
import com.battlelancer.seriesguide.databinding.FragmentMoviesCollectionBinding
import com.battlelancer.seriesguide.provider.SeriesGuideContract.Movies

/**
* Displays the users collection of movies.
*/
class MoviesCollectionFragment : MoviesBaseFragment() {

override val loaderId: Int = MoviesActivityImpl.COLLECTION_LOADER_ID

override val emptyViewTextResId = R.string.movies_collection_empty

override val gridView: GridView
get() = binding!!.gridViewMoviesCollection
private val _model by viewModels<MoviesCollectionViewModel>()

override val model: MoviesWatchedViewModel
get() = _model

override val recyclerView: RecyclerView
get() = binding!!.recyclerViewMoviesCollection

override val emptyView: TextView
get() = binding!!.textViewMoviesCollectionEmpty
Expand All @@ -42,14 +42,6 @@ class MoviesCollectionFragment : MoviesBaseFragment() {
.root
}

override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
return CursorLoader(
requireContext(), Movies.CONTENT_URI,
MoviesCursorAdapter.MoviesQuery.PROJECTION, Movies.SELECTION_COLLECTION, null,
MoviesDistillationSettings.getSortQuery(context)
)
}

override fun getTabPosition(showingNowTab: Boolean): Int {
return if (showingNowTab) {
MoviesActivityImpl.TAB_POSITION_COLLECTION_WITH_NOW
Expand All @@ -58,7 +50,12 @@ class MoviesCollectionFragment : MoviesBaseFragment() {
}
}

override fun onDestroyView() {
super.onDestroyView()
binding = null
}

companion object {
val liftOnScrollTargetViewId = R.id.gridViewMoviesCollection
val liftOnScrollTargetViewId = R.id.recyclerViewMoviesCollection
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: Apache-2.0
// Copyright 2024 Uwe Trottmann

package com.battlelancer.seriesguide.movies

import android.app.Application
import com.battlelancer.seriesguide.provider.SeriesGuideContract

class MoviesCollectionViewModel(application: Application) : MoviesWatchedViewModel(application) {

override val selection: String
get() = SeriesGuideContract.Movies.SELECTION_COLLECTION

}
Loading

0 comments on commit 0fe4726

Please sign in to comment.