refactor: delegate navigation to controller and helper
This commit is contained in:
@@ -10,7 +10,6 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
@@ -39,6 +38,9 @@ import com.cappielloantonio.tempo.databinding.ActivityMainBinding;
|
||||
import com.cappielloantonio.tempo.github.utils.UpdateUtil;
|
||||
import com.cappielloantonio.tempo.service.MediaManager;
|
||||
import com.cappielloantonio.tempo.ui.activity.base.BaseActivity;
|
||||
import com.cappielloantonio.tempo.ui.activity.base.NavigationController;
|
||||
import com.cappielloantonio.tempo.ui.activity.base.NavigationDelegate;
|
||||
import com.cappielloantonio.tempo.ui.activity.base.NavigationHelper;
|
||||
import com.cappielloantonio.tempo.ui.dialog.ConnectionAlertDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.GithubTempoUpdateDialog;
|
||||
import com.cappielloantonio.tempo.ui.dialog.ServerUnreachableDialog;
|
||||
@@ -62,6 +64,7 @@ public class MainActivity extends BaseActivity {
|
||||
private static final String TAG = "MainActivityLogs";
|
||||
|
||||
public ActivityMainBinding bind;
|
||||
private NavigationHelper navigationHelper;
|
||||
private MainViewModel mainViewModel;
|
||||
|
||||
private FragmentManager fragmentManager;
|
||||
@@ -71,7 +74,7 @@ public class MainActivity extends BaseActivity {
|
||||
public NavController navController;
|
||||
private DrawerLayout drawerLayout;
|
||||
private NavigationView navigationView;
|
||||
private BottomSheetBehavior bottomSheetBehavior;
|
||||
public BottomSheetBehavior bottomSheetBehavior;
|
||||
public boolean isLandscape = false;
|
||||
private AssetLinkNavigator assetLinkNavigator;
|
||||
private AssetLinkUtil.AssetLink pendingAssetLink;
|
||||
@@ -79,6 +82,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);
|
||||
@@ -126,6 +133,10 @@ public class MainActivity extends BaseActivity {
|
||||
super.onDestroy();
|
||||
connectivityStatusReceiverManager(false);
|
||||
bind = null;
|
||||
NavigationDelegate.getInstance().unbind();
|
||||
if (navigationHelper != null) {
|
||||
navigationHelper.release();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -148,7 +159,16 @@ public class MainActivity extends BaseActivity {
|
||||
fragmentManager = getSupportFragmentManager();
|
||||
|
||||
initBottomSheet();
|
||||
initNavigation();
|
||||
|
||||
// All of the navigation stuff, contained here
|
||||
NavigationDelegate.getInstance().bind(this);
|
||||
navigationHelper = NavigationHelper.init(this);
|
||||
|
||||
// This is for "backward compatibility" with old code
|
||||
navController = navigationHelper.getNavController();
|
||||
bottomNavigationView = navigationHelper.getBottomNavigationView();
|
||||
bottomNavigationViewFrame = navigationHelper.getBottomNavigationViewFrame();
|
||||
drawerLayout = navigationHelper.getDrawerLayout();
|
||||
|
||||
if (Preferences.getPassword() != null || (Preferences.getToken() != null && Preferences.getSalt() != null)) {
|
||||
goFromLogin();
|
||||
@@ -259,103 +279,42 @@ 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)
|
||||
) {
|
||||
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);
|
||||
}
|
||||
navigationHelper.setBottomNavigationBarVisibility(visibility);
|
||||
}
|
||||
|
||||
public void toggleBottomNavigationBarVisibilityOnOrientationChange() {
|
||||
// Ignore orientation change, bottom navbar always hidden
|
||||
if (Preferences.getHideBottomNavbarOnPortrait()) {
|
||||
setBottomNavigationBarVisibility(false);
|
||||
navigationHelper.setBottomNavigationBarVisibility(false);
|
||||
setPortraitPlayerBottomSheetPeekHeight(56);
|
||||
setSystemBarsVisibility(!isLandscape);
|
||||
navigationHelper.setSystemBarsVisibility(this, !isLandscape);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isLandscape) {
|
||||
// Show app navbar + show system bars
|
||||
setPortraitPlayerBottomSheetPeekHeight(136);
|
||||
setBottomNavigationBarVisibility(true);
|
||||
setSystemBarsVisibility(true);
|
||||
navigationHelper.setBottomNavigationBarVisibility(true);
|
||||
navigationHelper.setSystemBarsVisibility(this, true);
|
||||
} else {
|
||||
// Hide app navbar + hide system bars
|
||||
setPortraitPlayerBottomSheetPeekHeight(56);
|
||||
setBottomNavigationBarVisibility(false);
|
||||
setSystemBarsVisibility(false);
|
||||
navigationHelper.setBottomNavigationBarVisibility(false);
|
||||
navigationHelper.setSystemBarsVisibility(this, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void setNavigationDrawerLock(boolean locked) {
|
||||
int mode = locked
|
||||
? DrawerLayout.LOCK_MODE_LOCKED_CLOSED
|
||||
: DrawerLayout.LOCK_MODE_UNLOCKED;
|
||||
drawerLayout.setDrawerLockMode(mode);
|
||||
navigationHelper.setNavigationDrawerLock(locked);
|
||||
}
|
||||
|
||||
public void toggleNavigationDrawerLockOnOrientationChange() {
|
||||
// Ignore orientation check, drawer always unlocked
|
||||
if (Preferences.getEnableDrawerOnPortrait()) {
|
||||
setNavigationDrawerLock(false);
|
||||
return;
|
||||
}
|
||||
if (!isLandscape) {
|
||||
setNavigationDrawerLock(true);
|
||||
} else {
|
||||
setNavigationDrawerLock(false);
|
||||
}
|
||||
navigationHelper.toggleNavigationDrawerLockOnOrientationChange(this, isLandscape);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
navigationHelper.setSystemBarsVisibility(this, visibility);
|
||||
}
|
||||
|
||||
private void setPortraitPlayerBottomSheetPeekHeight(int peekHeight) {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.cappielloantonio.tempo.ui.activity.base;
|
||||
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
public interface NavigationController {
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
default boolean isVisible() {
|
||||
return NavigationDelegate.isVisible();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.cappielloantonio.tempo.ui.activity.base;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
|
||||
import com.cappielloantonio.tempo.databinding.ActivityMainBinding;
|
||||
import com.cappielloantonio.tempo.ui.activity.MainActivity;
|
||||
|
||||
/*
|
||||
The goal of this class is to stop instanciating MainActivity on each fragment */
|
||||
@UnstableApi
|
||||
public final class NavigationDelegate {
|
||||
|
||||
private static final NavigationDelegate INSTANCE = new NavigationDelegate();
|
||||
|
||||
private MainActivity activity;
|
||||
private boolean visible = true;
|
||||
|
||||
private NavigationDelegate() {}
|
||||
|
||||
public static NavigationDelegate getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
/* Call inside onCreate() in MainActivity giving as argument `this` */
|
||||
public void bind(MainActivity activity) {
|
||||
this.activity = activity;
|
||||
}
|
||||
|
||||
/* Call inside onDestroy() in MainActivity*/
|
||||
public void unbind() {
|
||||
this.activity = null;
|
||||
}
|
||||
|
||||
public static boolean isVisible() {
|
||||
return INSTANCE.visible;
|
||||
}
|
||||
|
||||
/** Change visibility and update the UI on the UI thread. */
|
||||
public void setVisibility(final boolean visible) {
|
||||
this.visible = visible;
|
||||
ActivityMainBinding bind = activity.getBinding();
|
||||
bind.bottomNavigation.setVisibility(visible
|
||||
? View.VISIBLE
|
||||
: View.GONE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
package com.cappielloantonio.tempo.ui.activity.base;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.OptIn;
|
||||
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.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 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;
|
||||
private NavController navController;
|
||||
|
||||
/* States that need to be remembered */
|
||||
private final Context context;
|
||||
|
||||
/* Private constructor */
|
||||
private NavigationHelper(@NonNull Context context) {
|
||||
this.context = context.getApplicationContext();
|
||||
}
|
||||
|
||||
/* Call inside onCreate() in MainActivity giving as argument `this` */
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public static NavigationHelper init(@NonNull MainActivity activity) {
|
||||
NavigationHelper helper = new NavigationHelper(activity);
|
||||
helper.bindViews(activity);
|
||||
helper.setupNavigation(activity);
|
||||
return helper;
|
||||
}
|
||||
|
||||
/* Call inside onDestroy() in MainActivity*/
|
||||
public void release() {
|
||||
bottomNavigationView = null;
|
||||
bottomNavigationViewFrame = null;
|
||||
drawerLayout = null;
|
||||
navigationView = null;
|
||||
navHostFragment = null;
|
||||
navController = null;
|
||||
}
|
||||
|
||||
/* Bind the views by finding them on the layout (XML id attr) */
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
private void bindViews(@NonNull MainActivity activity) {
|
||||
bottomNavigationView = activity.findViewById(R.id.bottom_navigation);
|
||||
bottomNavigationViewFrame = activity.findViewById(R.id.bottom_navigation_frame);
|
||||
drawerLayout = activity.findViewById(R.id.drawer_layout);
|
||||
navigationView = activity.findViewById(R.id.nav_view);
|
||||
|
||||
navHostFragment = (NavHostFragment) activity
|
||||
.getSupportFragmentManager()
|
||||
.findFragmentById(R.id.nav_host_fragment);
|
||||
navController = Objects.requireNonNull(navHostFragment).getNavController();
|
||||
}
|
||||
|
||||
/* The navigation graph (untouched original implementation) */
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
private void setupNavigation(@NonNull MainActivity activity) {
|
||||
navController.addOnDestinationChangedListener(
|
||||
(controller, destination, arguments) -> {
|
||||
int destId = destination.getId();
|
||||
boolean isTarget = destId == R.id.homeFragment ||
|
||||
destId == R.id.libraryFragment ||
|
||||
destId == R.id.downloadFragment;
|
||||
|
||||
if (isTarget &&
|
||||
activity.bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
|
||||
activity.bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
|
||||
}
|
||||
});
|
||||
|
||||
NavigationUI.setupWithNavController(bottomNavigationView, navController);
|
||||
NavigationUI.setupWithNavController(navigationView, navController);
|
||||
}
|
||||
|
||||
/*
|
||||
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);
|
||||
}
|
||||
|
||||
public void toggleNavigationDrawerLockOnOrientationChange(MainActivity activity, boolean isLandscape) {
|
||||
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 NavController getNavController() {
|
||||
return navController;
|
||||
}
|
||||
|
||||
@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(MainActivity 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user