Skip to content

Commit

Permalink
Merge pull request #727 from Orange-OpenSource/19-ods-module-about
Browse files Browse the repository at this point in the history
19 - ODS About module
  • Loading branch information
florentmaitre authored Dec 6, 2023
2 parents b586833 + 7539095 commit f4b0b9d
Show file tree
Hide file tree
Showing 115 changed files with 2,700 additions and 654 deletions.
3 changes: 3 additions & 0 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ dependencies {
implementation(project(":lib"))
implementation(project(":lib-xml"))
implementation(project(":theme-innovation-cup"))
implementation(project(":module-about"))

implementation(Dependencies.accompanistSystemUiController)
implementation(Dependencies.activityCompose)
Expand Down
8 changes: 8 additions & 0 deletions app/src/main/java/com/orange/ods/app/OdsApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,24 @@ import android.app.Application
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import dagger.hilt.android.HiltAndroidApp
import timber.log.Timber

@HiltAndroidApp
class OdsApplication : Application() {

override fun onCreate() {
super.onCreate()
initializeCrashlytics()
initializeLogger()
}

private fun initializeCrashlytics() {
Firebase.crashlytics.setCrashlyticsCollectionEnabled(!BuildConfig.DEBUG)
}

private fun initializeLogger() {
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import android.content.Context
import com.orange.ods.app.R
import com.orange.ods.app.domain.recipes.Recipe
import com.orange.ods.app.domain.recipes.RecipesRepository
import com.orange.ods.app.ui.utilities.extension.contentAsString
import com.orange.ods.extension.contentAsString
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import timber.log.Timber
Expand Down
84 changes: 31 additions & 53 deletions app/src/main/java/com/orange/ods/app/ui/AppBarState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@

package com.orange.ods.app.ui

import android.os.Bundle
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.runtime.Composable
Expand All @@ -22,28 +21,23 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.compose.currentBackStackEntryAsState
import com.orange.ods.app.R
import com.orange.ods.app.domain.recipes.LocalRecipes
import com.orange.ods.app.ui.AppBarAction.Companion.defaultActions
import com.orange.ods.app.ui.AppBarState.Companion.CustomDefaultConfiguration
import com.orange.ods.app.ui.components.appbars.top.TopAppBarCustomizationState
import com.orange.ods.app.ui.components.utilities.clickOnElement
import com.orange.ods.app.ui.utilities.NavigationItem
import com.orange.ods.compose.component.appbar.top.OdsTopAppBar
import com.orange.ods.compose.component.content.OdsComponentContent
import com.orange.ods.compose.component.menu.OdsDropdownMenu
import com.orange.ods.extension.orElse
import kotlin.math.max

val LocalAppBarManager = staticCompositionLocalOf<AppBarManager> { error("CompositionLocal AppBarManager not present") }

interface AppBarManager {
val searchedText: TextFieldValue

fun setCustomAppBar(appBarConfiguration: AppBarConfiguration)
fun setCustomAppBar(customAppBarConfiguration: CustomAppBarConfiguration)

fun updateAppBarTabs(tabsConfiguration: TabsConfiguration)
fun clearAppBarTabs()
Expand All @@ -52,59 +46,36 @@ interface AppBarManager {
/**
* AppBar state source of truth.
*
* The app bar is managed according to the [Screen] displayed except when its type is [ScreenType.WithCustomizableTopAppBar]. In this case, the app bar is
* displayed according to the provided [AppBarConfiguration].
* The app bar is managed according to the [Screen] displayed except if it has a custom app bar or if the screen cannot be found in the app [Screen]s (for
* example an about module screen). In this case, the app bar is displayed according to the provided [CustomAppBarConfiguration].
*/
class AppBarState(
private val navController: NavController,
private val navigationState: AppNavigationState,
private val searchText: MutableState<TextFieldValue>,
private val customAppBarConfiguration: MutableState<AppBarConfiguration>,
private val customAppBarConfiguration: MutableState<CustomAppBarConfiguration>,
val tabsState: AppBarTabsState
) : AppBarManager {

companion object {
val CustomDefaultConfiguration = AppBarConfiguration(
isLarge = false,
largeTitleRes = R.string.empty,
scrollBehavior = TopAppBarCustomizationState.ScrollBehavior.Collapsible,
isNavigationIconEnabled = true,
actionCount = defaultActions.size,
isOverflowMenuEnabled = false
)
}

private val currentBackStackEntry: NavBackStackEntry?
@Composable get() = navController.currentBackStackEntryAsState().value

private val currentScreenRoute: String?
@Composable get() = currentBackStackEntry?.destination?.route

private val currentScreenRouteArgs: Bundle?
@Composable get() = currentBackStackEntry?.arguments

private val currentScreen: Screen?
@Composable get() = currentScreenRoute?.let { getScreen(it, currentScreenRouteArgs) }

private val isCustom: Boolean
@Composable get() = currentScreen?.hasCustomAppBar.orElse { false }
@Composable get() = navigationState.currentScreen?.hasCustomAppBar != false

private val showNavigationIcon: Boolean
@Composable get() = (isCustom && customAppBarConfiguration.value.isNavigationIconEnabled)
|| (!isCustom && currentScreen?.isHome == false)
|| (!isCustom && navigationState.currentScreen?.isHome(navigationState.previousRoute) == false)

val isLarge: Boolean
@Composable get() = currentScreen?.isLargeAppBar == true
@Composable get() = navigationState.currentScreen?.isLargeAppBar == true

val title: String
@Composable get() = if (isCustom) {
stringResource(id = customAppBarConfiguration.value.largeTitleRes)
customAppBarConfiguration.value.title
} else {
currentScreen?.title?.asString().orEmpty()
navigationState.currentScreen?.title?.asString().orEmpty()
}

val actions: List<OdsComponentContent<Nothing>>
@Composable get() {
val screenAppBarActions = currentScreen?.getAppBarActions { searchText.value = it }.orEmpty()
val screenAppBarActions = navigationState.currentScreen?.getAppBarActions(navigationState.previousRoute) { searchText.value = it }.orEmpty()
return if (isCustom) {
val context = LocalContext.current
val customActionCount = max(0, customAppBarConfiguration.value.actionCount - AppBarAction.defaultActions.size)
Expand Down Expand Up @@ -152,8 +123,8 @@ class AppBarState(
override val searchedText: TextFieldValue
get() = searchText.value

override fun setCustomAppBar(appBarConfiguration: AppBarConfiguration) {
customAppBarConfiguration.value = appBarConfiguration
override fun setCustomAppBar(customAppBarConfiguration: CustomAppBarConfiguration) {
this.customAppBarConfiguration.value = customAppBarConfiguration
}

override fun updateAppBarTabs(tabsConfiguration: TabsConfiguration) {
Expand All @@ -168,19 +139,26 @@ class AppBarState(

@Composable
fun rememberAppBarState(
navController: NavController,
navigationState: AppNavigationState,
searchedText: MutableState<TextFieldValue> = remember { mutableStateOf(TextFieldValue("")) },
customAppBarConfiguration: MutableState<AppBarConfiguration> = remember { mutableStateOf(CustomDefaultConfiguration) },
customAppBarConfiguration: MutableState<CustomAppBarConfiguration> = remember { mutableStateOf(CustomAppBarConfiguration.Default) },
tabsState: AppBarTabsState = rememberAppBarTabsState()
) = remember(navController, searchedText, customAppBarConfiguration, tabsState) {
AppBarState(navController, searchedText, customAppBarConfiguration, tabsState)
) = remember(navigationState, searchedText, customAppBarConfiguration, tabsState) {
AppBarState(navigationState, searchedText, customAppBarConfiguration, tabsState)
}

data class AppBarConfiguration constructor(
val isLarge: Boolean,
val largeTitleRes: Int,
val scrollBehavior: TopAppBarCustomizationState.ScrollBehavior,
val isNavigationIconEnabled: Boolean,
data class CustomAppBarConfiguration constructor(
val title: String,
val actionCount: Int,
val isOverflowMenuEnabled: Boolean
)
val isNavigationIconEnabled: Boolean = true,
val isLarge: Boolean = false,
val scrollBehavior: TopAppBarCustomizationState.ScrollBehavior = TopAppBarCustomizationState.ScrollBehavior.Collapsible,
val isOverflowMenuEnabled: Boolean = false
) {
companion object {
val Default = CustomAppBarConfiguration(
title = "",
actionCount = defaultActions.size,
)
}
}
12 changes: 7 additions & 5 deletions app/src/main/java/com/orange/ods/app/ui/AppNavGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@
package com.orange.ods.app.ui

import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.composable
import com.orange.ods.app.ui.about.addAboutGraph
import com.orange.ods.app.ui.components.addComponentsGraph
import com.orange.ods.app.ui.guidelines.addGuidelinesGraph
import com.orange.ods.app.ui.modules.addModulesGraph
import com.orange.ods.app.ui.search.SearchScreen

/**
Expand All @@ -29,14 +30,15 @@ object MainNavigation {
* Navigation graph of the application.
*/
fun NavGraphBuilder.appNavGraph(
navController: NavController,
navigateToElement: (String, Long?, NavBackStackEntry) -> Unit,
upPress: () -> Unit
navigateToAboutModule: () -> Unit,
) {
addBottomBarGraph(navigateToElement)
addBottomBarGraph(navController)

addGuidelinesGraph()
addComponentsGraph(navigateToElement, upPress)
addAboutGraph()
addComponentsGraph(navController)
addModulesGraph(navigateToAboutModule)

composable(
route = MainNavigation.SearchRoute
Expand Down
100 changes: 100 additions & 0 deletions app/src/main/java/com/orange/ods/app/ui/AppNavigation.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
*
* Copyright 2021 Orange
*
* Use of this source code is governed by an MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
* /
*/

package com.orange.ods.app.ui

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.lifecycle.Lifecycle
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.NavGraph
import androidx.navigation.NavHostController
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController

@Composable
fun rememberAppNavigationState(navController: NavHostController = rememberNavController()) = remember(navController) { AppNavigationState(navController) }

class AppNavigationState(val navController: NavHostController) {
val previousRoute: String?
get() = navController.previousBackStackEntry?.destination?.route

val currentRoute: String?
get() = navController.currentDestination?.route

val currentScreen: Screen?
@Composable get() {
val routeArgs = navController.currentBackStackEntryAsState().value?.arguments
return currentRoute?.let { getScreen(it, routeArgs) }
}

fun navigateToBottomBarRoute(route: String) {
if (route != currentRoute) {
navController.navigateToBottomBarRoute(route)
}
}

fun navigateToElement(route: String, elementId: Long?, from: NavBackStackEntry) {
// In order to discard duplicated navigation events, we check the Lifecycle
if (from.lifecycleIsResumed()) {
val fullRoute = if (elementId != null) "$route/$elementId" else route
navController.navigate(fullRoute)
}
}

fun upPress() {
navController.navigateUp()
}
}

fun NavController.navigateToElement(route: String, elementId: Long?, from: NavBackStackEntry) {
// In order to discard duplicated navigation events, we check the Lifecycle
if (from.lifecycleIsResumed()) {
val fullRoute = if (elementId != null) "$route/$elementId" else route
navigate(fullRoute)
}
}

fun NavController.navigateToBottomBarRoute(route: String) {
navigate(route) {
// Avoid multiple copies of the same destination when
// reselecting the same item
launchSingleTop = true
// Restore state when reselecting a previously selected item
restoreState = true
// Pop up backstack to the first destination and save state. This makes going back
// to the start destination when pressing back in any other bottom tab.
popUpTo(findStartDestination(graph).id) {
saveState = true
}
}
}

/**
* If the lifecycle is not resumed it means this NavBackStackEntry already processed a nav event.
*
* This is used to de-duplicate navigation events.
*/
private fun NavBackStackEntry.lifecycleIsResumed() =
this.getLifecycle().currentState == Lifecycle.State.RESUMED

private val NavGraph.startDestination: NavDestination?
get() = findNode(startDestinationId)

/**
* Copied from similar function in NavigationUI.kt
*
* https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:navigation/navigation-ui/src/main/java/androidx/navigation/ui/NavigationUI.kt
*/
private tailrec fun findStartDestination(graph: NavDestination): NavDestination {
return if (graph is NavGraph) findStartDestination(graph.startDestination!!) else graph
}
Loading

0 comments on commit f4b0b9d

Please sign in to comment.