From e5d5713218590b024747d3c4a336a8dc878e7324 Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 31 Aug 2025 15:53:45 +0300 Subject: [PATCH 1/2] Introduce consolidated MainUiState --- .../java/ui/screens/main/MainActivity.java | 16 +++++----- .../java/ui/screens/main/MainUiState.java | 30 +++++++++++++++++++ .../java/ui/screens/main/MainViewModel.java | 29 ++++-------------- 3 files changed, 44 insertions(+), 31 deletions(-) create mode 100644 app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainUiState.java diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java index 61c7546a..aad63cf3 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainActivity.java @@ -185,13 +185,17 @@ private void launcherShortcuts() { } private void observeViewModel() { - mainViewModel.getBottomNavVisibility().observe(this, visibilityMode -> { + mainViewModel.getUiState().observe(this, uiState -> { + if (uiState == null) { + return; + } + EdgeToEdgeDelegate edgeToEdgeDelegate = new EdgeToEdgeDelegate(this); if (mBinding.navView instanceof BottomNavigationView) { edgeToEdgeDelegate.applyEdgeToEdgeBottomBar(mBinding.container, mBinding.navView); - ((BottomNavigationView) mBinding.navView).setLabelVisibilityMode(visibilityMode); + ((BottomNavigationView) mBinding.navView).setLabelVisibilityMode(uiState.getBottomNavVisibility()); if (mBinding.adView != null) { if (ConsentUtils.canShowAds(this)) { MobileAds.initialize(this); @@ -204,15 +208,13 @@ private void observeViewModel() { } else { edgeToEdgeDelegate.applyEdgeToEdge(mBinding.container); } - }); - mainViewModel.getDefaultNavDestination().observe(this, startFragmentId -> { NavHostFragment navHostFragment = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment_activity_main); if (navHostFragment != null) { navController = navHostFragment.getNavController(); NavGraph navGraph = navController.getNavInflater().inflate(R.navigation.mobile_navigation); - navGraph.setStartDestination(startFragmentId); + navGraph.setStartDestination(uiState.getDefaultNavDestination()); navController.setGraph(navGraph); if (mBinding.navView instanceof BottomNavigationView bottomNav) { @@ -228,10 +230,8 @@ private void observeViewModel() { bottomSheet.show(getSupportFragmentManager(), bottomSheet.getTag()); }); } - }); - mainViewModel.getThemeChanged().observe(this, changed -> { - if (Boolean.TRUE.equals(changed)) { + if (uiState.isThemeChanged()) { recreate(); } }); diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainUiState.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainUiState.java new file mode 100644 index 00000000..db298efa --- /dev/null +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainUiState.java @@ -0,0 +1,30 @@ +package com.d4rk.androidtutorials.java.ui.screens.main; + +/** + * UI state for {@link MainActivity}. Holds values related to the main screen such as + * bottom navigation visibility, the default navigation destination, and whether the theme + * has changed requiring a recreation of the activity. + */ +public class MainUiState { + private final int bottomNavVisibility; + private final int defaultNavDestination; + private final boolean themeChanged; + + public MainUiState(int bottomNavVisibility, int defaultNavDestination, boolean themeChanged) { + this.bottomNavVisibility = bottomNavVisibility; + this.defaultNavDestination = defaultNavDestination; + this.themeChanged = themeChanged; + } + + public int getBottomNavVisibility() { + return bottomNavVisibility; + } + + public int getDefaultNavDestination() { + return defaultNavDestination; + } + + public boolean isThemeChanged() { + return themeChanged; + } +} diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainViewModel.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainViewModel.java index 5128ed99..eb9741e9 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainViewModel.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainViewModel.java @@ -38,9 +38,7 @@ public class MainViewModel extends ViewModel { private final IsAppInstalledUseCase isAppInstalledUseCase; private final BuildShortcutIntentUseCase buildShortcutIntentUseCase; private final GetAppUpdateManagerUseCase getAppUpdateManagerUseCase; - private final MutableLiveData bottomNavLabelVisibility = new MutableLiveData<>(); - private final MutableLiveData defaultNavDestination = new MutableLiveData<>(); - private final MutableLiveData themeChanged = new MutableLiveData<>(); + private final MutableLiveData uiState = new MutableLiveData<>(); @Inject public MainViewModel(ApplyThemeSettingsUseCase applyThemeSettingsUseCase, @@ -83,11 +81,9 @@ public void applySettings(String[] themeValues, String[] bottomNavBarLabelsValues, String[] defaultTabValues) { boolean changedTheme = applyThemeSettingsUseCase.invoke(themeValues); - themeChanged.setValue(changedTheme); String labelVisibilityStr = getBottomNavLabelVisibilityUseCase.invoke(); int visibilityMode = getVisibilityMode(labelVisibilityStr, bottomNavBarLabelsValues); - bottomNavLabelVisibility.setValue(visibilityMode); String startFragmentIdValue = getDefaultTabPreferenceUseCase.invoke(); int startFragmentId; @@ -100,7 +96,8 @@ public void applySettings(String[] themeValues, } else { startFragmentId = R.id.navigation_home; } - defaultNavDestination.setValue(startFragmentId); + + uiState.setValue(new MainUiState(visibilityMode, startFragmentId, changedTheme)); applyLanguageSettingsUseCase.invoke(); } @@ -133,24 +130,10 @@ public Intent getShortcutIntent(boolean isInstalled) { } /** - * Expose the bottom nav visibility as LiveData, so MainActivity can observe it. - */ - public LiveData getBottomNavVisibility() { - return bottomNavLabelVisibility; - } - - /** - * Expose the default nav destination as LiveData, so MainActivity can observe it. - */ - public LiveData getDefaultNavDestination() { - return defaultNavDestination; - } - - /** - * This tells the UI whether the theme changed so it can decide to recreate if necessary. + * Expose the consolidated UI state so MainActivity can observe it. */ - public LiveData getThemeChanged() { - return themeChanged; + public LiveData getUiState() { + return uiState; } /** From 43bd81712ddd28909c648569c699d3a75acf2a7d Mon Sep 17 00:00:00 2001 From: Mihai-Cristian Condrea Date: Sun, 31 Aug 2025 16:07:55 +0300 Subject: [PATCH 2/2] Annotate bottom nav visibility for lint --- .../androidtutorials/java/ui/screens/main/MainUiState.java | 6 +++++- .../java/ui/screens/main/MainViewModel.java | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainUiState.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainUiState.java index db298efa..cf9d9074 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainUiState.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainUiState.java @@ -1,21 +1,25 @@ package com.d4rk.androidtutorials.java.ui.screens.main; +import com.google.android.material.navigation.NavigationBarView; + /** * UI state for {@link MainActivity}. Holds values related to the main screen such as * bottom navigation visibility, the default navigation destination, and whether the theme * has changed requiring a recreation of the activity. */ public class MainUiState { + @NavigationBarView.LabelVisibility private final int bottomNavVisibility; private final int defaultNavDestination; private final boolean themeChanged; - public MainUiState(int bottomNavVisibility, int defaultNavDestination, boolean themeChanged) { + public MainUiState(@NavigationBarView.LabelVisibility int bottomNavVisibility, int defaultNavDestination, boolean themeChanged) { this.bottomNavVisibility = bottomNavVisibility; this.defaultNavDestination = defaultNavDestination; this.themeChanged = themeChanged; } + @NavigationBarView.LabelVisibility public int getBottomNavVisibility() { return bottomNavVisibility; } diff --git a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainViewModel.java b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainViewModel.java index eb9741e9..d7cff5a2 100644 --- a/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainViewModel.java +++ b/app/src/main/java/com/d4rk/androidtutorials/java/ui/screens/main/MainViewModel.java @@ -61,8 +61,8 @@ public MainViewModel(ApplyThemeSettingsUseCase applyThemeSettingsUseCase, this.getAppUpdateManagerUseCase = getAppUpdateManagerUseCase; } - private static int getVisibilityMode(String labelVisibilityStr, String[] bottomNavBarLabelsValues) { - int visibilityMode = NavigationBarView.LABEL_VISIBILITY_AUTO; + private static @NavigationBarView.LabelVisibility int getVisibilityMode(String labelVisibilityStr, String[] bottomNavBarLabelsValues) { + @NavigationBarView.LabelVisibility int visibilityMode = NavigationBarView.LABEL_VISIBILITY_AUTO; if (labelVisibilityStr.equals(bottomNavBarLabelsValues[0])) { visibilityMode = NavigationBarView.LABEL_VISIBILITY_LABELED; } else if (labelVisibilityStr.equals(bottomNavBarLabelsValues[1])) { @@ -83,7 +83,7 @@ public void applySettings(String[] themeValues, boolean changedTheme = applyThemeSettingsUseCase.invoke(themeValues); String labelVisibilityStr = getBottomNavLabelVisibilityUseCase.invoke(); - int visibilityMode = getVisibilityMode(labelVisibilityStr, bottomNavBarLabelsValues); + @NavigationBarView.LabelVisibility int visibilityMode = getVisibilityMode(labelVisibilityStr, bottomNavBarLabelsValues); String startFragmentIdValue = getDefaultTabPreferenceUseCase.invoke(); int startFragmentId;