Merge pull request #6863 from Bnyro/master

feat: support for predictive back gestures
This commit is contained in:
Bnyro 2024-12-07 14:02:35 +01:00 committed by GitHub
commit 0760a38ad2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 319 additions and 190 deletions

View File

@ -125,7 +125,7 @@ object NavBarHelper {
return if (pref == Int.MAX_VALUE) {
getNavBarItems(context).firstOrNull { it.isVisible }?.itemId ?: R.id.homeFragment
} else {
defaultNavItems.get(pref).itemId
defaultNavItems[pref].itemId
}
}

View File

@ -11,7 +11,6 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewTreeObserver
import android.widget.ScrollView
import androidx.activity.addCallback
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.annotation.ColorInt
@ -22,7 +21,6 @@ import androidx.core.os.bundleOf
import androidx.core.view.allViews
import androidx.core.view.children
import androidx.core.view.isNotEmpty
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import androidx.lifecycle.lifecycleScope
import androidx.navigation.NavController
@ -38,7 +36,6 @@ import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.ActivityMainBinding
import com.github.libretube.enums.ImportFormat
import com.github.libretube.extensions.anyChildFocused
import com.github.libretube.extensions.toID
import com.github.libretube.helpers.ImportHelper
import com.github.libretube.helpers.IntentHelper
@ -54,17 +51,14 @@ import com.github.libretube.ui.dialogs.ImportTempPlaylistDialog
import com.github.libretube.ui.fragments.AudioPlayerFragment
import com.github.libretube.ui.fragments.DownloadsFragment
import com.github.libretube.ui.fragments.PlayerFragment
import com.github.libretube.ui.models.CommonPlayerViewModel
import com.github.libretube.ui.models.SearchViewModel
import com.github.libretube.ui.models.SubscriptionsViewModel
import com.github.libretube.ui.preferences.BackupRestoreSettings.Companion.FILETYPE_ANY
import com.github.libretube.ui.preferences.BackupRestoreSettings.Companion.JSON
import com.github.libretube.util.UpdateChecker
import com.google.android.material.elevation.SurfaceColors
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import kotlin.math.exp
class MainActivity : BaseActivity() {
lateinit var binding: ActivityMainBinding
@ -74,7 +68,6 @@ class MainActivity : BaseActivity() {
private var startFragmentId = R.id.homeFragment
private val commonPlayerViewModel: CommonPlayerViewModel by viewModels()
private val searchViewModel: SearchViewModel by viewModels()
private val subscriptionsViewModel: SubscriptionsViewModel by viewModels()
@ -185,44 +178,6 @@ class MainActivity : BaseActivity() {
setupSubscriptionsBadge()
onBackPressedDispatcher.addCallback {
if (commonPlayerViewModel.isFullscreen.value == true) {
val fullscreenUnsetSuccess = runOnPlayerFragment {
unsetFullscreen()
true
}
if (fullscreenUnsetSuccess) return@addCallback
}
if (binding.mainMotionLayout.progress == 0F) {
runCatching {
minimizePlayer()
return@addCallback
}
}
when (navController.currentDestination?.id) {
startFragmentId -> {
moveTaskToBack(true)
onUserLeaveHint()
}
R.id.searchFragment -> {
if (searchView.anyChildFocused()) searchView.clearFocus()
else navController.popBackStack()
}
R.id.searchResultFragment -> {
navController.popBackStack(R.id.searchFragment, true) ||
navController.popBackStack()
}
else -> {
navController.popBackStack()
}
}
}
loadIntentData()
}
@ -407,13 +362,8 @@ class MainActivity : BaseActivity() {
}
override fun onMenuItemActionCollapse(item: MenuItem): Boolean {
if (binding.mainMotionLayout.progress == 0F) {
runCatching {
minimizePlayer()
}
}
// Handover back press to `BackPressedDispatcher`
else if (binding.bottomNav.menu.children.none {
if (binding.bottomNav.menu.children.none {
it.itemId == navController.currentDestination?.id
}
) {
@ -591,25 +541,6 @@ class MainActivity : BaseActivity() {
return false
}
private fun minimizePlayer() {
binding.mainMotionLayout.transitionToEnd()
supportFragmentManager.fragments.forEach { fragment ->
(fragment as? PlayerFragment)?.binding?.apply {
mainContainer.isClickable = false
linLayout.isVisible = true
playerMotionLayout.setTransitionDuration(250)
playerMotionLayout.transitionToEnd()
playerMotionLayout.enableTransition(R.id.yt_transition, true)
}
(fragment as? AudioPlayerFragment)?.binding?.apply {
audioPlayerContainer.isClickable = false
playerMotionLayout.transitionToEnd()
}
}
requestOrientationChange()
}
@SuppressLint("SwitchIntDef")
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)

View File

@ -2,67 +2,22 @@ package com.github.libretube.ui.activities
import android.content.Intent
import android.os.Bundle
import androidx.activity.addCallback
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import androidx.fragment.app.replace
import androidx.navigation.findNavController
import com.github.libretube.R
import com.github.libretube.constants.IntentData
import com.github.libretube.databinding.ActivityNointernetBinding
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.NetworkHelper
import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.fragments.AudioPlayerFragment
import com.github.libretube.ui.fragments.DownloadsFragment
import com.google.android.material.snackbar.Snackbar
class NoInternetActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityNointernetBinding.inflate(layoutInflater)
// retry button
binding.retryButton.setOnClickListener {
if (NetworkHelper.isNetworkAvailable(this)) {
NavigationHelper.restartMainActivity(this)
} else {
Snackbar.make(binding.root, R.string.turnInternetOn, Snackbar.LENGTH_LONG).show()
}
}
binding.noInternetSettingsImageView.setOnClickListener {
val intent = Intent(this, SettingsActivity::class.java)
startActivity(intent)
}
binding.downloads.setOnClickListener {
supportFragmentManager.commit {
replace<DownloadsFragment>(R.id.container)
addToBackStack(null)
}
}
setContentView(binding.root)
onBackPressedDispatcher.addCallback(this) {
if (removeFragment<DownloadsFragment>() || removeFragment<AudioPlayerFragment>()) return@addCallback
finishAffinity()
}
}
private inline fun <reified T: Fragment> removeFragment(): Boolean {
val fragment = supportFragmentManager.fragments.filterIsInstance<T>()
.firstOrNull()
if (fragment != null) {
supportFragmentManager.commit {
remove(fragment)
}
return true
}
return false
val navController = findNavController(R.id.container)
navController.graph = navController.navInflater.inflate(R.navigation.nav_nointernet)
}
override fun onNewIntent(intent: Intent) {

View File

@ -1,7 +1,6 @@
package com.github.libretube.ui.activities
import android.os.Bundle
import androidx.activity.addCallback
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import androidx.fragment.app.replace
@ -26,22 +25,17 @@ class SettingsActivity : BaseActivity() {
}
if (savedInstanceState == null) {
redirectTo<MainSettings>()
}
// new way of dealing with back presses instead of onBackPressed()
onBackPressedDispatcher.addCallback(this) {
if (supportFragmentManager.findFragmentById(R.id.settings) is MainSettings) {
finishAndRemoveTask()
} else {
redirectTo<MainSettings>()
changeTopBarText(getString(R.string.settings))
}
goToMainSettings()
}
handleRedirect()
}
fun goToMainSettings() {
redirectTo<MainSettings>()
changeTopBarText(getString(R.string.settings))
}
private fun handleRedirect() {
val redirectKey = intent.extras?.getString(REDIRECT_KEY)

View File

@ -19,6 +19,8 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
abstract val titleResourceId: Int
private val settingsActivity get() = activity as? SettingsActivity
/**
* Whether any preference dialog is currently visible to the user.
*/
@ -26,7 +28,8 @@ abstract class BasePreferenceFragment : PreferenceFragmentCompat() {
override fun onStart() {
super.onStart()
(activity as? SettingsActivity)?.changeTopBarText(getString(titleResourceId))
settingsActivity?.changeTopBarText(getString(titleResourceId))
}
override fun onDisplayPreferenceDialog(preference: Preference) {

View File

@ -0,0 +1,19 @@
package com.github.libretube.ui.extensions
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
fun Fragment.setOnBackPressed(action: OnBackPressedCallback.() -> Unit): OnBackPressedCallback {
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
action()
}
}
requireActivity().onBackPressedDispatcher.addCallback(
viewLifecycleOwner,
callback
)
return callback
}

View File

@ -0,0 +1,70 @@
package com.github.libretube.ui.extensions
import android.view.View
import androidx.activity.BackEventCompat
import androidx.activity.OnBackPressedCallback
import androidx.core.view.animation.PathInterpolatorCompat
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
private val GestureInterpolator = PathInterpolatorCompat.create(0f, 0f, 0f, 1f)
/**
* Set a fragment animation to be displayed when swiping back as well as an [onBackPressed] action
* that is executed when the back press was confirmed and not cancelled by the user
*
* @see <a href="https://github.com/android/animation-samples/blob/main/Motion/app/src/main/java/com/example/android/motion/demo/containertransform/CheeseArticleFragment.kt">Android animation samples</a>
*/
fun Fragment.setupFragmentAnimation(
background: View, onBackPressed: () -> Unit = {
findNavController().popBackStack()
}
) {
val predictiveBackMargin = 50f
var initialTouchY = -1f
val callback = object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
onBackPressed()
}
override fun handleOnBackProgressed(backEvent: BackEventCompat) {
val progress = GestureInterpolator.getInterpolation(backEvent.progress)
if (initialTouchY < 0f) {
initialTouchY = backEvent.touchY
}
val progressY = GestureInterpolator.getInterpolation(
(backEvent.touchY - initialTouchY) / background.height
)
// See the motion spec about the calculations below.
// https://developer.android.com/design/ui/mobile/guides/patterns/predictive-back#motion-specs
// Shift horizontally.
val maxTranslationX = (background.width / 20) - predictiveBackMargin
background.translationX = progress * maxTranslationX *
(if (backEvent.swipeEdge == BackEventCompat.EDGE_LEFT) 1 else -1)
// Shift vertically.
val maxTranslationY = (background.height / 20) - predictiveBackMargin
background.translationY = progressY * maxTranslationY
// Scale down from 100% to 90%.
val scale = 1f - (0.1f * progress)
background.scaleX = scale
background.scaleY = scale
}
override fun handleOnBackCancelled() {
initialTouchY = -1f
background.run {
translationX = 0f
translationY = 0f
scaleX = 1f
scaleY = 1f
}
}
}
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, callback)
}

View File

@ -47,6 +47,7 @@ import com.github.libretube.services.OfflinePlayerService
import com.github.libretube.services.OnlinePlayerService
import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.extensions.setOnBackPressed
import com.github.libretube.ui.interfaces.AudioPlayerOptions
import com.github.libretube.ui.listeners.AudioPlayerThumbnailListener
import com.github.libretube.ui.models.ChaptersViewModel
@ -238,6 +239,17 @@ class AudioPlayerFragment : Fragment(), AudioPlayerOptions {
if (!PlayerHelper.playAutomatically) updatePlayPauseButton()
updateChapterIndex()
val onBackPressedCallback = setOnBackPressed {
binding.audioPlayerContainer.isClickable = false
binding.playerMotionLayout.transitionToEnd()
mainActivity?.binding?.mainMotionLayout?.transitionToEnd()
mainActivity?.requestOrientationChange()
}
viewModel.isMiniPlayerVisible.observe(viewLifecycleOwner) { isMiniPlayerVisible ->
// if the player is minimized, the fragment behind the player should handle the event
onBackPressedCallback.isEnabled = isMiniPlayerVisible != true
}
}
private fun killFragment() {

View File

@ -31,6 +31,7 @@ import com.github.libretube.obj.ShareData
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.dialogs.ShareDialog
import com.github.libretube.ui.extensions.setupFragmentAnimation
import com.github.libretube.ui.extensions.setupSubscriptionButton
import com.github.libretube.ui.sheets.AddChannelToGroupSheet
import com.github.libretube.util.deArrow
@ -103,6 +104,8 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
}
fetchChannel()
setupFragmentAnimation(binding.root)
}
// adjust sensitivity due to the issue of viewpager2 with SwipeToRefresh https://issuetracker.google.com/issues/138314213

View File

@ -34,6 +34,7 @@ import com.github.libretube.extensions.serializable
import com.github.libretube.extensions.setOnDismissListener
import com.github.libretube.helpers.BackgroundHelper
import com.github.libretube.helpers.DownloadHelper
import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.obj.DownloadStatus
@ -41,6 +42,7 @@ import com.github.libretube.receivers.DownloadReceiver
import com.github.libretube.services.DownloadService
import com.github.libretube.ui.adapters.DownloadsAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.extensions.setupFragmentAnimation
import com.github.libretube.ui.sheets.BaseBottomSheet
import com.github.libretube.ui.viewholders.DownloadsViewHolder
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@ -82,6 +84,10 @@ class DownloadsFragment : Fragment() {
else -> throw IllegalArgumentException()
}
}.attach()
if (NavBarHelper.getStartFragmentId(requireContext()) != R.id.downloadsFragment) {
setupFragmentAnimation(binding.root)
}
}
fun bindDownloadService() {

View File

@ -23,12 +23,14 @@ import com.github.libretube.constants.PreferenceKeys.HOME_TAB_CONTENT
import com.github.libretube.databinding.FragmentHomeBinding
import com.github.libretube.db.DatabaseHelper
import com.github.libretube.db.obj.PlaylistBookmark
import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.activities.SettingsActivity
import com.github.libretube.ui.adapters.PlaylistBookmarkAdapter
import com.github.libretube.ui.adapters.PlaylistsAdapter
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.adapters.VideosAdapter.Companion.LayoutMode
import com.github.libretube.ui.extensions.setupFragmentAnimation
import com.github.libretube.ui.models.HomeViewModel
import com.github.libretube.ui.models.SubscriptionsViewModel
import com.google.android.material.snackbar.Snackbar
@ -84,6 +86,10 @@ class HomeFragment : Fragment() {
binding.changeInstance.setOnClickListener {
redirectToIntentSettings()
}
if (NavBarHelper.getStartFragmentId(requireContext()) != R.id.homeFragment) {
setupFragmentAnimation(binding.root)
}
}
override fun onResume() {

View File

@ -35,6 +35,7 @@ import com.github.libretube.ui.adapters.PlaylistsAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.dialogs.CreatePlaylistDialog
import com.github.libretube.ui.dialogs.CreatePlaylistDialog.Companion.CREATE_PLAYLIST_DIALOG_REQUEST_KEY
import com.github.libretube.ui.extensions.setupFragmentAnimation
import com.github.libretube.ui.models.CommonPlayerViewModel
import com.github.libretube.ui.sheets.BaseBottomSheet
import kotlinx.coroutines.Dispatchers
@ -131,6 +132,10 @@ class LibraryFragment : DynamicLayoutManagerFragment() {
}
}.show(childFragmentManager)
}
if (NavBarHelper.getStartFragmentId(requireContext()) != R.id.libraryFragment) {
setupFragmentAnimation(binding.root)
}
}
override fun onDestroyView() {

View File

@ -0,0 +1,49 @@
package com.github.libretube.ui.fragments
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.github.libretube.R
import com.github.libretube.databinding.FragmentNointernetBinding
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.NetworkHelper
import com.github.libretube.ui.activities.SettingsActivity
import com.google.android.material.snackbar.Snackbar
class NoInternetFragment: Fragment() {
private var _binding: FragmentNointernetBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentNointernetBinding.inflate(layoutInflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.retryButton.setOnClickListener {
if (NetworkHelper.isNetworkAvailable(requireContext())) {
NavigationHelper.restartMainActivity(requireContext())
} else {
Snackbar.make(binding.root, R.string.turnInternetOn, Snackbar.LENGTH_LONG).show()
}
}
binding.noInternetSettingsImageView.setOnClickListener {
val intent = Intent(requireContext(), SettingsActivity::class.java)
startActivity(intent)
}
binding.downloads.setOnClickListener {
findNavController().navigate(R.id.downloadsFragment)
}
}
}

View File

@ -95,6 +95,7 @@ import com.github.libretube.ui.dialogs.AddToPlaylistDialog
import com.github.libretube.ui.dialogs.PlayOfflineDialog
import com.github.libretube.ui.dialogs.ShareDialog
import com.github.libretube.ui.extensions.animateDown
import com.github.libretube.ui.extensions.setOnBackPressed
import com.github.libretube.ui.extensions.setupSubscriptionButton
import com.github.libretube.ui.interfaces.OnlinePlayerOptions
import com.github.libretube.ui.listeners.SeekbarPreviewListener
@ -459,6 +460,20 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
} else {
attachToPlayerService(playerData, createNewSession)
}
val onBackPressedCallback = setOnBackPressed {
if (commonPlayerViewModel.isFullscreen.value == true) unsetFullscreen()
else {
binding.playerMotionLayout.setTransitionDuration(250)
binding.playerMotionLayout.transitionToEnd()
mainActivity.binding.mainMotionLayout.transitionToEnd()
mainActivity.requestOrientationChange()
}
}
commonPlayerViewModel.isMiniPlayerVisible.observe(viewLifecycleOwner) { isMiniPlayerVisible ->
// if the player is minimized, the fragment behind the player should handle the event
onBackPressedCallback.isEnabled = isMiniPlayerVisible != true
}
}
private fun attachToPlayerService(playerData: PlayerData, startNewSession: Boolean) {

View File

@ -41,6 +41,7 @@ import com.github.libretube.ui.adapters.PlaylistAdapter
import com.github.libretube.ui.base.BaseActivity
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.extensions.addOnBottomReachedListener
import com.github.libretube.ui.extensions.setupFragmentAnimation
import com.github.libretube.ui.models.CommonPlayerViewModel
import com.github.libretube.ui.sheets.BaseBottomSheet
import com.github.libretube.ui.sheets.PlaylistOptionsBottomSheet
@ -119,6 +120,8 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
})
fetchPlaylist()
setupFragmentAnimation(binding.root)
}
override fun onDestroyView() {

View File

@ -12,6 +12,7 @@ import androidx.fragment.app.viewModels
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import androidx.paging.LoadState
import androidx.recyclerview.widget.GridLayoutManager
@ -26,6 +27,7 @@ import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.adapters.SearchResultsAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.extensions.setOnBackPressed
import com.github.libretube.ui.models.SearchResultViewModel
import com.github.libretube.util.TextUtils.toTimeInSeconds
import kotlinx.coroutines.Dispatchers
@ -112,6 +114,11 @@ class SearchResultFragment : DynamicLayoutManagerFragment() {
}
}
}
setOnBackPressed {
findNavController().popBackStack(R.id.searchFragment, true) ||
findNavController().popBackStack()
}
}
private fun addToHistory(query: String) {

View File

@ -10,6 +10,7 @@ import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.constants.IntentData
@ -17,10 +18,12 @@ import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.FragmentSearchSuggestionsBinding
import com.github.libretube.db.DatabaseHolder.Database
import com.github.libretube.extensions.TAG
import com.github.libretube.extensions.anyChildFocused
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.ui.activities.MainActivity
import com.github.libretube.ui.adapters.SearchHistoryAdapter
import com.github.libretube.ui.adapters.SearchSuggestionsAdapter
import com.github.libretube.ui.extensions.setOnBackPressed
import com.github.libretube.ui.models.SearchViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@ -30,6 +33,7 @@ class SearchSuggestionsFragment : Fragment() {
private var _binding: FragmentSearchSuggestionsBinding? = null
private val binding get() = _binding!!
private val viewModel: SearchViewModel by activityViewModels()
private val mainActivity get() = activity as MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -57,6 +61,11 @@ class SearchSuggestionsFragment : Fragment() {
viewModel.searchQuery.observe(viewLifecycleOwner) {
showData(it)
}
setOnBackPressed {
if (mainActivity.searchView.anyChildFocused()) mainActivity.searchView.clearFocus()
else findNavController().popBackStack()
}
}
private fun showData(query: String?) {

View File

@ -29,6 +29,7 @@ import com.github.libretube.db.DatabaseHolder
import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.formatShort
import com.github.libretube.extensions.toID
import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.obj.SelectableOption
@ -37,8 +38,9 @@ import com.github.libretube.ui.adapters.SubscriptionChannelAdapter
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.extensions.addOnBottomReachedListener
import com.github.libretube.ui.models.EditChannelGroupsModel
import com.github.libretube.ui.extensions.setupFragmentAnimation
import com.github.libretube.ui.models.CommonPlayerViewModel
import com.github.libretube.ui.models.EditChannelGroupsModel
import com.github.libretube.ui.models.SubscriptionsViewModel
import com.github.libretube.ui.sheets.ChannelGroupsSheet
import com.github.libretube.ui.sheets.FilterSortBottomSheet
@ -203,6 +205,10 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
.sortedBy { it.index }
channelGroupsModel.groups.postValue(groups)
}
if (NavBarHelper.getStartFragmentId(requireContext()) != R.id.subscriptionsFragment) {
setupFragmentAnimation(binding.root)
}
}
private fun loadNextFeedItems() {

View File

@ -11,9 +11,11 @@ import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.R
import com.github.libretube.databinding.FragmentTrendsBinding
import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.ui.activities.SettingsActivity
import com.github.libretube.ui.adapters.VideosAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.extensions.setupFragmentAnimation
import com.github.libretube.ui.models.TrendsViewModel
import com.google.android.material.snackbar.Snackbar
@ -70,6 +72,10 @@ class TrendsFragment : DynamicLayoutManagerFragment() {
})
viewModel.fetchTrending(requireContext())
if (NavBarHelper.getStartFragmentId(requireContext()) != R.id.trendsFragment) {
setupFragmentAnimation(binding.root)
}
}
override fun onConfigurationChanged(newConfig: Configuration) {

View File

@ -27,12 +27,14 @@ import com.github.libretube.db.obj.WatchHistoryItem
import com.github.libretube.extensions.ceilHalf
import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.setOnDismissListener
import com.github.libretube.helpers.NavBarHelper
import com.github.libretube.helpers.NavigationHelper
import com.github.libretube.helpers.PreferenceHelper
import com.github.libretube.helpers.ProxyHelper
import com.github.libretube.ui.adapters.WatchHistoryAdapter
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
import com.github.libretube.ui.extensions.addOnBottomReachedListener
import com.github.libretube.ui.extensions.setupFragmentAnimation
import com.github.libretube.ui.models.CommonPlayerViewModel
import com.github.libretube.ui.sheets.BaseBottomSheet
import com.github.libretube.util.PlayingQueue
@ -159,6 +161,10 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
showWatchHistory(history)
}
if (NavBarHelper.getStartFragmentId(requireContext()) != R.id.watchHistoryFragment) {
setupFragmentAnimation(binding.root)
}
}
private fun showWatchHistory(history: List<WatchHistoryItem>) {

View File

@ -1,66 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/noInternet_settingsImageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end"
android:layout_margin="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="3dp"
android:src="@drawable/ic_settings" />
<LinearLayout
android:id="@+id/middle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/noInternet_imageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_no_wifi" />
<TextView
android:id="@+id/noInternet_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/noInternet" />
<com.google.android.material.button.MaterialButton
android:id="@+id/retry_button"
style="@style/ThemeOverlay.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="@string/retry"
android:textColor="?android:attr/colorBackground"
app:elevation="20dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/downloads"
style="@style/ThemeOverlay.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:text="@string/downloads"
android:textColor="?android:attr/colorBackground"
app:elevation="20dp" />
</LinearLayout>
<FrameLayout
<fragment
android:id="@+id/container"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:elevation="20dp"

View File

@ -0,0 +1,61 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/noInternet_settingsImageView"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="end"
android:layout_margin="16dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="3dp"
android:src="@drawable/ic_settings" />
<LinearLayout
android:id="@+id/middle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/noInternet_imageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center"
app:srcCompat="@drawable/ic_no_wifi" />
<TextView
android:id="@+id/noInternet_textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/noInternet" />
<com.google.android.material.button.MaterialButton
android:id="@+id/retry_button"
style="@style/ThemeOverlay.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="20dp"
android:text="@string/retry"
android:textColor="?android:attr/colorBackground"
app:elevation="20dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/downloads"
style="@style/ThemeOverlay.Material3.Button.ElevatedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginTop="5dp"
android:text="@string/downloads"
android:textColor="?android:attr/colorBackground"
app:elevation="20dp" />
</LinearLayout>
</FrameLayout>

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_noInternet"
app:startDestination="@id/noInternetFragment">
<fragment
android:id="@+id/noInternetFragment"
android:name="com.github.libretube.ui.fragments.NoInternetFragment"
android:label="fragment_nointernet"
tools:layout="@layout/fragment_nointernet" />
<fragment
android:id="@+id/downloadsFragment"
android:name="com.github.libretube.ui.fragments.DownloadsFragment"
android:label="@string/downloads"
tools:layout="@layout/fragment_downloads" />
</navigation>