Skip to content

Commit

Permalink
- Feat: Add Floating Reader Bottom Bar Option
Browse files Browse the repository at this point in the history
This commit adds a new setting to enable a floating bottom bar in the reader.

- A new setting is added to the reader settings and global settings to toggle the floating bottom bar.
- The bottom bar is now displayed as a floating bar when the setting is enabled.
- The floating bar is hidden by default and can be expanded by tapping on a small icon.
- The bar contains buttons for page selection, settings, chapter change, and chapter list.
  • Loading branch information
jacobrein committed Oct 21, 2024
1 parent 97af2d2 commit 6c93b6a
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ import androidx.compose.ui.util.fastForEach
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.core.app.ActivityOptionsCompat
import androidx.lifecycle.compose.LifecycleResumeEffect
import com.programmersbox.uiviews.ChangingSettingsRepository
import com.programmersbox.uiviews.GridChoice
import com.programmersbox.uiviews.R
Expand Down Expand Up @@ -496,13 +497,10 @@ fun ManagedActivityResultLauncher<Intent, ActivityResult>.launchCatching(
fun HideSystemBarsWhileOnScreen() {
val changingSettingsRepository: ChangingSettingsRepository = koinInject()

LifecycleHandle(
onStop = { changingSettingsRepository.showNavBar.tryEmit(true) },
onDestroy = { changingSettingsRepository.showNavBar.tryEmit(true) },
onCreate = { changingSettingsRepository.showNavBar.tryEmit(false) },
onStart = { changingSettingsRepository.showNavBar.tryEmit(false) },
onResume = { changingSettingsRepository.showNavBar.tryEmit(false) }
)
LifecycleResumeEffect(Unit) {
changingSettingsRepository.showNavBar.tryEmit(false)
onPauseOrDispose { changingSettingsRepository.showNavBar.tryEmit(true) }
}
}

@OptIn(ExperimentalMaterial3Api::class)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.LibraryBooks
import androidx.compose.material.icons.filled.BorderBottom
import androidx.compose.material.icons.filled.Favorite
import androidx.compose.material.icons.filled.FavoriteBorder
import androidx.compose.material.icons.filled.Gesture
Expand Down Expand Up @@ -385,6 +386,14 @@ class GenericManga(
settingTitle = { Text("Allow User Gestures for Chapter List in Reader") },
settingIcon = { Icon(Icons.Default.Gesture, null, modifier = Modifier.fillMaxSize()) }
)

var useFloatingBottomBar by mangaSettingsHandling.rememberUseFloatingReaderBottomBar()
SwitchSetting(
value = useFloatingBottomBar,
updateValue = { useFloatingBottomBar = it },
settingTitle = { Text("Use a Floating Bottom Bar in Reader") },
settingIcon = { Icon(Icons.Default.BorderBottom, null, modifier = Modifier.fillMaxSize()) }
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ class MangaSettingsHandling(context: Context) {
defaultValue = true
)

@Composable
fun rememberUseFloatingReaderBottomBar() = preferences.rememberPreference(
key = { it.useFloatingReaderBottomBar },
update = { setUseFloatingReaderBottomBar(it) },
defaultValue = true
)

inner class SettingInfo<T>(
val flow: Flow<T>,
private val updateValue: suspend MangaSettings.Builder.(T) -> MangaSettings.Builder,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,22 @@ import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.shrinkHorizontally
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth
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.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.ArrowForward
import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.filled.GridOn
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Numbers
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.BottomAppBar
Expand All @@ -22,10 +33,16 @@ import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.FloatingAppBarDefaults
import androidx.compose.material3.FloatingAppBarScrollBehavior
import androidx.compose.material3.HorizontalFloatingAppBar
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedIconButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBarDefaults
Expand Down Expand Up @@ -172,6 +189,87 @@ internal fun ReaderTopBar(
)
}

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
internal fun FloatingBottomBar(
vm: ReadViewModel,
onPageSelectClick: () -> Unit,
onSettingsClick: () -> Unit,
chapterChange: () -> Unit,
onChapterShow: () -> Unit,
modifier: Modifier = Modifier,
exitAlwaysScrollBehavior: FloatingAppBarScrollBehavior? = null,
) {
Box(
modifier = Modifier.fillMaxWidth()
) {
var showFloatBar by remember { mutableStateOf(false) }
HorizontalFloatingAppBar(
modifier = modifier
.align(Alignment.BottomEnd)
.windowInsetsPadding(WindowInsets.navigationBars)
.offset(y = -FloatingAppBarDefaults.ScreenOffset),
expanded = showFloatBar,
leadingContent = {
val prevShown = vm.currentChapter < vm.list.lastIndex
val nextShown = vm.currentChapter > 0

AnimatedVisibility(
visible = prevShown && vm.list.size > 1,
enter = expandHorizontally(expandFrom = Alignment.Start),
exit = shrinkHorizontally(shrinkTowards = Alignment.Start)
) {
PreviousIconButton(
previousChapter = chapterChange,
vm = vm,
)
}

GoBackIconButton()

AnimatedVisibility(
visible = nextShown && vm.list.size > 1,
enter = expandHorizontally(),
exit = shrinkHorizontally()
) {
NextIconButton(
nextChapter = chapterChange,
vm = vm,
)
}

IconButton(
onClick = onPageSelectClick,
) { Icon(Icons.Default.GridOn, null) }

IconButton(
onClick = onChapterShow,
) { Icon(Icons.Default.Numbers, null) }
},
trailingContent = {
IconButton(
onClick = onSettingsClick,
//modifier = Modifier.weight(1f)
) { Icon(Icons.Default.Settings, null) }
},
scrollBehavior = exitAlwaysScrollBehavior,
content = {
FilledIconButton(
onClick = { showFloatBar = !showFloatBar },
modifier = Modifier
.width(64.dp)
) {
Icon(
if (showFloatBar) Icons.Default.ChevronRight else Icons.Default.ChevronLeft,
contentDescription = "Localized description"
)
}
},
)
}
}

@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
internal fun BottomBar(
vm: ReadViewModel,
Expand Down Expand Up @@ -299,3 +397,37 @@ private fun PreviousButton(
modifier = modifier
) { Text(stringResource(id = R.string.loadPreviousChapter)) }
}

@Composable
private fun PreviousIconButton(
vm: ReadViewModel,
modifier: Modifier = Modifier,
previousChapter: () -> Unit,
) {
IconButton(
onClick = { vm.addChapterToWatched(++vm.currentChapter, previousChapter) },
modifier = modifier
) { Icon(Icons.Default.ArrowBack, null) }
}

@Composable
private fun GoBackIconButton(modifier: Modifier = Modifier) {
val navController = LocalNavController.current
OutlinedIconButton(
onClick = { navController.popBackStack() },
modifier = modifier,
border = BorderStroke(ButtonDefaults.outlinedButtonBorder(true).width, MaterialTheme.colorScheme.primary)
) { Icon(Icons.Default.Home, null) }
}

@Composable
private fun NextIconButton(
vm: ReadViewModel,
modifier: Modifier = Modifier,
nextChapter: () -> Unit,
) {
FilledIconButton(
onClick = { vm.addChapterToWatched(--vm.currentChapter, nextChapter) },
modifier = modifier
) { Icon(Icons.Default.ArrowForward, null) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FloatingAppBarDefaults
import androidx.compose.material3.FloatingAppBarExitDirection
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.ModalDrawerSheet
Expand All @@ -44,6 +46,7 @@ import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Dp
Expand Down Expand Up @@ -190,6 +193,10 @@ fun ReadView(
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
var showBottomSheet by remember { mutableStateOf(false) }

val floatingBottomBar by mangaSettingsHandling.rememberUseFloatingReaderBottomBar()

val exitAlwaysScrollBehavior = FloatingAppBarDefaults.exitAlwaysScrollBehavior(exitDirection = FloatingAppBarExitDirection.Bottom)

BackHandler(drawerState.isOpen || showBottomSheet) {
scope.launch {
when {
Expand Down Expand Up @@ -274,25 +281,37 @@ fun ReadView(
}
},
bottomBar = {
AnimatedVisibility(
visible = showItems,
enter = slideInVertically { it } + fadeIn(),
exit = slideOutVertically { it } + fadeOut()
) {
BottomBar(
if (floatingBottomBar) {
FloatingBottomBar(
onPageSelectClick = { showBottomSheet = true },
onSettingsClick = { settingsPopup = true },
chapterChange = ::showToast,
onChapterShow = { scope.launch { drawerState.open() } },
vm = readVm,
showBlur = showBlur,
isAmoledMode = isAmoledMode,
modifier = if (showBlur) Modifier.hazeChild(hazeState, style = HazeMaterials.thin()) {
//progressive = HazeProgressive.verticalGradient(startIntensity = 0f, endIntensity = 1f)
} else Modifier
exitAlwaysScrollBehavior = exitAlwaysScrollBehavior
)
} else {
AnimatedVisibility(
visible = showItems,
enter = slideInVertically { it } + fadeIn(),
exit = slideOutVertically { it } + fadeOut()
) {
BottomBar(
onPageSelectClick = { showBottomSheet = true },
onSettingsClick = { settingsPopup = true },
chapterChange = ::showToast,
onChapterShow = { scope.launch { drawerState.open() } },
vm = readVm,
showBlur = showBlur,
isAmoledMode = isAmoledMode,
modifier = if (showBlur) Modifier.hazeChild(hazeState, style = HazeMaterials.thin()) {
//progressive = HazeProgressive.verticalGradient(startIntensity = 0f, endIntensity = 1f)
} else Modifier,
)
}
}
},
modifier = Modifier.nestedScroll(exitAlwaysScrollBehavior)
) { p ->
Box(
modifier = if (showBlur)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.BatteryAlert
import androidx.compose.material.icons.filled.BorderBottom
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.FormatLineSpacing
Expand Down Expand Up @@ -141,6 +142,14 @@ internal fun SettingsSheet(
settingIcon = { Icon(Icons.Default.Gesture, null, modifier = Modifier.fillMaxSize()) }
)

var useFloatingBottomBar by mangaSettingsHandling.rememberUseFloatingReaderBottomBar()
SwitchSetting(
value = useFloatingBottomBar,
updateValue = { useFloatingBottomBar = it },
settingTitle = { Text("Use a Floating Bottom Bar in Reader") },
settingIcon = { Icon(Icons.Default.BorderBottom, null, modifier = Modifier.fillMaxSize()) }
)

HorizontalDivider()

var showReaderTypeDropdown by remember { mutableStateOf(false) }
Expand Down
1 change: 1 addition & 0 deletions mangaworld/src/main/proto/manga_settings.proto
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ message MangaSettings {
ImageLoaderType imageLoaderType = 6;
bool useFlipPager = 7;
bool allowUserDrawerGesture = 8;
bool useFloatingReaderBottomBar = 9;
}

enum PlayingStartAction {
Expand Down

0 comments on commit 6c93b6a

Please sign in to comment.