Skip to content

Commit

Permalink
Merge pull request #119 from DroidKaigi/feature/navigation_design_rough
Browse files Browse the repository at this point in the history
Feature/navigation design rough
  • Loading branch information
takahirom authored Jul 15, 2024
2 parents 1fb8130 + 490c3fc commit 4a54b4d
Show file tree
Hide file tree
Showing 6 changed files with 349 additions and 66 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import io.github.droidkaigi.confsched.main.MainNestedGraphStateHolder
import io.github.droidkaigi.confsched.main.MainScreenTab
import io.github.droidkaigi.confsched.main.MainScreenTab.About
import io.github.droidkaigi.confsched.main.MainScreenTab.EventMap
import io.github.droidkaigi.confsched.main.MainScreenTab.Favorite
import io.github.droidkaigi.confsched.main.MainScreenTab.ProfileCard
import io.github.droidkaigi.confsched.main.MainScreenTab.Timetable
import io.github.droidkaigi.confsched.main.mainScreen
Expand Down Expand Up @@ -141,7 +142,8 @@ class KaigiAppMainNestedGraphStateHolder : MainNestedGraphStateHolder {
when (tab) {
Timetable -> mainNestedNavController.navigateTimetableScreen()
EventMap -> mainNestedNavController.navigateEventMapScreen()
About -> TODO()
Favorite -> {}
About -> {}
ProfileCard -> mainNestedNavController.navigateProfileCardScreen()
}
}
Expand All @@ -164,7 +166,6 @@ private class ExternalNavController(
private val context: Context,
private val shareNavigator: ShareNavigator,
) {

fun navigate(url: String) {
val uri: Uri = url.toUri()
val launched = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
Expand Down Expand Up @@ -216,10 +217,14 @@ private class ExternalNavController(

@Suppress("SwallowedException")
@RequiresApi(Build.VERSION_CODES.R)
private fun navigateToNativeAppApi30(context: Context, uri: Uri): Boolean {
val nativeAppIntent = Intent(Intent.ACTION_VIEW, uri)
.addCategory(Intent.CATEGORY_BROWSABLE)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER)
private fun navigateToNativeAppApi30(
context: Context,
uri: Uri,
): Boolean {
val nativeAppIntent =
Intent(Intent.ACTION_VIEW, uri)
.addCategory(Intent.CATEGORY_BROWSABLE)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER)
return try {
context.startActivity(nativeAppIntent)
true
Expand All @@ -229,7 +234,10 @@ private class ExternalNavController(
}

@SuppressLint("QueryPermissionsNeeded")
private fun navigateToNativeApp(context: Context, uri: Uri): Boolean {
private fun navigateToNativeApp(
context: Context,
uri: Uri,
): Boolean {
val pm = context.packageManager

// Get all Apps that resolve a generic url
Expand Down Expand Up @@ -264,7 +272,10 @@ private class ExternalNavController(
return true
}

private fun navigateToCustomTab(context: Context, uri: Uri) {
private fun navigateToCustomTab(
context: Context,
uri: Uri,
) {
CustomTabsIntent.Builder()
.setShowTitle(true)
.build()
Expand Down
1 change: 1 addition & 0 deletions feature/main/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ kotlin {
implementation(projects.core.model)
implementation(projects.core.designsystem)
implementation(projects.core.ui)
implementation(libs.haze)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.CalendarMonth
import androidx.compose.material.icons.filled.Info
import androidx.compose.material.icons.outlined.CalendarMonth
import androidx.compose.material.icons.outlined.Favorite
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material.icons.outlined.Map
import androidx.compose.material.icons.outlined.People
import androidx.compose.material3.Button
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHostState
Expand All @@ -29,29 +30,30 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import conference_app_2024.feature.main.generated.resources.Res
import conference_app_2024.feature.main.generated.resources.icon_achievement_fill
import conference_app_2024.feature.main.generated.resources.icon_achievement_outline
import conference_app_2024.feature.main.generated.resources.icon_map_fill
import dev.chrisbanes.haze.HazeState
import dev.chrisbanes.haze.HazeStyle
import dev.chrisbanes.haze.haze
import io.github.droidkaigi.confsched.compose.EventEmitter
import io.github.droidkaigi.confsched.compose.rememberEventEmitter
import io.github.droidkaigi.confsched.main.NavigationType.BottomNavigation
import io.github.droidkaigi.confsched.main.NavigationType.NavigationRail
import io.github.droidkaigi.confsched.main.section.GlassLikeBottomNavigation
import io.github.droidkaigi.confsched.main.strings.MainStrings
import io.github.droidkaigi.confsched.ui.SnackbarMessageEffect
import io.github.droidkaigi.confsched.ui.UserMessageStateHolder
import org.jetbrains.compose.resources.DrawableResource
import org.jetbrains.compose.resources.ExperimentalResourceApi

const val mainScreenRoute = "main"
const val MainScreenTestTag = "MainScreen"

fun NavGraphBuilder.mainScreen(
windowSize: WindowSizeClass,
Expand All @@ -69,12 +71,18 @@ fun NavGraphBuilder.mainScreen(

interface MainNestedGraphStateHolder {
val startDestination: String

fun routeToTab(route: String): MainScreenTab?
fun onTabSelected(mainNestedNavController: NavController, tab: MainScreenTab)

fun onTabSelected(
mainNestedNavController: NavController,
tab: MainScreenTab,
)
}

enum class NavigationType {
BottomNavigation, NavigationRail
BottomNavigation,
NavigationRail,
}

@Composable
Expand All @@ -87,12 +95,13 @@ fun MainScreen(
) {
val snackbarHostState = remember { SnackbarHostState() }

val navigationType: NavigationType = when (windowSize.widthSizeClass) {
WindowWidthSizeClass.Compact -> BottomNavigation
WindowWidthSizeClass.Medium -> NavigationRail
WindowWidthSizeClass.Expanded -> NavigationRail
else -> BottomNavigation
}
val navigationType: NavigationType =
when (windowSize.widthSizeClass) {
WindowWidthSizeClass.Compact -> BottomNavigation
WindowWidthSizeClass.Medium -> NavigationRail
WindowWidthSizeClass.Expanded -> NavigationRail
else -> BottomNavigation
}

SnackbarMessageEffect(
snackbarHostState = snackbarHostState,
Expand All @@ -116,23 +125,29 @@ sealed class IconRepresentation {
}

enum class MainScreenTab(
val icon: IconRepresentation,
val icon: IconRepresentation.Vector,
val selectedIcon: IconRepresentation,
val label: String,
val contentDescription: String,
val testTag: String = "mainScreenTab:$label",
) {
Timetable(
icon = IconRepresentation.Vector(Icons.Outlined.CalendarMonth),
selectedIcon = IconRepresentation.Vector(Icons.Filled.CalendarMonth),
selectedIcon = IconRepresentation.Vector(Icons.Outlined.CalendarMonth),
label = MainStrings.Timetable.asString(),
contentDescription = MainStrings.Timetable.asString(),
),

@OptIn(ExperimentalResourceApi::class)
EventMap(
icon = IconRepresentation.Vector(Icons.Outlined.Map),
selectedIcon = IconRepresentation.Drawable(drawableId = Res.drawable.icon_map_fill),
selectedIcon = IconRepresentation.Vector(Icons.Outlined.Map),
label = MainStrings.EventMap.asString(),
contentDescription = MainStrings.EventMap.asString(),
),

Favorite(
icon = IconRepresentation.Vector(Icons.Outlined.Favorite),
selectedIcon = IconRepresentation.Vector(Icons.Outlined.Favorite),
label = MainStrings.EventMap.asString(),
contentDescription = MainStrings.EventMap.asString(),
),
Expand All @@ -144,13 +159,19 @@ enum class MainScreenTab(
contentDescription = MainStrings.About.asString(),
),

@OptIn(ExperimentalResourceApi::class)
ProfileCard(
icon = IconRepresentation.Drawable(drawableId = Res.drawable.icon_achievement_outline),
selectedIcon = IconRepresentation.Drawable(drawableId = Res.drawable.icon_achievement_fill),
icon = IconRepresentation.Vector(Icons.Outlined.People),
selectedIcon = IconRepresentation.Vector(Icons.Outlined.People),
label = MainStrings.ProfileCard.asString(),
contentDescription = MainStrings.ProfileCard.asString(),
),
;

companion object {
val size: Int get() = values().size
fun indexOf(tab: MainScreenTab): Int = values().indexOf(tab)
fun fromIndex(index: Int): MainScreenTab = values()[index]
}
}

data class MainScreenUiState(
Expand Down Expand Up @@ -184,26 +205,32 @@ fun MainScreen(
}
}
}

val hazeState = remember { HazeState() }

Scaffold(
bottomBar = {
AnimatedVisibility(visible = navigationType == BottomNavigation) {
Row {
MainScreenTab.entries.forEach { tab ->
Button(
modifier = Modifier.weight(1F),
onClick = { onTabSelected(mainNestedNavController, tab) },
) {
Text(text = tab.label + " " + (currentTab == tab))
}
}
}
}
GlassLikeBottomNavigation(
hazeState = hazeState,
onTabSelected = {
onTabSelected(mainNestedNavController, it)
},
)
},
) { padding ->
val hazeStyle =
HazeStyle(
tint = Color.Black.copy(alpha = .2f),
blurRadius = 30.dp,
)
NavHost(
navController = mainNestedNavController,
startDestination = "timetable",
modifier = Modifier,
modifier =
Modifier.haze(
hazeState,
hazeStyle,
),
enterTransition = { materialFadeThroughIn() },
exitTransition = { materialFadeThroughOut() },
) {
Expand All @@ -213,25 +240,31 @@ fun MainScreen(
}
}

private fun materialFadeThroughIn(): EnterTransition = fadeIn(
animationSpec = tween(
durationMillis = 195,
delayMillis = 105,
easing = LinearOutSlowInEasing,
),
) + scaleIn(
animationSpec = tween(
durationMillis = 195,
delayMillis = 105,
easing = LinearOutSlowInEasing,
),
initialScale = 0.92f,
)
private fun materialFadeThroughIn(): EnterTransition =
fadeIn(
animationSpec =
tween(
durationMillis = 195,
delayMillis = 105,
easing = LinearOutSlowInEasing,
),
) +
scaleIn(
animationSpec =
tween(
durationMillis = 195,
delayMillis = 105,
easing = LinearOutSlowInEasing,
),
initialScale = 0.92f,
)

private fun materialFadeThroughOut(): ExitTransition = fadeOut(
animationSpec = tween(
durationMillis = 105,
delayMillis = 0,
easing = FastOutLinearInEasing,
),
)
private fun materialFadeThroughOut(): ExitTransition =
fadeOut(
animationSpec =
tween(
durationMillis = 105,
delayMillis = 0,
easing = FastOutLinearInEasing,
),
)
Loading

0 comments on commit 4a54b4d

Please sign in to comment.