Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion demo/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

<activity
android:name="dev.hotwire.demo.main.MainActivity"
android:configChanges="orientation|screenSize"
android:configChanges="screenSize"
android:launchMode="singleInstance"
android:theme="@style/MyTheme.DayNight"
android:windowSoftInputMode="adjustResize"
Expand Down
18 changes: 9 additions & 9 deletions demo/src/main/kotlin/dev/hotwire/demo/main/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ import android.os.Bundle
import android.view.View
import androidx.activity.enableEdgeToEdge
import androidx.activity.viewModels
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.navigation.NavigationBarView
import dev.hotwire.core.turbo.webview.WebViewInfo
import dev.hotwire.core.turbo.webview.WebViewVersionCompatibility
import dev.hotwire.demo.R
import dev.hotwire.navigation.activities.HotwireActivity
import dev.hotwire.navigation.tabs.HotwireBottomNavigationController
import dev.hotwire.navigation.tabs.HotwireNavigationController
import dev.hotwire.navigation.tabs.navigatorConfigurations
import dev.hotwire.navigation.util.applyDefaultImeWindowInsets

class MainActivity : HotwireActivity() {
private lateinit var bottomNavigationController: HotwireBottomNavigationController
private lateinit var navigationController: HotwireNavigationController
private val viewModel: MainActivityViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -24,20 +24,20 @@ class MainActivity : HotwireActivity() {
setContentView(R.layout.activity_main)
findViewById<View>(R.id.root).applyDefaultImeWindowInsets()

initializeBottomTabs()
initializeNavigation()

WebViewVersionCompatibility.displayUpdateDialogIfOutdated(
activity = this,
requiredVersion = WebViewInfo.REQUIRED_WEBVIEW_VERSION
)
}

private fun initializeBottomTabs() {
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav)
private fun initializeNavigation() {
val navigationView = findViewById<NavigationBarView>(R.id.navigation_bar)

bottomNavigationController = HotwireBottomNavigationController(this, bottomNavigationView)
bottomNavigationController.load(mainTabs, viewModel.selectedTabIndex)
bottomNavigationController.setOnTabSelectedListener { index, _ ->
navigationController = HotwireNavigationController(this, navigationView)
navigationController.load(mainTabs, viewModel.selectedTabIndex)
navigationController.setOnTabSelectedListener { index, _ ->
viewModel.selectedTabIndex = index
}
}
Expand Down
10 changes: 5 additions & 5 deletions demo/src/main/kotlin/dev/hotwire/demo/main/MainTabs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package dev.hotwire.demo.main
import dev.hotwire.demo.Demo
import dev.hotwire.demo.R
import dev.hotwire.navigation.navigator.NavigatorConfiguration
import dev.hotwire.navigation.tabs.HotwireBottomTab
import dev.hotwire.navigation.tabs.HotwireTab

private val navigation = HotwireBottomTab(
private val navigation = HotwireTab(
title = "Navigation",
iconResId = R.drawable.ic_tab_navigation,
configuration = NavigatorConfiguration(
Expand All @@ -15,7 +15,7 @@ private val navigation = HotwireBottomTab(
)
)

private val bridgeComponents = HotwireBottomTab(
private val bridgeComponents = HotwireTab(
title = "Bridge Components",
iconResId = R.drawable.ic_tab_bridge_components,
configuration = NavigatorConfiguration(
Expand All @@ -25,7 +25,7 @@ private val bridgeComponents = HotwireBottomTab(
)
)

private val resources = HotwireBottomTab(
private val resources = HotwireTab(
title = "Resources",
iconResId = R.drawable.ic_tab_resources,
configuration = NavigatorConfiguration(
Expand All @@ -35,7 +35,7 @@ private val resources = HotwireBottomTab(
)
)

private val bugsAndFixes = HotwireBottomTab(
private val bugsAndFixes = HotwireTab(
title = "Bugs & Fixes",
iconResId = R.drawable.ic_tab_bugs_fixes,
isVisible = Demo.current == Demo.Environment.Local,
Expand Down
64 changes: 64 additions & 0 deletions demo/src/main/res/layout-land/activity_main.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".main.MainActivity">

<com.google.android.material.navigationrail.NavigationRailView
android:id="@+id/navigation_bar"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:menuGravity="center" />

<androidx.fragment.app.FragmentContainerView
android:id="@+id/navigation_navigator_host"
android:name="dev.hotwire.navigation.navigator.NavigatorHost"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/navigation_bar"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="false" />

<androidx.fragment.app.FragmentContainerView
android:id="@+id/bridge_components_navigator_host"
android:name="dev.hotwire.navigation.navigator.NavigatorHost"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/navigation_bar"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="false" />

<androidx.fragment.app.FragmentContainerView
android:id="@+id/resources_navigator_host"
android:name="dev.hotwire.navigation.navigator.NavigatorHost"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/navigation_bar"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="false" />

<androidx.fragment.app.FragmentContainerView
android:id="@+id/bugs_fixes_navigator_host"
android:name="dev.hotwire.navigation.navigator.NavigatorHost"
android:layout_width="0dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/navigation_bar"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="false" />

</androidx.constraintlayout.widget.ConstraintLayout>
10 changes: 5 additions & 5 deletions demo/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_nav"
app:layout_constraintBottom_toTopOf="@id/navigation_bar"
app:defaultNavHost="false" />

<androidx.fragment.app.FragmentContainerView
Expand All @@ -23,7 +23,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_nav"
app:layout_constraintBottom_toTopOf="@id/navigation_bar"
app:defaultNavHost="false" />

<androidx.fragment.app.FragmentContainerView
Expand All @@ -32,7 +32,7 @@
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_nav"
app:layout_constraintBottom_toTopOf="@id/navigation_bar"
app:defaultNavHost="false" />

<androidx.fragment.app.FragmentContainerView
Expand All @@ -41,11 +41,11 @@
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/bottom_nav"
app:layout_constraintBottom_toTopOf="@id/navigation_bar"
app:defaultNavHost="false" />

<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_nav"
android:id="@+id/navigation_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,27 @@ import androidx.core.view.isVisible
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.fragment.FragmentNavigator
import com.google.android.material.bottomnavigation.BottomNavigationView
import com.google.android.material.navigation.NavigationBarView
import dev.hotwire.core.turbo.nav.PresentationContext
import dev.hotwire.core.turbo.visit.VisitOptions
import dev.hotwire.navigation.activities.HotwireActivity
import dev.hotwire.navigation.navigator.NavigatorHost
import dev.hotwire.navigation.navigator.presentationContext

/**
* A [BottomNavigationView] controller that manages multiple [HotwireBottomTab]s, each associated
* A [NavigationBarView] controller that manages multiple [HotwireTab]s, each associated
* with its own [NavigatorHost] instance in the Activity layout.
*/
class HotwireBottomNavigationController(
class HotwireNavigationController(
val activity: HotwireActivity,
val view: BottomNavigationView,
val view: NavigationBarView,
val initialVisibility: Visibility = Visibility.DEFAULT,
val clearNavigationOnTabReselection: Boolean = true,
val animateVisibilityChanges: Boolean = true
) : NavController.OnDestinationChangedListener {

/**
* The visibility mode for the `BottomNavigationView`.
* The visibility mode for the `NavigationBarView`.
*/
enum class Visibility {
/**
Expand All @@ -58,10 +58,10 @@ class HotwireBottomNavigationController(
updateVisibility()
}

private var listener: ((Int, HotwireBottomTab) -> Unit)? = null
private var listener: ((Int, HotwireTab) -> Unit)? = null

/**
* Set the visibility of the `BottomNavigationView`.
* Set the visibility of the `NavigationBarView`.
*/
var visibility = initialVisibility
set(value) {
Expand All @@ -70,29 +70,29 @@ class HotwireBottomNavigationController(
}

/**
* The currently selected tab in the [BottomNavigationView].
* The currently selected tab in the [NavigationBarView].
*/
val currentTab: HotwireBottomTab
val currentTab: HotwireTab
get() {
require(tabs.isNotEmpty()) { "No tabs have been loaded." }
return tabs[view.selectedItemId]
}

/**
* The tabs that have been loaded into the [BottomNavigationView].
* The tabs that have been loaded into the [NavigationBarView].
*/
var tabs = listOf<HotwireBottomTab>()
var tabs = listOf<HotwireTab>()
private set

/**
* Load the tabs and their navigator configurations into the [BottomNavigationView].
* Load the tabs and their navigator configurations into the [NavigationBarView].
*
* @param tabs The list of [HotwireBottomTab] instances that correspond to the
* [BottomNavigationView] tabs.
* @param tabs The list of [HotwireTab] instances that correspond to the
* [NavigationBarView] tabs.
* @param selectedTabIndex The index of the initially selected tab.
*/
fun load(
tabs: List<HotwireBottomTab>,
tabs: List<HotwireTab>,
selectedTabIndex: Int = 0
) {
require(tabs.isNotEmpty()) { "Tabs cannot be empty." }
Expand Down Expand Up @@ -121,7 +121,7 @@ class HotwireBottomNavigationController(
/**
* Set a listener that will be notified when a navigation tab is selected.
*/
fun setOnTabSelectedListener(listener: ((index: Int, tab: HotwireBottomTab) -> Unit)?) {
fun setOnTabSelectedListener(listener: ((index: Int, tab: HotwireTab) -> Unit)?) {
this.listener = listener
}

Expand Down Expand Up @@ -219,7 +219,7 @@ class HotwireBottomNavigationController(
}
}

private fun switchTab(tab: HotwireBottomTab) {
private fun switchTab(tab: HotwireTab) {
activity.delegate.setCurrentNavigator(tab.configuration)

tabs.forEach {
Expand All @@ -228,7 +228,7 @@ class HotwireBottomNavigationController(
}
}

private val HotwireBottomTab.navigatorHost: NavigatorHost
private val HotwireTab.navigatorHost: NavigatorHost
get() {
val fragment = activity.supportFragmentManager.findFragmentById(configuration.navigatorHostId)
return fragment as NavigatorHost
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import androidx.annotation.DrawableRes
import dev.hotwire.navigation.navigator.NavigatorConfiguration

/**
* Represents a bottom tab used by the [HotwireBottomNavigationController].
* Represents a tab used by the [HotwireNavigationController].
*
* @param itemId The [com.google.android.material.bottomnavigation.BottomNavigationView]'s
* @param itemId The [com.google.android.material.navigation.NavigationBarView]'s
* menu item ID for the corresponding tab.
* @param configuration The [NavigatorConfiguration] for the tab.
*/
data class HotwireBottomTab(
data class HotwireTab(
val title: String,
@DrawableRes val iconResId: Int,
val isVisible: Boolean = true,
Expand All @@ -20,5 +20,5 @@ data class HotwireBottomTab(
/**
* Maps the tabs to a list of their navigator configurations.
*/
val List<HotwireBottomTab>.navigatorConfigurations
val List<HotwireTab>.navigatorConfigurations
get() = map { it.configuration }