Overview of Hotwire Native for Android
mainWebView instance across multiple destinations. This approach provides native navigation UI while maintaining the client-side performance benefits of Hotwire.repository·main·Indexed 41 days ago
https://github.com/hotwired/hotwire-native-androidA Kotlin library that wraps a Hotwire web application in a native Android shell. It provides native navigation UI and high-performance web integration via a managed WebView, allowing developers to leverage existing Hotwire web apps to build mobile applications.
WebView instance across multiple destinations. This approach provides native navigation UI while maintaining the client-side performance benefits of Hotwire.To run the demonstration application, follow these steps:
demo module to your connected Android device or emulator.# Example workflow
git clone https://github.com/hotwired/hotwire-native-android.git
cd hotwire-native-android
# Open in Android Studio and run the 'demo' moduleThe HotwireConfig class is used to customize the behavior of the Hotwire Native Android library. You can configure logging, WebView debugging, custom User Agents, and bridge component serialization.
Note: The constructor for HotwireConfig is internal, implying it is typically instantiated or accessed through a central entry point provided by the library (such as a singleton or a builder pattern defined elsewhere in the SDK).
Use HotwireBottomNavigationController to manage a BottomNavigationView where each tab is associated with its own NavigatorHost.
When instantiating the controller, you can configure several behaviors:
initialVisibility: Sets the starting visibility mode (see Visibility).clearNavigationOnTabReselection: If true (default), reselecting the active tab clears its navigation backstack to the start destination. If false, reselecting has no effect.animateVisibilityChanges: If true (default), the bottom navigation bar animates in and out when visibility changes. If false, changes are instant.lazyLoadTabs: If true, a tab's start location is only loaded when it is first selected. If false (default), all tabs load their start locations immediately upon creation.val controller = HotwireBottomNavigationController(
activity = myHotwireActivity,
view = myBottomNavigationView,
initialVisibility = HotwireBottomNavigationController.Visibility.DEFAULT,
clearNavigationOnTabReselection = true,
animateVisibilityChanges = true,
lazyLoadTabs = true
)To enable media capture (audio or video) within the WebView, you must declare specific permissions in your host app's AndroidManifest.xml. If these are missing, WebViewPermissionDelegate will deny the request and the web page will receive a NotAllowedError.
| WebView Resource | Required Android Permissions |
|---|---|
RESOURCE_AUDIO_CAPTURE | android.permission.RECORD_AUDIO AND android.permission.MODIFY_AUDIO_SETTINGS |
RESOURCE_VIDEO_CAPTURE | android.permission.CAMERA |
Note on Audio: Even though MODIFY_AUDIO_SETTINGS is a normal (install-time) permission, it is strictly required by the Chromium WebView's audio pipeline. Without it, getUserMedia({ audio: true }) will fail with Unable to select communication device! even if RECORD_AUDIO is granted.
To load URL path properties, you must provide a Location object to the load method. You can specify where the configuration JSON file is located using two primary methods:
assets directory. Do not include the assets/ prefix. For example, if your file is at assets/json/configuration.json, use "json/configuration.json".Providing a local configuration file is highly recommended to ensure configuration is available immediately at startup.
val location = PathConfiguration.Location(
assetFilePath = "json/configuration.json",
remoteFileUrl = "https://your-server.com/configuration.json"
)You can inspect the current state of the navigator using these properties:
currentDestination: Returns the currently active HotwireDestination on the backstack. If a dialog is active, it returns that dialog destination.location: Returns the URL/location string of the current backstack entry.previousLocation: Returns the URL/location string of the previous backstack entry.isAtStartDestination(): Returns true if the current destination is the only entry in the backstack.Use selectTab(tabIndex: Int) to programmatically change the currently selected tab in the BottomNavigationView.
controller.selectTab(1) // Selects the second tabUse the route() method to perform navigation. The resulting destination and its presentation (e.g., push, replace, modal) are determined by your pathConfiguration rules. You can provide optional VisitOptions, a Bundle for arguments, or FragmentNavigator.Extras for fragment-specific behavior.
If a custom route decision (via customRouteDecision) returns Router.Decision.CANCEL, the navigation will be aborted.
navigator.route(
location = "/new-path",
options = VisitOptions(action = VisitAction.PUSH),
bundle = Bundle().apply { putString("key", "value") }
)DefaultHotwireLogger, which prints logs to Logcat. To redirect or handle library log messages differently, provide your own implementation of the HotwireLogger interface to the logger property.The offlineRequestHandler property allows you to provide a custom OfflineRequestHandler.
Warning: This API is experimental and may be removed; it is not ready for production use.
Once loaded, you can retrieve a map of properties for a specific URL using the properties(location: String) method. Pass the absolute URL you want to match; the system will use the URL's relative path to find matching regex rules defined in your configuration.
Returns a PathConfigurationProperties (a HashMap<String, Any>) containing the values defined in your JSON for that path.
val props = pathConfiguration.properties("https://example.com/products/123")