diff --git a/app/src/main/java/com/cappielloantonio/tempo/navigation/NavigationController.java b/app/src/main/java/com/cappielloantonio/tempo/navigation/NavigationController.java new file mode 100644 index 00000000..a6437dca --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/navigation/NavigationController.java @@ -0,0 +1,44 @@ +package com.cappielloantonio.tempo.navigation; + +import android.view.View; + +import androidx.annotation.NonNull; +import androidx.annotation.OptIn; +import androidx.appcompat.app.AppCompatActivity; +import androidx.media3.common.util.UnstableApi; +import androidx.navigation.NavController; + +import com.cappielloantonio.tempo.navigation.NavigationHelper; + +import com.google.android.material.bottomsheet.BottomSheetBehavior; + +public class NavigationController { + + NavigationHelper helper; + + public NavigationController(@NonNull NavigationHelper helper) { + this.helper = helper; + } + + public void syncWithBottomSheetBehavior(BottomSheetBehavior bottomSheetBehavior, + NavController navController) { + helper.syncWithBottomSheetBehavior(bottomSheetBehavior, navController); + + } + + public void setNavbarVisibility(boolean visibility) { + helper.setBottomNavigationBarVisibility(visibility); + } + + public void setDrawerLock(boolean visibility) { + helper.setNavigationDrawerLock(visibility); + } + + public void toggleDrawerLockOnOrientation(AppCompatActivity activity) { + helper.toggleNavigationDrawerLockOnOrientationChange(activity); + } + + public void setSystemBarsVisibility(AppCompatActivity activity, boolean visibility) { + helper.setSystemBarsVisibility(activity, visibility); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/tempo/navigation/NavigationHelper.java b/app/src/main/java/com/cappielloantonio/tempo/navigation/NavigationHelper.java new file mode 100644 index 00000000..b5916f73 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/navigation/NavigationHelper.java @@ -0,0 +1,167 @@ +package com.cappielloantonio.tempo.navigation; + +import android.content.Context; +import android.content.res.Configuration; +import android.view.View; +import android.view.Window; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.OptIn; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.view.WindowCompat; +import androidx.core.view.WindowInsetsCompat; +import androidx.core.view.WindowInsetsControllerCompat; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.media3.common.util.UnstableApi; +import androidx.navigation.NavController; +import androidx.navigation.NavDestination; +import androidx.navigation.fragment.NavHostFragment; +import androidx.navigation.ui.NavigationUI; + +import com.cappielloantonio.tempo.R; +import com.cappielloantonio.tempo.ui.activity.MainActivity; +import com.cappielloantonio.tempo.util.Preferences; +import com.google.android.material.bottomnavigation.BottomNavigationView; +import com.google.android.material.bottomsheet.BottomSheetBehavior; +import com.google.android.material.navigation.NavigationView; + +import org.jetbrains.annotations.Contract; + +import java.util.Objects; + +public class NavigationHelper { + /* UI components */ + private BottomNavigationView bottomNavigationView; + private FrameLayout bottomNavigationViewFrame; + private DrawerLayout drawerLayout; + + /* Navigation components */ + private NavigationView navigationView; + private NavHostFragment navHostFragment; + + /* States that need to be remembered */ + // -- // + + /* Private constructor */ + public NavigationHelper(@NonNull BottomNavigationView bottomNavigationView, + @NonNull FrameLayout bottomNavigationViewFrame, + @NonNull DrawerLayout drawerLayout, + @NonNull NavigationView navigationView, + @NonNull NavHostFragment navHostFragment) { + this.bottomNavigationView = bottomNavigationView; + this.bottomNavigationViewFrame = bottomNavigationViewFrame; + this.drawerLayout = drawerLayout; + this.navigationView = navigationView; + this.navHostFragment = navHostFragment; + } + + public void syncWithBottomSheetBehavior(@NonNull BottomSheetBehavior bottomSheetBehavior, + @NonNull NavController navController) { + navController.addOnDestinationChangedListener( + (controller, destination, arguments) -> { + // React to the user clicking one of these on bottom-navbar/drawer + boolean isTarget = isTargetDestination(destination); + int currentState = bottomSheetBehavior.getState(); + + if (isTarget && currentState == BottomSheetBehavior.STATE_EXPANDED) { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + } + }); + + NavigationUI.setupWithNavController(bottomNavigationView, navController); + NavigationUI.setupWithNavController(navigationView, navController); + } + + @Contract(pure = true) + private static boolean isTargetDestination(NavDestination destination) { + int destId = destination.getId(); + return destId == R.id.homeFragment || + destId == R.id.libraryFragment || + destId == R.id.downloadFragment || + destId == R.id.albumCatalogueFragment || + destId == R.id.artistCatalogueFragment || + destId == R.id.genreCatalogueFragment || + destId == R.id.playlistCatalogueFragment; + } + + /* + Clean public methods + Removes the need to invoke the activity on the fragment + */ + + public void setBottomNavigationBarVisibility(boolean visible) { + int visibility = visible + ? View.VISIBLE + : View.GONE; + bottomNavigationView.setVisibility(visibility); + bottomNavigationViewFrame.setVisibility(visibility); + } + + public void setNavigationDrawerLock(boolean locked) { + int mode = locked + ? DrawerLayout.LOCK_MODE_LOCKED_CLOSED + : DrawerLayout.LOCK_MODE_UNLOCKED; + drawerLayout.setDrawerLockMode(mode); + } + + @OptIn(markerClass = UnstableApi.class) + public void toggleNavigationDrawerLockOnOrientationChange( + AppCompatActivity activity) { + + int orientation = activity.getResources().getConfiguration().orientation; + boolean isLandscape = orientation == Configuration.ORIENTATION_LANDSCAPE; + + if (Preferences.getEnableDrawerOnPortrait()) { + setNavigationDrawerLock(false); + return; + } + setNavigationDrawerLock(!isLandscape); + } + + /* + All of these are the "backward compatible" changes that don't break the assumption + that everything was defined on the activity and is gobally available + */ + + @NonNull + public BottomNavigationView getBottomNavigationView() { + return bottomNavigationView; + } + + @NonNull + public FrameLayout getBottomNavigationViewFrame() { + return bottomNavigationViewFrame; + } + + @NonNull + public DrawerLayout getDrawerLayout() { + return drawerLayout; + } + + /* + Auxiliar functions, could be moved somewhere else + */ + + @OptIn(markerClass = UnstableApi.class) + public void setSystemBarsVisibility(AppCompatActivity activity, boolean visibility) { + WindowInsetsControllerCompat insetsController; + Window window = activity.getWindow(); + View decorView = window.getDecorView(); + insetsController = new WindowInsetsControllerCompat(window, decorView); + + if (visibility) { + WindowCompat.setDecorFitsSystemWindows(window, true); + insetsController.show(WindowInsetsCompat.Type.navigationBars()); + insetsController.show(WindowInsetsCompat.Type.statusBars()); + insetsController.setSystemBarsBehavior( + WindowInsetsControllerCompat.BEHAVIOR_DEFAULT); + } else { + WindowCompat.setDecorFitsSystemWindows(window, false); + insetsController.hide(WindowInsetsCompat.Type.navigationBars()); + insetsController.hide(WindowInsetsCompat.Type.statusBars()); + insetsController.setSystemBarsBehavior( + WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); + } + } +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java b/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java index b69f9581..313621bf 100644 --- a/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/activity/MainActivity.java @@ -4,7 +4,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; -import android.graphics.Rect; import android.content.IntentFilter; import android.net.ConnectivityManager; import android.net.NetworkInfo; @@ -31,7 +30,6 @@ import androidx.media3.common.Player; import androidx.media3.common.util.UnstableApi; import androidx.navigation.NavController; import androidx.navigation.fragment.NavHostFragment; -import androidx.navigation.ui.NavigationUI; import com.cappielloantonio.tempo.App; import com.cappielloantonio.tempo.BuildConfig; @@ -39,8 +37,12 @@ import com.cappielloantonio.tempo.R; import com.cappielloantonio.tempo.broadcast.receiver.ConnectivityStatusBroadcastReceiver; import com.cappielloantonio.tempo.databinding.ActivityMainBinding; import com.cappielloantonio.tempo.github.utils.UpdateUtil; +import com.cappielloantonio.tempo.navigation.NavigationController; +import com.cappielloantonio.tempo.navigation.NavigationHelper; import com.cappielloantonio.tempo.service.MediaManager; import com.cappielloantonio.tempo.ui.activity.base.BaseActivity; +import com.cappielloantonio.tempo.ui.controller.BottomSheetController; +import com.cappielloantonio.tempo.ui.controller.BottomSheetHelper; import com.cappielloantonio.tempo.ui.dialog.ConnectionAlertDialog; import com.cappielloantonio.tempo.ui.dialog.GithubTempoUpdateDialog; import com.cappielloantonio.tempo.ui.dialog.ServerUnreachableDialog; @@ -70,10 +72,12 @@ public class MainActivity extends BaseActivity { private NavHostFragment navHostFragment; private BottomNavigationView bottomNavigationView; private FrameLayout bottomNavigationViewFrame; - public NavController navController; private DrawerLayout drawerLayout; private NavigationView navigationView; - private BottomSheetBehavior bottomSheetBehavior; + public NavController navController; + private NavigationController navigationController; + private BottomSheetController bottomSheetController; + public BottomSheetBehavior bottomSheetBehavior; public boolean isLandscape = false; private AssetLinkNavigator assetLinkNavigator; private AssetLinkUtil.AssetLink pendingAssetLink; @@ -81,6 +85,10 @@ public class MainActivity extends BaseActivity { ConnectivityStatusBroadcastReceiver connectivityStatusBroadcastReceiver; private Intent pendingDownloadPlaybackIntent; + public ActivityMainBinding getBinding() { + return bind; + } + @Override protected void onCreate(Bundle savedInstanceState) { SplashScreen.installSplashScreen(this); @@ -147,7 +155,6 @@ public class MainActivity extends BaseActivity { } public void init() { - fragmentManager = getSupportFragmentManager(); initBottomSheet(); initNavigation(); @@ -162,49 +169,74 @@ public class MainActivity extends BaseActivity { } - // BOTTOM SHEET/NAVIGATION - private void initBottomSheet() { - bottomSheetBehavior = BottomSheetBehavior.from(findViewById(R.id.player_bottom_sheet)); - bottomSheetBehavior.addBottomSheetCallback(bottomSheetCallback); - fragmentManager.beginTransaction().replace(R.id.player_bottom_sheet, new PlayerBottomSheetFragment(), "PlayerBottomSheet").commit(); + private void initNavigation() { + // We link the nav_graph.xml with our navigationController + NavHostFragment navHostFragment = (NavHostFragment) this + .getSupportFragmentManager() + .findFragmentById(R.id.nav_host_fragment); + navController = Objects.requireNonNull(navHostFragment).getNavController(); + /* + navController is currently global since some legacy code still invokes it directly + the MainActivity methods that use it must be converted to NavigationHelper methods + */ - checkBottomSheetAfterStateChanged(); + // Helper + NavigationHelper navigationHelper = + new NavigationHelper( + findViewById(R.id.bottom_navigation), + findViewById(R.id.bottom_navigation_frame), + findViewById(R.id.drawer_layout), + findViewById(R.id.nav_view), + navHostFragment + ); + + // Controller + navigationController = new NavigationController(navigationHelper); + navigationController.syncWithBottomSheetBehavior(bottomSheetBehavior, navController); + } + + private void initBottomSheet() { + FragmentManager fragmentManager = getSupportFragmentManager(); + View bottomSheetView = findViewById(R.id.player_bottom_sheet); + bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetView); + /* + bottomSheetBehavior is currently global since some legacy code still invokes it directly + the MainActivity methods that use it must be converted to BottomSheetHelper methods + */ + + // Helper + BottomSheetHelper bottomSheetHelper = + new BottomSheetHelper( + bottomSheetBehavior, + bottomSheetView, + fragmentManager + ); + + // Controller + bottomSheetController = new BottomSheetController(bottomSheetHelper); + bottomSheetController.addCallback(bottomSheetCallback); + bottomSheetController.replaceFragment(R.id.player_bottom_sheet); + bottomSheetController.checkAfterStateChanged(mainViewModel); } public void setBottomSheetInPeek(Boolean isVisible) { - if (isVisible) { - bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); - } else { - bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - } + bottomSheetController.setStateInPeek(isVisible); } public void setBottomSheetVisibility(boolean visibility) { - if (visibility) { - findViewById(R.id.player_bottom_sheet).setVisibility(View.VISIBLE); - } else { - findViewById(R.id.player_bottom_sheet).setVisibility(View.GONE); - } - } - - private void checkBottomSheetAfterStateChanged() { - final Handler handler = new Handler(); - final Runnable runnable = () -> setBottomSheetInPeek(mainViewModel.isQueueLoaded()); - handler.postDelayed(runnable, 100); + bottomSheetController.setVisibility(visibility); } public void collapseBottomSheetDelayed() { - final Handler handler = new Handler(); - final Runnable runnable = () -> bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); - handler.postDelayed(runnable, 100); + bottomSheetController.collapseDelayed(); } public void expandBottomSheet() { - bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); + bottomSheetController.expand(); } public void setBottomSheetDraggableState(Boolean isDraggable) { - bottomSheetBehavior.setDraggable(isDraggable); + bottomSheetController.setDraggable(isDraggable); } private final BottomSheetBehavior.BottomSheetCallback bottomSheetCallback = @@ -217,7 +249,7 @@ public class MainActivity extends BaseActivity { switch (state) { case BottomSheetBehavior.STATE_HIDDEN: - resetMusicSession(); + resetMusicSession(); // I can't put the callback inside BottomSheetHelper because of this line break; case BottomSheetBehavior.STATE_COLLAPSED: if (playerBottomSheetFragment != null) @@ -241,12 +273,7 @@ public class MainActivity extends BaseActivity { }; private void animateBottomSheet(float slideOffset) { - PlayerBottomSheetFragment playerBottomSheetFragment = (PlayerBottomSheetFragment) getSupportFragmentManager().findFragmentByTag("PlayerBottomSheet"); - if (playerBottomSheetFragment != null) { - float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f; - playerBottomSheetFragment.getPlayerHeader().setAlpha(1 - condensedSlideOffset); - playerBottomSheetFragment.getPlayerHeader().setVisibility(condensedSlideOffset > 0.99 ? View.GONE : View.VISIBLE); - } + bottomSheetController.animate(slideOffset); } private void animateBottomNavigation(float slideOffset, int navigationHeight) { @@ -261,117 +288,52 @@ public class MainActivity extends BaseActivity { bind.bottomNavigation.setTranslationY(slideY); } - private void initNavigation() { - bottomNavigationView = findViewById(R.id.bottom_navigation); - bottomNavigationViewFrame = findViewById(R.id.bottom_navigation_frame); - navHostFragment = (NavHostFragment) fragmentManager.findFragmentById(R.id.nav_host_fragment); - navController = Objects.requireNonNull(navHostFragment).getNavController(); - // This is the lateral slide-in drawer - drawerLayout = findViewById(R.id.drawer_layout); - navigationView = findViewById(R.id.nav_view); - - /* - * In questo modo intercetto il cambio schermata tramite navbar e se il bottom sheet รจ aperto, - * lo chiudo - */ - navController.addOnDestinationChangedListener((controller, destination, arguments) -> { - if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED && ( - destination.getId() == R.id.homeFragment || - destination.getId() == R.id.libraryFragment || - destination.getId() == R.id.downloadFragment || - destination.getId() == R.id.albumCatalogueFragment || - destination.getId() == R.id.artistCatalogueFragment || - destination.getId() == R.id.genreCatalogueFragment || - destination.getId() == R.id.playlistCatalogueFragment) - ) { - bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); - } - }); - - NavigationUI.setupWithNavController(bottomNavigationView, navController); - NavigationUI.setupWithNavController(navigationView, navController); - } - public void setBottomNavigationBarVisibility(boolean visibility) { - if (visibility) { - bottomNavigationView.setVisibility(View.VISIBLE); - bottomNavigationViewFrame.setVisibility(View.VISIBLE); - } else { - bottomNavigationView.setVisibility(View.GONE); - bottomNavigationViewFrame.setVisibility(View.GONE); - } + navigationController.setNavbarVisibility(visibility); } public void toggleBottomNavigationBarVisibilityOnOrientationChange() { + float displayDensity = getResources().getDisplayMetrics().density; // Ignore orientation change, bottom navbar always hidden if (Preferences.getHideBottomNavbarOnPortrait()) { - setBottomNavigationBarVisibility(false); - setPortraitPlayerBottomSheetPeekHeight(56); - setSystemBarsVisibility(!isLandscape); + navigationController.setNavbarVisibility(false); + bottomSheetController.setPeekHeight(56, displayDensity); + navigationController.setSystemBarsVisibility(this, !isLandscape); return; } if (!isLandscape) { // Show app navbar + show system bars - setPortraitPlayerBottomSheetPeekHeight(136); - setBottomNavigationBarVisibility(true); - setSystemBarsVisibility(true); + bottomSheetController.setPeekHeight(136, displayDensity); + navigationController.setNavbarVisibility(true); + navigationController.setSystemBarsVisibility(this, true); } else { // Hide app navbar + hide system bars - setPortraitPlayerBottomSheetPeekHeight(56); - setBottomNavigationBarVisibility(false); - setSystemBarsVisibility(false); + bottomSheetController.setPeekHeight(56, displayDensity); + navigationController.setNavbarVisibility(false); + navigationController.setSystemBarsVisibility(this, false); } } public void setNavigationDrawerLock(boolean locked) { - int mode = locked - ? DrawerLayout.LOCK_MODE_LOCKED_CLOSED - : DrawerLayout.LOCK_MODE_UNLOCKED; - drawerLayout.setDrawerLockMode(mode); + navigationController.setDrawerLock(locked); } public void toggleNavigationDrawerLockOnOrientationChange() { - // Ignore orientation check, drawer always unlocked - if (Preferences.getEnableDrawerOnPortrait()) { - setNavigationDrawerLock(false); - return; - } - if (!isLandscape) { - setNavigationDrawerLock(true); - } else { - setNavigationDrawerLock(false); - } + navigationController.toggleDrawerLockOnOrientation(this); } public void setSystemBarsVisibility(boolean visibility) { - WindowInsetsControllerCompat insetsController; - View decorView = getWindow().getDecorView(); - insetsController = new WindowInsetsControllerCompat(getWindow(), decorView); - - if (visibility) { - WindowCompat.setDecorFitsSystemWindows(getWindow(), true); - insetsController.show(WindowInsetsCompat.Type.navigationBars()); - insetsController.show(WindowInsetsCompat.Type.statusBars()); - insetsController.setSystemBarsBehavior( - WindowInsetsControllerCompat.BEHAVIOR_DEFAULT); - } else { - WindowCompat.setDecorFitsSystemWindows(getWindow(), false); - insetsController.hide(WindowInsetsCompat.Type.navigationBars()); - insetsController.hide(WindowInsetsCompat.Type.statusBars()); - insetsController.setSystemBarsBehavior( - WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE); - } + navigationController.setSystemBarsVisibility(this, visibility); } - private void setPortraitPlayerBottomSheetPeekHeight(int peekHeight) { - FrameLayout bottomSheet = findViewById(R.id.player_bottom_sheet); - BottomSheetBehavior behavior = - BottomSheetBehavior.from(bottomSheet); - - int newPeekPx = (int) (peekHeight * getResources().getDisplayMetrics().density); - behavior.setPeekHeight(newPeekPx); - } + /* + There are only 4 init functions that must exist up to here + 1. init() + 2. initNavigation() + 3. initBottomSheet() + 4. bottomSheetCallback = new BottomSheetBehavior.BottomSheetCallback() { ... } + */ private void initService() { MediaManager.check(getMediaBrowserListenableFuture()); @@ -407,7 +369,7 @@ public class MainActivity extends BaseActivity { } private void goToHome() { - bottomNavigationView.setVisibility(View.VISIBLE); + setBottomNavigationBarVisibility(true); if (Objects.requireNonNull(navController.getCurrentDestination()).getId() == R.id.landingFragment) { navController.navigate(R.id.action_landingFragment_to_homeFragment); @@ -657,4 +619,4 @@ public class MainActivity extends BaseActivity { MediaManager.playDownloadedMediaItem(getMediaBrowserListenableFuture(), mediaItem); } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/controller/BottomSheetController.java b/app/src/main/java/com/cappielloantonio/tempo/ui/controller/BottomSheetController.java new file mode 100644 index 00000000..9ebff6d9 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/controller/BottomSheetController.java @@ -0,0 +1,63 @@ +package com.cappielloantonio.tempo.ui.controller; + +import androidx.annotation.NonNull; + +import com.cappielloantonio.tempo.viewmodel.MainViewModel; +import com.google.android.material.bottomsheet.BottomSheetBehavior; + +public class BottomSheetController { + + BottomSheetHelper helper; + + public BottomSheetController(@NonNull BottomSheetHelper bottomSheetPlayerHelper) { + this.helper = bottomSheetPlayerHelper; + } + + public void expand() { + helper.setState(BottomSheetBehavior.STATE_EXPANDED); + } + + public void hide() { + helper.setState(BottomSheetBehavior.STATE_HIDDEN); + } + + public void setStateInPeek(boolean isVisible) { + helper.setStateInPeek(isVisible); + } + + public void setVisibility(boolean visibility) { + helper.setVisibility(visibility); + } + + public void addCallback(BottomSheetBehavior.BottomSheetCallback callback) { + helper.addCallback(callback); + } + + public void replaceFragment(int playerBottomSheet) { + helper.replaceFragment(playerBottomSheet); + } + + public void checkAfterStateChanged(MainViewModel mainViewModel) { + helper.checkAfterStateChanged(mainViewModel); + } + + public void collapseDelayed() { + helper.collapseDelayed(); + } + + public void setDraggable(Boolean isDraggable) { + helper.setDraggable(isDraggable); + } + + public int getState() { + return helper.getState(); + } + + public void animate(float slideOffset) { + helper.animate(slideOffset); + } + + public void setPeekHeight(int peekHeight, float displayDensity) { + helper.setPeekHeight(peekHeight, displayDensity); + } +} diff --git a/app/src/main/java/com/cappielloantonio/tempo/ui/controller/BottomSheetHelper.java b/app/src/main/java/com/cappielloantonio/tempo/ui/controller/BottomSheetHelper.java new file mode 100644 index 00000000..a0c88938 --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/tempo/ui/controller/BottomSheetHelper.java @@ -0,0 +1,97 @@ +package com.cappielloantonio.tempo.ui.controller; + +import android.os.Handler; +import android.view.View; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.fragment.app.FragmentManager; + +import com.cappielloantonio.tempo.R; +import com.cappielloantonio.tempo.ui.fragment.PlayerBottomSheetFragment; +import com.cappielloantonio.tempo.viewmodel.MainViewModel; +import com.google.android.material.bottomsheet.BottomSheetBehavior; + +public class BottomSheetHelper { + + BottomSheetBehavior bottomSheetBehavior; + View bottomSheetView; + FragmentManager fragmentManager; // Of the entire activity + PlayerBottomSheetFragment playerBottomSheetFragment; + + public void setState(int state) { + bottomSheetBehavior.setState(state); + } + + public BottomSheetHelper(@NonNull BottomSheetBehavior bottomSheetBehavior, + @NonNull View bottomSheetView, + @NonNull FragmentManager fragmentManager) { + this.bottomSheetBehavior = bottomSheetBehavior; + this.bottomSheetView = bottomSheetView; + this.fragmentManager = fragmentManager; + this.playerBottomSheetFragment = new PlayerBottomSheetFragment(); + } + + public void addCallback(BottomSheetBehavior.BottomSheetCallback callback) { + bottomSheetBehavior.addBottomSheetCallback(callback); + } + + public void setStateInPeek(boolean isVisible) { + if (isVisible) { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + } else { + bottomSheetBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + } + } + + public void setVisibility(boolean visibility) { + if (visibility) { + bottomSheetView.setVisibility(View.VISIBLE); + } else { + bottomSheetView.setVisibility(View.GONE); + } + } + + public void replaceFragment(int playerBottomSheet) { + fragmentManager + .beginTransaction() + .replace( + playerBottomSheet, + playerBottomSheetFragment, + "PlayerBottomSheet") + .commit(); + } + + public void checkAfterStateChanged(MainViewModel mainViewModel) { + final Handler handler = new Handler(); + final Runnable runnable = () -> setStateInPeek(mainViewModel.isQueueLoaded()); + handler.postDelayed(runnable, 100); + } + + public void collapseDelayed() { + final Handler handler = new Handler(); + final Runnable runnable = () -> bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + handler.postDelayed(runnable, 100); + } + + public void setDraggable(Boolean isDraggable) { + bottomSheetBehavior.setDraggable((isDraggable)); + } + + public int getState() { + return bottomSheetBehavior.getState(); + } + + public void animate(float slideOffset) { + if (playerBottomSheetFragment != null) { + float condensedSlideOffset = Math.max(0.0f, Math.min(0.2f, slideOffset - 0.2f)) / 0.2f; + playerBottomSheetFragment.getPlayerHeader().setAlpha(1 - condensedSlideOffset); + playerBottomSheetFragment.getPlayerHeader().setVisibility(condensedSlideOffset > 0.99 ? View.GONE : View.VISIBLE); + } + } + + public void setPeekHeight(int peekHeight, float displayDensity) { + int newPeekPx = (int) (peekHeight * displayDensity); + bottomSheetBehavior.setPeekHeight(newPeekPx); + } +} \ No newline at end of file