From cea8d0062c7c395e64b2362e09919c9b6de58c3e Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sat, 8 Apr 2023 07:07:48 +0530 Subject: [PATCH] Free fragment view bindings when their fragments are destroyed. --- .../ui/fragments/AudioPlayerFragment.kt | 14 +++++--- .../libretube/ui/fragments/ChannelFragment.kt | 25 +++++++------ .../ui/fragments/CommentsMainFragment.kt | 20 +++++++---- .../ui/fragments/CommentsRepliesFragment.kt | 28 +++++++++------ .../ui/fragments/DownloadsFragment.kt | 11 ++++-- .../libretube/ui/fragments/HomeFragment.kt | 14 ++++++-- .../libretube/ui/fragments/LibraryFragment.kt | 11 ++++-- .../ui/fragments/PlaylistFragment.kt | 35 +++++++++++-------- .../libretube/ui/fragments/SearchFragment.kt | 2 +- .../ui/fragments/SubscriptionsFragment.kt | 29 +++++++++------ .../libretube/ui/fragments/TrendsFragment.kt | 10 ++++-- .../ui/fragments/WatchHistoryFragment.kt | 14 ++++++-- 12 files changed, 143 insertions(+), 70 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt index 09ab9819c..e92f8d0e1 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/AudioPlayerFragment.kt @@ -41,7 +41,9 @@ import com.github.libretube.util.PlayingQueue import kotlin.math.abs class AudioPlayerFragment : Fragment(), AudioPlayerOptions { - private lateinit var binding: FragmentAudioPlayerBinding + private var _binding: FragmentAudioPlayerBinding? = null + private val binding get() = _binding!! + private lateinit var audioHelper: AudioHelper private val mainActivity get() = context as MainActivity private val viewModel: PlayerViewModel by activityViewModels() @@ -84,7 +86,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentAudioPlayerBinding.inflate(layoutInflater) + _binding = FragmentAudioPlayerBinding.inflate(inflater) return binding.root } @@ -183,8 +185,6 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions { mainActivity.supportFragmentManager.commit { remove(this@AudioPlayerFragment) } - - onDestroy() } @SuppressLint("ClickableViewAccessibility") @@ -277,6 +277,7 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions { * Update the position, duration and text views belonging to the seek bar */ private fun updateSeekBar() { + val binding = _binding ?: return val duration = playerService?.getDuration()?.takeIf { it > 0 } ?: let { // if there's no duration available, clear everything binding.timeBar.value = 0f @@ -313,6 +314,11 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions { initializeSeekBar() } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + override fun onDestroy() { // unregister all listeners and the connected [playerService] playerService?.onIsPlayingChanged = null diff --git a/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt index 2aff76887..2252540fd 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt @@ -34,7 +34,8 @@ import kotlinx.coroutines.withContext import retrofit2.HttpException class ChannelFragment : Fragment() { - private lateinit var binding: FragmentChannelBinding + private var _binding: FragmentChannelBinding? = null + private val binding get() = _binding!! private var channelId: String? = null private var channelName: String? = null @@ -67,7 +68,7 @@ class ChannelFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentChannelBinding.inflate(layoutInflater, container, false) + _binding = FragmentChannelBinding.inflate(inflater, container, false) return binding.root } @@ -87,16 +88,20 @@ class ChannelFragment : Fragment() { refreshChannel() } - binding.channelScrollView.viewTreeObserver - .addOnScrollChangedListener { - if (!binding.channelScrollView.canScrollVertically(1)) { - try { - onScrollEnd.invoke() - } catch (e: Exception) { - Log.e("tabs failed", e.toString()) - } + binding.channelScrollView.viewTreeObserver.addOnScrollChangedListener { + if (_binding?.channelScrollView?.canScrollVertically(1) == false) { + try { + onScrollEnd() + } catch (e: Exception) { + Log.e("tabs failed", e.toString()) } } + } + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null } private fun fetchChannel() { diff --git a/app/src/main/java/com/github/libretube/ui/fragments/CommentsMainFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/CommentsMainFragment.kt index 5804138a2..dab6f46a5 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/CommentsMainFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/CommentsMainFragment.kt @@ -13,7 +13,9 @@ import com.github.libretube.ui.adapters.CommentsAdapter import com.github.libretube.ui.models.CommentsViewModel class CommentsMainFragment : Fragment() { - private lateinit var binding: FragmentCommentsBinding + private var _binding: FragmentCommentsBinding? = null + private val binding get() = _binding!! + private lateinit var commentsAdapter: CommentsAdapter private val viewModel: CommentsViewModel by activityViewModels() @@ -23,7 +25,7 @@ class CommentsMainFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentCommentsBinding.inflate(inflater, container, false) + _binding = FragmentCommentsBinding.inflate(inflater, container, false) return binding.root } @@ -33,12 +35,11 @@ class CommentsMainFragment : Fragment() { binding.commentsRV.layoutManager = LinearLayoutManager(requireContext()) binding.commentsRV.setItemViewCacheSize(20) - binding.commentsRV.viewTreeObserver - .addOnScrollChangedListener { - if (!binding.commentsRV.canScrollVertically(1)) { - viewModel.fetchNextComments() - } + binding.commentsRV.viewTreeObserver.addOnScrollChangedListener { + if (_binding?.commentsRV?.canScrollVertically(1) == false) { + viewModel.fetchNextComments() } + } commentsAdapter = CommentsAdapter( this, @@ -74,4 +75,9 @@ class CommentsMainFragment : Fragment() { ) } } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/CommentsRepliesFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/CommentsRepliesFragment.kt index 4d7ab8de6..c75f988f0 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/CommentsRepliesFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/CommentsRepliesFragment.kt @@ -25,7 +25,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class CommentsRepliesFragment : Fragment() { - private lateinit var binding: FragmentCommentsBinding + private var _binding: FragmentCommentsBinding? = null + private val binding get() = _binding!! + private lateinit var repliesPage: CommentsPage private lateinit var repliesAdapter: CommentsAdapter private val viewModel: CommentsViewModel by activityViewModels() @@ -37,7 +39,7 @@ class CommentsRepliesFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentCommentsBinding.inflate(inflater, container, false) + _binding = FragmentCommentsBinding.inflate(inflater, container, false) return binding.root } @@ -63,21 +65,25 @@ class CommentsRepliesFragment : Fragment() { binding.commentsRV.layoutManager = LinearLayoutManager(context) binding.commentsRV.adapter = repliesAdapter - binding.commentsRV.viewTreeObserver - .addOnScrollChangedListener { - if (!binding.commentsRV.canScrollVertically(1) && - ::repliesPage.isInitialized && - repliesPage.nextpage != null - ) { - fetchReplies(videoId, repliesPage.nextpage!!) { - repliesAdapter.updateItems(repliesPage.comments) - } + binding.commentsRV.viewTreeObserver.addOnScrollChangedListener { + if (_binding?.commentsRV?.canScrollVertically(1) == false && + ::repliesPage.isInitialized && + repliesPage.nextpage != null + ) { + fetchReplies(videoId, repliesPage.nextpage!!) { + repliesAdapter.updateItems(repliesPage.comments) } } + } loadInitialReplies(videoId, comment.repliesPage ?: "", repliesAdapter) } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + private fun loadInitialReplies( videoId: String, nextPage: String, diff --git a/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt index 61d133fe8..6b8a2e6bf 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/DownloadsFragment.kt @@ -32,7 +32,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking class DownloadsFragment : Fragment() { - private lateinit var binding: FragmentDownloadsBinding + private var _binding: FragmentDownloadsBinding? = null + private val binding get() = _binding!! + private var binder: DownloadService.LocalBinder? = null private val downloads = mutableListOf() private val downloadReceiver = DownloadReceiver() @@ -63,7 +65,7 @@ class DownloadsFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentDownloadsBinding.inflate(layoutInflater) + _binding = FragmentDownloadsBinding.inflate(inflater) return binding.root } @@ -185,4 +187,9 @@ class DownloadsFragment : Fragment() { context?.unbindService(serviceConnection) } } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt index 017a681b8..2326dfd48 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/HomeFragment.kt @@ -28,7 +28,9 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext class HomeFragment : Fragment() { - private lateinit var binding: FragmentHomeBinding + private var _binding: FragmentHomeBinding? = null + private val binding get() = _binding!! + private val subscriptionsViewModel: SubscriptionsViewModel by activityViewModels() override fun onCreateView( @@ -36,7 +38,7 @@ class HomeFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentHomeBinding.inflate(layoutInflater, container, false) + _binding = FragmentHomeBinding.inflate(inflater, container, false) return binding.root } @@ -67,6 +69,11 @@ class HomeFragment : Fragment() { fetchHomeFeed() } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + private fun fetchHomeFeed() { lifecycleScope.launchWhenCreated { loadTrending() @@ -85,6 +92,7 @@ class HomeFragment : Fragment() { RetrofitInstance.api.getTrending(region).take(10) } }.getOrNull()?.takeIf { it.isNotEmpty() } ?: return + val binding = _binding ?: return makeVisible(binding.trendingRV, binding.trendingTV) binding.trendingRV.layoutManager = GridLayoutManager(context, 2) @@ -106,6 +114,7 @@ class HomeFragment : Fragment() { } }.getOrNull()?.takeIf { it.isNotEmpty() } ?: return }.take(20) + val binding = _binding ?: return makeVisible(binding.featuredRV, binding.featuredTV) binding.featuredRV.layoutManager = LinearLayoutManager( @@ -165,6 +174,7 @@ class HomeFragment : Fragment() { views.forEach { it.visibility = View.VISIBLE } + val binding = _binding ?: return binding.progress.visibility = View.GONE binding.scroll.visibility = View.VISIBLE binding.refresh.isRefreshing = false diff --git a/app/src/main/java/com/github/libretube/ui/fragments/LibraryFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/LibraryFragment.kt index 058d69f78..bcb0f35ce 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/LibraryFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/LibraryFragment.kt @@ -33,7 +33,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext class LibraryFragment : Fragment() { - private lateinit var binding: FragmentLibraryBinding + private var _binding: FragmentLibraryBinding? = null + private val binding get() = _binding!! + private val playerViewModel: PlayerViewModel by activityViewModels() override fun onCreateView( @@ -41,7 +43,7 @@ class LibraryFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentLibraryBinding.inflate(layoutInflater, container, false) + _binding = FragmentLibraryBinding.inflate(inflater, container, false) return binding.root } @@ -92,6 +94,11 @@ class LibraryFragment : Fragment() { } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + private fun initBookmarks() { lifecycleScope.launch { val bookmarks = withContext(Dispatchers.IO) { diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt index f9035b496..909ee0a78 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt @@ -39,7 +39,8 @@ import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext class PlaylistFragment : Fragment() { - private lateinit var binding: FragmentPlaylistBinding + private var _binding: FragmentPlaylistBinding? = null + private val binding get() = _binding!! // general playlist information private var playlistId: String? = null @@ -69,7 +70,7 @@ class PlaylistFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentPlaylistBinding.inflate(layoutInflater, container, false) + _binding = FragmentPlaylistBinding.inflate(inflater, container, false) return binding.root } @@ -95,6 +96,11 @@ class PlaylistFragment : Fragment() { fetchPlaylist() } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + private fun updateBookmarkRes() { binding.bookmark.setIconResource( if (isBookmarked) R.drawable.ic_bookmark else R.drawable.ic_bookmark_outlined @@ -211,21 +217,20 @@ class PlaylistFragment : Fragment() { }) binding.playlistRecView.adapter = playlistAdapter - binding.playlistScrollview.viewTreeObserver - .addOnScrollChangedListener { - if (!binding.playlistScrollview.canScrollVertically(1)) { - if (isLoading) return@addOnScrollChangedListener - - // append more playlists to the recycler view - if (playlistType != PlaylistType.PUBLIC) { - isLoading = true - playlistAdapter?.showMoreItems() - isLoading = false - } else { - fetchNextPage() - } + binding.playlistScrollview.viewTreeObserver.addOnScrollChangedListener { + if (_binding?.playlistScrollview?.canScrollVertically(1) == false && + !isLoading + ) { + // append more playlists to the recycler view + if (playlistType != PlaylistType.PUBLIC) { + isLoading = true + playlistAdapter?.showMoreItems() + isLoading = false + } else { + fetchNextPage() } } + } // listener for swiping to the left or right if (playlistType != PlaylistType.PUBLIC) { diff --git a/app/src/main/java/com/github/libretube/ui/fragments/SearchFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/SearchFragment.kt index 69ad6fa99..e4a86b088 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/SearchFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/SearchFragment.kt @@ -39,7 +39,7 @@ class SearchFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = FragmentSearchBinding.inflate(layoutInflater) + _binding = FragmentSearchBinding.inflate(inflater) return binding.root } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt index 48b29fb46..af9e7486a 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt @@ -34,7 +34,9 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking class SubscriptionsFragment : Fragment() { - private lateinit var binding: FragmentSubscriptionsBinding + private var _binding: FragmentSubscriptionsBinding? = null + private val binding get() = _binding!! + private val viewModel: SubscriptionsViewModel by activityViewModels() private var channelGroups: List = listOf() private var selectedFilterGroup: Int = 0 @@ -56,7 +58,7 @@ class SubscriptionsFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentSubscriptionsBinding.inflate(layoutInflater, container, false) + _binding = FragmentSubscriptionsBinding.inflate(inflater, container, false) return binding.root } @@ -147,22 +149,27 @@ class SubscriptionsFragment : Fragment() { } } - binding.scrollviewSub.viewTreeObserver - .addOnScrollChangedListener { - if (!binding.scrollviewSub.canScrollVertically(1)) { - // scroll view is at bottom - if (viewModel.videoFeed.value == null) return@addOnScrollChangedListener - binding.subRefresh.isRefreshing = true - subscriptionsAdapter?.updateItems() - binding.subRefresh.isRefreshing = false - } + binding.scrollviewSub.viewTreeObserver.addOnScrollChangedListener { + val binding = _binding + if (binding?.scrollviewSub?.canScrollVertically(1) == false && + viewModel.videoFeed.value != null // scroll view is at bottom + ) { + binding.subRefresh.isRefreshing = true + subscriptionsAdapter?.updateItems() + binding.subRefresh.isRefreshing = false } + } lifecycleScope.launch { initChannelGroups() } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + @SuppressLint("InflateParams") private suspend fun initChannelGroups() { channelGroups = DatabaseHolder.Database.subscriptionGroupsDao().getAll() diff --git a/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt index 88cece414..00e7016c4 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/TrendsFragment.kt @@ -23,14 +23,15 @@ import kotlinx.coroutines.withContext import retrofit2.HttpException class TrendsFragment : Fragment() { - private lateinit var binding: FragmentTrendsBinding + private var _binding: FragmentTrendsBinding? = null + private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentTrendsBinding.inflate(layoutInflater, container, false) + _binding = FragmentTrendsBinding.inflate(inflater, container, false) return binding.root } @@ -44,6 +45,11 @@ class TrendsFragment : Fragment() { } } + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + private fun fetchTrending() { lifecycleScope.launchWhenCreated { val response = try { diff --git a/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt index feebea6ba..10f9dae98 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/WatchHistoryFragment.kt @@ -30,7 +30,8 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking class WatchHistoryFragment : Fragment() { - private lateinit var binding: FragmentWatchHistoryBinding + private var _binding: FragmentWatchHistoryBinding? = null + private val binding get() = _binding!! private val playerViewModel: PlayerViewModel by activityViewModels() private var isLoading = false @@ -40,7 +41,7 @@ class WatchHistoryFragment : Fragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentWatchHistoryBinding.inflate(layoutInflater, container, false) + _binding = FragmentWatchHistoryBinding.inflate(inflater, container, false) return binding.root } @@ -149,7 +150,9 @@ class WatchHistoryFragment : Fragment() { // add a listener for scroll end, delay needed to prevent loading new ones the first time Handler(Looper.getMainLooper()).postDelayed(200) { binding.historyScrollView.viewTreeObserver.addOnScrollChangedListener { - if (!binding.historyScrollView.canScrollVertically(1) && !isLoading) { + if (_binding?.historyScrollView?.canScrollVertically(1) == false && + !isLoading + ) { isLoading = true watchHistoryAdapter.showMoreItems() isLoading = false @@ -157,4 +160,9 @@ class WatchHistoryFragment : Fragment() { } } } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } }