### Extend HotwireActivity for Navigation Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Activities must extend `HotwireActivity` and provide navigator configurations. This example shows a basic `MainActivity` setup. ```kotlin import dev.hotwire.navigation.activities.HotwireActivity import dev.hotwire.navigation.navigator.Navigator import dev.hotwire.navigation.navigator.NavigatorConfiguration class MainActivity : HotwireActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } // Provide navigator configurations for this activity override fun navigatorConfigurations(): List { return listOf( NavigatorConfiguration( name = "main", startLocation = "https://myapp.com", navigatorHostId = R.id.navigator_host ) ) } // Called when a navigator is ready for navigation override fun onNavigatorReady(navigator: Navigator) { // Navigator and its root destination are ready Log.d("MainActivity", "Navigator ${navigator.configuration.name} is ready") } } ``` -------------------------------- ### Navigator: Reset Navigation Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Use `navigator.clearAll()` to clear all destinations and reset to the start, or `navigator.reset()` for a full reset including the session. ```kotlin navigator.clearAll { Log.d("Navigator", "Navigation cleared") } // Full reset including session navigator.reset { Log.d("Navigator", "Navigator reset complete") } ``` -------------------------------- ### Define Bottom Tabs and Navigator Configurations Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Define a list of HotwireBottomTab objects, each specifying a title, icon, and NavigatorConfiguration. The NavigatorConfiguration includes the navigator name, host ID, and start location. Tabs can be conditionally shown or hidden using the isVisible property. ```kotlin import dev.hotwire.navigation.tabs.HotwireBottomTab import dev.hotwire.navigation.tabs.HotwireBottomNavigationController import dev.hotwire.navigation.tabs.navigatorConfigurations import dev.hotwire.navigation.navigator.NavigatorConfiguration // Define tabs val homeTabs = listOf( HotwireBottomTab( title = "Home", iconResId = R.drawable.ic_home, configuration = NavigatorConfiguration( name = "home", navigatorHostId = R.id.home_navigator_host, startLocation = "https://myapp.com" ) ), HotwireBottomTab( title = "Search", iconResId = R.drawable.ic_search, configuration = NavigatorConfiguration( name = "search", navigatorHostId = R.id.search_navigator_host, startLocation = "https://myapp.com/search" ) ), HotwireBottomTab( title = "Profile", iconResId = R.drawable.ic_profile, isVisible = true, // Can conditionally show/hide tabs configuration = NavigatorConfiguration( name = "profile", navigatorHostId = R.id.profile_navigator_host, startLocation = "https://myapp.com/profile" ) ) ) ``` -------------------------------- ### Implement Custom Route Decision Handlers in Kotlin Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Define custom route decision handlers for managing how different types of URLs are processed. This example shows handlers for external links and deep links. ```kotlin import dev.hotwire.core.config.Hotwire import dev.hotwire.navigation.config.registerRouteDecisionHandlers import dev.hotwire.navigation.routing.Router import dev.hotwire.navigation.routing.AppNavigationRouteDecisionHandler import dev.hotwire.navigation.routing.BrowserTabRouteDecisionHandler import dev.hotwire.navigation.navigator.NavigatorConfiguration import dev.hotwire.navigation.activities.HotwireActivity import android.net.Uri import androidx.browser.customtabs.CustomTabsIntent import android.content.Intent import android.app.Application // Custom route decision handler for external links class ExternalLinkHandler : Router.RouteDecisionHandler { override val name = "ExternalLinkHandler" override fun matches( location: String, configuration: NavigatorConfiguration ): Boolean { val appDomain = Uri.parse(configuration.startLocation).host val linkDomain = Uri.parse(location).host return linkDomain != appDomain } override fun handle( location: String, configuration: NavigatorConfiguration, activity: HotwireActivity ): Router.Decision { // Open external links in Chrome Custom Tabs val customTabsIntent = CustomTabsIntent.Builder().build() customTabsIntent.launchUrl(activity, Uri.parse(location)) return Router.Decision.CANCEL } } // Custom handler for deep links class DeepLinkHandler : Router.RouteDecisionHandler { override val name = "DeepLinkHandler" override fun matches( location: String, configuration: NavigatorConfiguration ): Boolean { return location.startsWith("myapp://") } override fun handle( location: String, configuration: NavigatorConfiguration, activity: HotwireActivity ): Router.Decision { // Handle custom deep link val intent = Intent(Intent.ACTION_VIEW, Uri.parse(location)) activity.startActivity(intent) return Router.Decision.CANCEL } } // Register handlers in Application class MyApplication : Application() { override fun onCreate() { super.onCreate() Hotwire.registerRouteDecisionHandlers( AppNavigationRouteDecisionHandler(), // Default in-app navigation ExternalLinkHandler(), DeepLinkHandler(), BrowserTabRouteDecisionHandler() // Fallback to browser ) } } ``` -------------------------------- ### Initialize Hotwire Native Configuration Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Configure the `Hotwire` object during application startup. Set default fragment destinations, register all possible fragment destinations, enable debug logging, and load path configurations from local assets or remote URLs. ```kotlin import dev.hotwire.core.config.Hotwire import dev.hotwire.core.turbo.config.PathConfiguration import dev.hotwire.navigation.config.defaultFragmentDestination import dev.hotwire.navigation.config.registerFragmentDestinations import dev.hotwire.navigation.config.registerBridgeComponents class MyApplication : Application() { override fun onCreate() { super.onCreate() // Set the default fragment destination for web requests Hotwire.defaultFragmentDestination = WebFragment::class // Register all fragment destinations that can be navigated to Hotwire.registerFragmentDestinations( WebFragment::class, WebBottomSheetFragment::class, NativeFragment::class ) // Configure debug options (disable in production) Hotwire.config.debugLoggingEnabled = BuildConfig.DEBUG Hotwire.config.webViewDebuggingEnabled = BuildConfig.DEBUG // Set a custom user agent prefix Hotwire.config.applicationUserAgentPrefix = "MyApp/1.0;" // Load path configuration from local asset and remote URL Hotwire.loadPathConfiguration( context = this, location = PathConfiguration.Location( assetFilePath = "json/path-configuration.json", remoteFileUrl = "https://myapp.com/api/path-configuration.json" ) ) // Get WebView information val webViewInfo = Hotwire.webViewInfo(this) Log.d("WebView", "Version: ${webViewInfo.majorVersion}") } } ``` -------------------------------- ### Activity Main Layout with NavigatorHost Source: https://context7.com/hotwired/hotwire-native-android/llms.txt The `activity_main.xml` layout file must include a `FragmentContainerView` with the `NavigatorHost` name to host the navigator. ```xml ``` -------------------------------- ### Activity with Bottom Navigation Controller Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Set up the MainActivity to use HotwireBottomNavigationController. Load the defined tabs and handle tab selection events to update the selected tab index in the ViewModel. Ensure navigatorConfigurations are provided. ```kotlin import dev.hotwire.navigation.tabs.HotwireBottomTab import dev.hotwire.navigation.tabs.HotwireBottomNavigationController import dev.hotwire.navigation.tabs.navigatorConfigurations import dev.hotwire.navigation.navigator.NavigatorConfiguration // Activity with bottom navigation class MainActivity : HotwireActivity() { private lateinit var bottomNavigationController: HotwireBottomNavigationController private val viewModel: MainViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val bottomNav = findViewById(R.id.bottom_nav) bottomNavigationController = HotwireBottomNavigationController(this, bottomNav) bottomNavigationController.load(homeTabs, viewModel.selectedTabIndex) bottomNavigationController.setOnTabSelectedListener { index, tab -> viewModel.selectedTabIndex = index } } override fun navigatorConfigurations() = homeTabs.navigatorConfigurations } ``` -------------------------------- ### Accessing and Observing Path Configuration Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Retrieve path configuration properties for a given URL, check presentation modes, contexts, and pull-to-refresh settings. Observe the load state of the path configuration to react to changes. ```kotlin import dev.hotwire.core.config.Hotwire import dev.hotwire.core.turbo.config.* // Access path configuration properties for a URL val properties = Hotwire.config.pathConfiguration.properties("https://myapp.com/posts/new") // Check presentation mode val presentation = properties.presentation // Presentation.DEFAULT, PUSH, REPLACE, etc. val context = properties.context // PresentationContext.DEFAULT or MODAL val pullToRefresh = properties.pullToRefreshEnabled // Boolean // Access custom settings val settings = Hotwire.config.pathConfiguration.settings val featureEnabled = settings["enable_feature_x"] as? Boolean ?: false // Observe load state Hotwire.config.pathConfiguration.loadState.collect { state -> when (state) { is PathConfigurationLoadState.NotLoaded -> { /* Initial state */ } is PathConfigurationLoadState.Loaded.FromBundledAsset -> { /* Loaded from local */ } is PathConfigurationLoadState.Loaded.FromCachedRemote -> { /* Loaded from cache */ } is PathConfigurationLoadState.Loaded.FromRemote -> { /* Fresh remote load */ } } } ``` -------------------------------- ### Navigator: Navigate to New Page Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Use the `navigator.route()` method to navigate to new locations. Custom `VisitOptions` can be provided, and additional bundle arguments can be passed. ```kotlin import dev.hotwire.navigation.navigator.Navigator import dev.hotwire.core.turbo.visit.VisitOptions import dev.hotwire.core.turbo.visit.VisitAction class MyFragment : HotwireWebFragment() { fun navigateToNewPage() { // Route to a location with default options navigator.route("https://myapp.com/posts") // Route with custom visit options navigator.route( location = "https://myapp.com/posts/new", options = VisitOptions(action = VisitAction.REPLACE) ) // Pass additional bundle arguments navigator.route( location = "https://myapp.com/posts/1", bundle = bundleOf("postId" to 1) ) } fun navigateBack() { // Pop the backstack navigator.pop() } fun resetNavigation() { // Clear all destinations and reset to start navigator.clearAll { Log.d("Navigator", "Navigation cleared") } // Full reset including session navigator.reset { Log.d("Navigator", "Navigator reset complete") } } fun checkNavigatorState() { // Check if navigator is ready val isReady = navigator.isReady() // Check if at start destination val isAtStart = navigator.isAtStartDestination() // Get current and previous locations val currentLocation = navigator.location val previousLocation = navigator.previousLocation // Access the session val session = navigator.session val isSessionReady = session.isReady } } ``` -------------------------------- ### Configure Custom WebView Factory Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Provide a custom factory function to `Hotwire.config.makeCustomWebView` to create and configure your WebView instance. This allows for detailed customization of WebView settings like JavaScript enablement, DOM storage, and text zoom. Ensure all necessary imports are included. ```kotlin import dev.hotwire.core.config.Hotwire import dev.hotwire.core.turbo.webview.HotwireWebView class MyApplication : Application() { override fun onCreate() { super.onCreate() // Provide a custom WebView factory Hotwire.config.makeCustomWebView = { context -> HotwireWebView(context, null).apply { settings.apply { // Customize WebView settings javaScriptEnabled = true domStorageEnabled = true databaseEnabled = true mediaPlaybackRequiresUserGesture = false allowFileAccess = false // Set text zoom textZoom = 100 } } } // Set custom JSON converter for bridge components Hotwire.config.jsonConverter = KotlinXJsonConverter() } } ``` -------------------------------- ### Activity Main XML Layout Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Define the layout for the activity, including a FrameLayout to contain multiple NavigatorHost instances for each tab and a BottomNavigationView for user interaction. ```xml ``` -------------------------------- ### Implement WebFragment for Web Destinations Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Extend HotwireWebFragment to manage WebView display, loading states, and bridge component integration for web-based destinations. Customize progress and error views, and handle visit errors. ```kotlin import dev.hotwire.navigation.fragments.HotwireWebFragment import dev.hotwire.navigation.destinations.HotwireDestinationDeepLink import dev.hotwire.core.turbo.errors.VisitError import dev.hotwire.core.turbo.webview.HotwireWebChromeClient @HotwireDestinationDeepLink(uri = "hotwire://fragment/web") open class WebFragment : HotwireWebFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setupToolbar() } // Customize the progress view shown during loading override fun createProgressView(location: String): View { return layoutInflater.inflate(R.layout.custom_progress, null) } // Customize the error view shown on visit errors override fun createErrorView(error: VisitError): View { return layoutInflater.inflate(R.layout.custom_error, null).apply { findViewById(R.id.error_message).text = error.description() } } // Provide a custom WebChromeClient override fun createWebChromeClient(): HotwireWebChromeClient { return HotwireWebChromeClient(navigator.session) } // Handle visit errors override fun onVisitErrorReceived(location: String, error: VisitError) { when (error) { is HttpError.ServerError -> showServerError() is HttpError.ClientError -> showClientError() else -> super.onVisitErrorReceived(location, error) } } // Called when form submission starts override fun onFormSubmissionStarted(location: String) { showProgressIndicator() } // Called when form submission finishes override fun onFormSubmissionFinished(location: String) { hideProgressIndicator() } // Refresh the page content override fun refresh(displayProgress: Boolean) { super.refresh(displayProgress) } private fun setupToolbar() { toolbarForNavigation()?.apply { inflateMenu(R.menu.web_menu) setOnMenuItemClickListener { item -> when (item.itemId) { R.id.action_refresh -> { refresh() true } else -> false } } } } } ``` -------------------------------- ### Implement HotwireFragment for Native Destinations Source: https://context7.com/hotwired/hotwire-native-android/llms.txt Extend HotwireFragment for fully native screens that do not display web content. Access path properties, the location, and navigate to other routes. Provide a custom toolbar. ```kotlin import dev.hotwire.navigation.fragments.HotwireFragment import dev.hotwire.navigation.destinations.HotwireDestinationDeepLink @HotwireDestinationDeepLink(uri = "hotwire://fragment/numbers") class NumbersFragment : HotwireFragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { return inflater.inflate(R.layout.fragment_numbers, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // Access path properties for this destination val title = pathProperties.title ?: "Numbers" toolbarForNavigation()?.title = title // Access the location Log.d("NumbersFragment", "Location: $location") // Navigate to another location view.findViewById