From ad73c29a440b75fa9fbf1f11b1e4b6cd8592432d Mon Sep 17 00:00:00 2001 From: jacobrein Date: Fri, 18 Oct 2024 09:35:13 -0600 Subject: [PATCH] - Refactor: Extract Multiple Actions Logic This commit refactors the logic for handling multiple actions in the bottom navigation bar by extracting it into a separate composable function called `MultipleActions`. This change improves code organization and readability by isolating the multiple actions functionality into a dedicated component. It also introduces a new state class, `MultipleBarState`, to manage the state of the multiple actions bar, further enhancing modularity and maintainability. --- .../uiviews/BaseMainActivity.kt | 175 +++--------------- .../utils/components/MultipleActions.kt | 128 +++++++++++++ 2 files changed, 158 insertions(+), 145 deletions(-) create mode 100644 UIViews/src/main/java/com/programmersbox/uiviews/utils/components/MultipleActions.kt diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/BaseMainActivity.kt b/UIViews/src/main/java/com/programmersbox/uiviews/BaseMainActivity.kt index 7056478bf..9cfa34847 100644 --- a/UIViews/src/main/java/com/programmersbox/uiviews/BaseMainActivity.kt +++ b/UIViews/src/main/java/com/programmersbox/uiviews/BaseMainActivity.kt @@ -47,9 +47,7 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBars -import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.selectableGroup @@ -64,8 +62,6 @@ import androidx.compose.material.icons.filled.Notifications import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.Star -import androidx.compose.material.icons.filled.UnfoldLess -import androidx.compose.material.icons.filled.UnfoldMore import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.Settings import androidx.compose.material3.Badge @@ -74,9 +70,6 @@ import androidx.compose.material3.BottomAppBarScrollBehavior import androidx.compose.material3.BottomAppBarState import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi -import androidx.compose.material3.FilledIconButton -import androidx.compose.material3.FloatingAppBarDefaults -import androidx.compose.material3.HorizontalFloatingAppBar import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -99,11 +92,9 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -175,10 +166,11 @@ import com.programmersbox.uiviews.utils.SettingsHandling import com.programmersbox.uiviews.utils.appVersion import com.programmersbox.uiviews.utils.chromeCustomTabs import com.programmersbox.uiviews.utils.components.HazeScaffold +import com.programmersbox.uiviews.utils.components.MultipleActions +import com.programmersbox.uiviews.utils.components.rememberMultipleBarState import com.programmersbox.uiviews.utils.currentDetailsUrl import com.programmersbox.uiviews.utils.currentService import com.programmersbox.uiviews.utils.customsettings.ScreenBottomItem -import com.programmersbox.uiviews.utils.customsettings.item import com.programmersbox.uiviews.utils.dispatchIo import com.programmersbox.uiviews.utils.rememberFloatingNavigation import com.programmersbox.uiviews.utils.sharedelements.LocalSharedElementScope @@ -186,7 +178,6 @@ import com.programmersbox.uiviews.utils.sharedelements.animatedScopeComposable import dev.chrisbanes.haze.HazeState import dev.chrisbanes.haze.hazeChild import dev.chrisbanes.haze.materials.HazeMaterials -import kotlinx.coroutines.delay import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.launchIn @@ -448,16 +439,7 @@ abstract class BaseMainActivity : AppCompatActivity() { Modifier } - var showHorizontalBar by remember { mutableStateOf(false) } - var expanded by remember { mutableStateOf(false) } - - val closeMultipleBar: () -> Unit = { - scope.launch { - expanded = false - delay(250) - showHorizontalBar = false - } - } + val multipleBarState = rememberMultipleBarState() Box(modifier = Modifier.fillMaxWidth()) { FloatingNavigationBar( @@ -520,10 +502,12 @@ abstract class BaseMainActivity : AppCompatActivity() { navController = navController, colors = colors, multipleClick = { - if (showHorizontalBar) { - closeMultipleBar() - } else { - showHorizontalBar = true + scope.launch { + if (multipleBarState.showHorizontalBar) { + multipleBarState.hide() + } else { + multipleBarState.show() + } } } ) @@ -536,63 +520,17 @@ abstract class BaseMainActivity : AppCompatActivity() { ) } - if (middleNavItem == MiddleNavigationAction.Multiple) { - AnimatedVisibility( - visible = showHorizontalBar, - enter = slideInVertically( - animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() - ) { it / 2 } + fadeIn( - animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() - ), - exit = slideOutVertically( - animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() - ) { it / 2 } + fadeOut( - animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() - ), - modifier = Modifier - .align(Alignment.BottomCenter) - .windowInsetsPadding(WindowInsets.navigationBars) - .offset(y = -FloatingAppBarDefaults.ScreenOffset), - ) { - LaunchedEffect(Unit) { - delay(250) - expanded = true - } - - HorizontalFloatingAppBar( - expanded = expanded, - leadingContent = { - multipleActions.startAction.item?.ScreenBottomItem( - currentDestination = currentDestination, - navController = navController, - additionalOnClick = closeMultipleBar - ) - }, - trailingContent = { - multipleActions.endAction.item?.ScreenBottomItem( - currentDestination = currentDestination, - navController = navController, - additionalOnClick = closeMultipleBar - ) - }, - ) { - FilledIconButton( - modifier = Modifier.width(64.dp), - onClick = closeMultipleBar - ) { - Icon( - if (expanded) Icons.Default.UnfoldLess else Icons.Filled.UnfoldMore, - contentDescription = "Localized description" - ) - } - } - } - } + MultipleActions( + state = multipleBarState, + middleNavItem = middleNavItem, + multipleActions = multipleActions, + currentDestination = currentDestination, + navController = navController + ) } } } - @OptIn(ExperimentalMaterial3ExpressiveApi::class) @Composable private fun BottomNav( navController: NavHostController, @@ -605,17 +543,7 @@ abstract class BaseMainActivity : AppCompatActivity() { multipleActions: MiddleMultipleActions, ) { val scope = rememberCoroutineScope() - - var showHorizontalBar by remember { mutableStateOf(false) } - var expanded by remember { mutableStateOf(false) } - - val closeMultipleBar: () -> Unit = { - scope.launch { - expanded = false - delay(250) - showHorizontalBar = false - } - } + val multipleBarState = rememberMultipleBarState() Box { Column { BottomBarAdditions() @@ -664,10 +592,12 @@ abstract class BaseMainActivity : AppCompatActivity() { currentDestination = currentDestination, navController = navController, multipleClick = { - if (showHorizontalBar) { - closeMultipleBar() - } else { - showHorizontalBar = true + scope.launch { + if (multipleBarState.showHorizontalBar) { + multipleBarState.hide() + } else { + multipleBarState.show() + } } } ) @@ -682,58 +612,13 @@ abstract class BaseMainActivity : AppCompatActivity() { } } - if (middleNavItem == MiddleNavigationAction.Multiple) { - AnimatedVisibility( - visible = showHorizontalBar, - enter = slideInVertically( - animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() - ) { it / 2 } + fadeIn( - animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() - ), - exit = slideOutVertically( - animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() - ) { it / 2 } + fadeOut( - animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() - ), - modifier = Modifier - .align(Alignment.BottomCenter) - .windowInsetsPadding(WindowInsets.navigationBars) - .offset(y = -FloatingAppBarDefaults.ScreenOffset), - ) { - LaunchedEffect(Unit) { - delay(250) - expanded = true - } - - HorizontalFloatingAppBar( - expanded = expanded, - leadingContent = { - multipleActions.startAction.item?.ScreenBottomItem( - currentDestination = currentDestination, - navController = navController, - additionalOnClick = closeMultipleBar - ) - }, - trailingContent = { - multipleActions.endAction.item?.ScreenBottomItem( - currentDestination = currentDestination, - navController = navController, - additionalOnClick = closeMultipleBar - ) - }, - ) { - FilledIconButton( - modifier = Modifier.width(64.dp), - onClick = closeMultipleBar - ) { - Icon( - if (expanded) Icons.Default.UnfoldLess else Icons.Filled.UnfoldMore, - contentDescription = "Localized description" - ) - } - } - } - } + MultipleActions( + state = multipleBarState, + middleNavItem = middleNavItem, + multipleActions = multipleActions, + currentDestination = currentDestination, + navController = navController + ) } } diff --git a/UIViews/src/main/java/com/programmersbox/uiviews/utils/components/MultipleActions.kt b/UIViews/src/main/java/com/programmersbox/uiviews/utils/components/MultipleActions.kt new file mode 100644 index 000000000..20e6766e2 --- /dev/null +++ b/UIViews/src/main/java/com/programmersbox/uiviews/utils/components/MultipleActions.kt @@ -0,0 +1,128 @@ +package com.programmersbox.uiviews.utils.components + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.navigationBars +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.windowInsetsPadding +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.UnfoldLess +import androidx.compose.material.icons.filled.UnfoldMore +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.FilledIconButton +import androidx.compose.material3.FloatingAppBarDefaults +import androidx.compose.material3.HorizontalFloatingAppBar +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import androidx.navigation.NavDestination +import androidx.navigation.NavHostController +import com.programmersbox.uiviews.MiddleMultipleActions +import com.programmersbox.uiviews.MiddleNavigationAction +import com.programmersbox.uiviews.utils.customsettings.ScreenBottomItem +import com.programmersbox.uiviews.utils.customsettings.item +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +@Composable +fun rememberMultipleBarState( + hideOnClick: Boolean = true, +) = remember(hideOnClick) { MultipleBarState(hideOnClick) } + +class MultipleBarState( + hideOnClick: Boolean = true, +) { + var expanded by mutableStateOf(false) + private set + var showHorizontalBar by mutableStateOf(false) + private set + + var hideOnClick by mutableStateOf(hideOnClick) + + suspend fun show() { + expanded = false + showHorizontalBar = true + delay(250) + expanded = true + } + + suspend fun hide() { + expanded = false + delay(250) + showHorizontalBar = false + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun BoxScope.MultipleActions( + state: MultipleBarState, + middleNavItem: MiddleNavigationAction, + multipleActions: MiddleMultipleActions, + currentDestination: NavDestination?, + navController: NavHostController, + modifier: Modifier = Modifier, +) { + val scope = rememberCoroutineScope() + if (middleNavItem == MiddleNavigationAction.Multiple) { + AnimatedVisibility( + visible = state.showHorizontalBar, + enter = slideInVertically( + animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() + ) { it / 2 } + fadeIn( + animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() + ), + exit = slideOutVertically( + animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() + ) { it / 2 } + fadeOut( + animationSpec = MaterialTheme.motionScheme.fastSpatialSpec() + ), + modifier = modifier + .align(Alignment.BottomCenter) + .windowInsetsPadding(WindowInsets.navigationBars) + .offset(y = -FloatingAppBarDefaults.ScreenOffset), + ) { + HorizontalFloatingAppBar( + expanded = state.expanded, + leadingContent = { + multipleActions.startAction.item?.ScreenBottomItem( + currentDestination = currentDestination, + navController = navController, + additionalOnClick = { scope.launch { if (state.hideOnClick) state.hide() } } + ) + }, + trailingContent = { + multipleActions.endAction.item?.ScreenBottomItem( + currentDestination = currentDestination, + navController = navController, + additionalOnClick = { scope.launch { if (state.hideOnClick) state.hide() } } + ) + }, + ) { + FilledIconButton( + modifier = Modifier.width(64.dp), + onClick = { scope.launch { state.hide() } } + ) { + Icon( + if (state.expanded) Icons.Default.UnfoldLess else Icons.Filled.UnfoldMore, + contentDescription = "Localized description" + ) + } + } + } + } +} \ No newline at end of file