/* * Copyright (c) 2021 Auxio Project * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package org.oxycblt.auxio import android.os.Build import android.os.Bundle import android.view.LayoutInflater import android.view.View import androidx.activity.OnBackPressedCallback import androidx.fragment.app.activityViewModels import androidx.navigation.findNavController import androidx.navigation.fragment.findNavController import com.google.android.material.transition.MaterialFadeThrough import org.oxycblt.auxio.databinding.FragmentMainBinding import org.oxycblt.auxio.music.Music import org.oxycblt.auxio.music.Song import org.oxycblt.auxio.playback.PlaybackViewModel import org.oxycblt.auxio.ui.MainNavigationAction import org.oxycblt.auxio.ui.NavigationViewModel import org.oxycblt.auxio.ui.fragment.ViewBindingFragment import org.oxycblt.auxio.util.androidActivityViewModels import org.oxycblt.auxio.util.collect import org.oxycblt.auxio.util.collectImmediately /** * A wrapper around the home fragment that shows the playback fragment and controls the more * high-level navigation features. * @author OxygenCobalt */ class MainFragment : ViewBindingFragment() { private val playbackModel: PlaybackViewModel by androidActivityViewModels() private val navModel: NavigationViewModel by activityViewModels() private var callback: DynamicBackPressedCallback? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enterTransition = MaterialFadeThrough() exitTransition = MaterialFadeThrough() } override fun onCreateBinding(inflater: LayoutInflater) = FragmentMainBinding.inflate(inflater) override fun onBindingCreated(binding: FragmentMainBinding, savedInstanceState: Bundle?) { // --- UI SETUP --- requireActivity() .onBackPressedDispatcher.addCallback( viewLifecycleOwner, DynamicBackPressedCallback().also { callback = it }) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // Auxio's layout completely breaks down when it's window is resized too small, // but for some insane reason google decided to cripple the window APIs one could use // to limit it's size. So, we just have our own special layout that is shown whenever // the screen is too small because of course we have to. if (requireActivity().isInMultiWindowMode) { val config = resources.configuration if (config.screenHeightDp < 250 || config.screenWidthDp < 250) { binding.layoutTooSmall.visibility = View.VISIBLE } } } // --- VIEWMODEL SETUP --- collect(navModel.mainNavigationAction, ::handleMainNavigation) collect(navModel.exploreNavigationItem, ::handleExploreNavigation) collectImmediately(playbackModel.song, ::updateSong) } override fun onResume() { super.onResume() callback?.isEnabled = true } override fun onPause() { super.onPause() callback?.isEnabled = false } private fun handleMainNavigation(action: MainNavigationAction?) { if (action == null) return val binding = requireBinding() when (action) { is MainNavigationAction.Expand -> binding.bottomSheetLayout.expand() is MainNavigationAction.Collapse -> binding.bottomSheetLayout.collapse() is MainNavigationAction.Settings -> findNavController().navigate(MainFragmentDirections.actionShowSettings()) is MainNavigationAction.About -> findNavController().navigate(MainFragmentDirections.actionShowAbout()) is MainNavigationAction.Queue -> findNavController().navigate(MainFragmentDirections.actionShowQueue()) is MainNavigationAction.SongDetails -> findNavController() .navigate(MainFragmentDirections.actionShowDetails(action.song.id)) } navModel.finishMainNavigation() } private fun handleExploreNavigation(item: Music?) { if (item != null) { requireBinding().bottomSheetLayout.collapse() } } private fun updateSong(song: Song?) { val binding = requireBinding() if (song != null) { binding.bottomSheetLayout.isDraggable = true binding.bottomSheetLayout.show() } else { binding.bottomSheetLayout.isDraggable = false binding.bottomSheetLayout.hide() } } /** * A back press callback that handles how to respond to backwards navigation in the detail * fragments and the playback panel. * * TODO: Migrate to new predictive API */ inner class DynamicBackPressedCallback : OnBackPressedCallback(false) { override fun handleOnBackPressed() { val binding = requireBinding() if (!binding.bottomSheetLayout.collapse()) { val navController = binding.exploreNavHost.findNavController() if (navController.currentDestination?.id == navController.graph.startDestinationId) { isEnabled = false requireActivity().onBackPressed() isEnabled = true } else { navController.navigateUp() } } } } }