mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-10-03 17:14:33 -04:00
26 lines
39 KiB
HTML
26 lines
39 KiB
HTML
<!DOCTYPE html> <html lang="en-US"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=Edge"> <link rel="stylesheet" href="/Meshtastic-Android/assets/css/just-the-docs-default.css"> <link rel="stylesheet" href="/Meshtastic-Android/assets/css/just-the-docs-head-nav.css" id="jtd-head-nav-stylesheet"> <style id="jtd-nav-activation"> .site-nav > ul.nav-list:first-child > li > a, .site-nav > ul.nav-list:first-child > li > ul > li:not(:nth-child(4)) > a, .site-nav > ul.nav-list:first-child > li > ul > li > ul > li a { background-image: none; } .site-nav > ul.nav-list:not(:first-child) a, .site-nav li.external a { background-image: none; } .site-nav > ul.nav-list:first-child > li:nth-child(3) > ul > li:nth-child(4) > a { font-weight: 600; text-decoration: none; }.site-nav > ul.nav-list:first-child > li:nth-child(3) > button svg, .site-nav > ul.nav-list:first-child > li:nth-child(3) > ul > li:nth-child(4) > button svg { transform: rotate(-90deg); }.site-nav > ul.nav-list:first-child > li.nav-list-item:nth-child(3) > ul.nav-list, .site-nav > ul.nav-list:first-child > li.nav-list-item:nth-child(3) > ul.nav-list > li.nav-list-item:nth-child(4) > ul.nav-list { display: block; } </style> <script src="/Meshtastic-Android/assets/js/vendor/lunr.min.js"></script> <script src="/Meshtastic-Android/assets/js/just-the-docs.js"></script> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- Begin Jekyll SEO tag v2.9.0 --> <title>Navigation & Deep Links | Meshtastic Android</title> <meta name="generator" content="Jekyll v4.4.1" /> <meta property="og:title" content="Navigation & Deep Links" /> <meta property="og:locale" content="en_US" /> <meta name="description" content="User and developer documentation for the Meshtastic Android, Desktop, and iOS applications." /> <meta name="twitter:description" property="og:description" content="User and developer documentation for the Meshtastic Android, Desktop, and iOS applications." /> <link rel="canonical" href="/Meshtastic-Android/en/developer/navigation-and-deep-links.html" /> <meta property="og:url" content="/Meshtastic-Android/en/developer/navigation-and-deep-links.html" /> <meta property="og:site_name" content="Meshtastic Android" /> <meta property="og:type" content="website" /> <meta name="twitter:card" content="summary" /> <meta name="twitter:title" content="Navigation & Deep Links" /> <script type="application/ld+json"> {"@context":"https://schema.org","@type":"WebPage","description":"User and developer documentation for the Meshtastic Android, Desktop, and iOS applications.","headline":"Navigation & Deep Links","url":"/Meshtastic-Android/en/developer/navigation-and-deep-links.html"}</script> <!-- End Jekyll SEO tag --> <!-- Inter font from Google Fonts --> <link rel="preconnect" href="https://fonts.googleapis.com"> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet"> <style> /* M3-inspired typography */ :root { --font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; --font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', ui-monospace, monospace; } body, .site-title, .search-input { font-family: var(--font-sans) !important; } code, pre, .highlight { font-family: var(--font-mono) !important; font-size: 0.875em; } /* M3 surface elevation & rounding */ .main-content table { border-radius: 12px; overflow: hidden; } .main-content blockquote { border-radius: 12px; padding: 1rem 1.25rem; border-left-width: 4px; } .main-content pre { border-radius: 12px; } .main-content code { border-radius: 6px; padding: 0.15em 0.4em; } /* Smooth transitions for theme switching */ body, .side-bar, .main, .main-content, .site-header, .search-input, .search-results, a { transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease; } /* Theme toggle button styling */ .theme-toggle { background: none; border: 1px solid var(--border-color, #D5D6E0); border-radius: 20px; padding: 6px 12px; cursor: pointer; font-family: var(--font-sans); font-size: 0.75rem; font-weight: 500; display: inline-flex; align-items: center; gap: 6px; color: inherit; margin: 0 auto; transition: background-color 0.2s ease, border-color 0.2s ease; } .theme-toggle:hover { background-color: rgba(128, 128, 128, 0.1); } .theme-toggle-wrap { text-align: center; padding: 12px 0 4px; } /* Heading weight refinement */ h1, h2, h3, h4, h5, h6 { font-weight: 600; letter-spacing: -0.01em; } h1 { font-weight: 700; letter-spacing: -0.02em; } /* Smooth anchor scroll */ html { scroll-behavior: smooth; } /* Language switcher */ .language-switcher { display: inline-block; position: relative; margin-left: 8px; } .language-switcher-btn { cursor: pointer; font-size: 0.8rem; font-weight: 500; font-family: var(--font-sans); list-style: none; padding: 4px 10px; border: 1px solid var(--border-color, #D5D6E0); border-radius: 16px; display: inline-flex; align-items: center; gap: 4px; transition: background-color 0.2s ease; } .language-switcher-btn:hover { background-color: rgba(128, 128, 128, 0.1); } .language-switcher[open] .language-switcher-list { display: block; } .language-switcher-list { position: absolute; top: 100%; right: 0; left: auto; z-index: 100; list-style: none; padding: 8px 0; margin: 4px 0 0; min-width: 140px; background: var(--body-background-color, #fff); border: 1px solid var(--border-color, #D5D6E0); border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.1); } .language-switcher-list li { padding: 0; } .language-switcher-list a { display: block; padding: 6px 16px; text-decoration: none; font-size: 0.85rem; color: inherit; } .language-switcher-list a:hover { background-color: rgba(103, 234, 148, 0.15); } /* Version switcher (shares the language-switcher dropdown styling) */ .version-switcher { margin-left: 8px; } .version-switcher[hidden] { display: none; } /* Link out to the upstream meshtastic.org docs */ .upstream-docs-link { display: inline-flex; align-items: center; margin-left: 8px; padding: 4px 10px; border: 1px solid var(--border-color, #D5D6E0); border-radius: 16px; font-size: 0.8rem; font-weight: 500; font-family: var(--font-sans); text-decoration: none; color: inherit; transition: background-color 0.2s ease; } .upstream-docs-link:hover { background-color: rgba(128, 128, 128, 0.1); } </style> <script> // Respect OS preference on first visit, then remember user choice (function() { var stored = localStorage.getItem('jtd-theme'); if (stored) return; // will be applied by jtd.setTheme below if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { localStorage.setItem('jtd-theme', 'meshtastic-dark'); } })(); </script> </head> <body> <a class="skip-to-main" href="#main-content">Skip to main content</a> <svg xmlns="http://www.w3.org/2000/svg" class="d-none"> <symbol id="svg-link" viewBox="0 0 24 24"> <title>Link</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"> <path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path> </svg> </symbol> <symbol id="svg-menu" viewBox="0 0 24 24"> <title>Menu</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu"> <line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line> </svg> </symbol> <symbol id="svg-arrow-right" viewBox="0 0 24 24"> <title>Expand</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"> <polyline points="9 18 15 12 9 6"></polyline> </svg> </symbol> <!-- Feather. MIT License: https://github.com/feathericons/feather/blob/master/LICENSE --> <symbol id="svg-external-link" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"> <title id="svg-external-link-title">(external link)</title> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line> </symbol> <symbol id="svg-doc" viewBox="0 0 24 24"> <title>Document</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"> <path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline> </svg> </symbol> <symbol id="svg-search" viewBox="0 0 24 24"> <title>Search</title> <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"> <circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line> </svg> </symbol> <!-- Bootstrap Icons. MIT License: https://github.com/twbs/icons/blob/main/LICENSE.md --> <symbol id="svg-copy" viewBox="0 0 16 16"> <title>Copy</title> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16"> <path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"/> <path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"/> </svg> </symbol> <symbol id="svg-copied" viewBox="0 0 16 16"> <title>Copied</title> <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard-check-fill" viewBox="0 0 16 16"> <path d="M6.5 0A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3Zm3 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3Z"/> <path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1A2.5 2.5 0 0 1 9.5 5h-3A2.5 2.5 0 0 1 4 2.5v-1Zm6.854 7.354-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 0 1 .708-.708L7.5 10.793l2.646-2.647a.5.5 0 0 1 .708.708Z"/> </svg> </symbol> </svg> <header class="side-bar"> <div class="site-header"> <a href="/Meshtastic-Android/" class="site-title lh-tight"> Meshtastic Android </a> <button id="menu-button" class="site-button btn-reset" aria-label="Menu" aria-expanded="false"> <svg viewBox="0 0 24 24" class="icon" aria-hidden="true"><use xlink:href="#svg-menu"></use></svg> </button> </div> <nav aria-label="Main" id="site-nav" class="site-nav"> <ul class="nav-list"><li class="nav-list-item"><a href="/Meshtastic-Android/en/" class="nav-list-link">Home</a></li><li class="nav-list-item"><button class="nav-list-expander btn-reset" aria-label="User Guide submenu" aria-expanded="false"> <svg viewBox="0 0 24 24" aria-hidden="true"><use xlink:href="#svg-arrow-right"></use></svg> </button><a href="/Meshtastic-Android/en/user.html" class="nav-list-link">User Guide</a><ul class="nav-list"><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/onboarding.html" class="nav-list-link">Getting Started</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/connections.html" class="nav-list-link">Connections</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/messages-and-channels.html" class="nav-list-link">Messages & Channels</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/nodes.html" class="nav-list-link">Nodes</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/node-metrics.html" class="nav-list-link">Node Metrics</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/map-and-waypoints.html" class="nav-list-link">Map & Waypoints</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/settings-radio-user.html" class="nav-list-link">Settings — Radio & User</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/settings-module-admin.html" class="nav-list-link">Settings — Modules & Admin</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/telemetry-and-sensors.html" class="nav-list-link">Telemetry & Sensors</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/tak.html" class="nav-list-link">TAK Integration</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/mqtt.html" class="nav-list-link">MQTT</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/discovery.html" class="nav-list-link">Discovery</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/firmware.html" class="nav-list-link">Firmware Updates</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/desktop.html" class="nav-list-link">Desktop App</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/signal-meter.html" class="nav-list-link">How the Meshtastic Signal Meter Works</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/units-and-locale.html" class="nav-list-link">Units, Measurement & Locale</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/translate.html" class="nav-list-link">Translate the App</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/android-auto.html" class="nav-list-link">Android Auto</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/app-functions.html" class="nav-list-link">App Functions</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/widget.html" class="nav-list-link">Home Screen Widget</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/help-and-docs.html" class="nav-list-link">Help & In-App Docs</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/user/debug-logs.html" class="nav-list-link">Debug Logs</a></li></ul></li><li class="nav-list-item"><button class="nav-list-expander btn-reset" aria-label="Developer Guide submenu" aria-expanded="false"> <svg viewBox="0 0 24 24" aria-hidden="true"><use xlink:href="#svg-arrow-right"></use></svg> </button><a href="/Meshtastic-Android/en/developer.html" class="nav-list-link">Developer Guide</a><ul class="nav-list"><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/architecture.html" class="nav-list-link">Architecture</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/codebase.html" class="nav-list-link">Codebase</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/adding-a-feature-module.html" class="nav-list-link">Adding a Feature Module</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/navigation-and-deep-links.html" class="nav-list-link">Navigation & Deep Links</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/transport.html" class="nav-list-link">Transport</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/persistence.html" class="nav-list-link">Persistence</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/testing.html" class="nav-list-link">Testing</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/contributing.html" class="nav-list-link">Contributing</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/measurement.html" class="nav-list-link">Measurement & Formatting</a></li><li class="nav-list-item"><a href="/Meshtastic-Android/en/developer/test-builds.html" class="nav-list-link">Test Builds & Obtainium</a></li></ul></li></ul> </nav> <div class="d-md-block d-sm-none"> <div class="site-footer"> This site uses <a href="https://github.com/just-the-docs/just-the-docs">Just the Docs</a>, a documentation theme for Jekyll. </div> </div> </header> <div class="main" id="top"> <div id="main-header" class="main-header"> <div class="search" role="search"> <div class="search-input-wrap"> <input type="text" id="search-input" class="search-input" tabindex="0" placeholder="Search Meshtastic Android" autocomplete="off"> <label for="search-input" class="search-label"> <span class="sr-only">Search Meshtastic Android</span> <svg viewBox="0 0 24 24" class="search-icon" aria-hidden="true"><use xlink:href="#svg-search"></use></svg> </label> </div> <div id="search-results" class="search-results"></div> </div> <div class="theme-toggle-wrap"> <button class="theme-toggle" id="theme-toggle" aria-label="Toggle dark mode" title="Toggle light/dark theme" onclick="toggleMeshtasticTheme()"> <span id="theme-icon">🌙</span> <span id="theme-label">Dark</span> </button> <details class="language-switcher version-switcher" id="version-switcher" aria-label="Documentation version" hidden> <summary class="language-switcher-btn" title="Switch documentation version"> 🏷️ <span id="version-current"></span> </summary> <ul class="language-switcher-list" id="version-switcher-list"></ul> </details> <script> (function() { var baseurl = '/Meshtastic-Android'; var root = baseurl; var channel = 'latest'; // /main/, /vX.Y.Z/ or a prerelease snapshot like /v2.8.0-open.1/. var match = baseurl.match(/^(.*)\/(main|v\d+\.\d+\.\d+(?:-(?:open|closed)\.\d+)?)$/); if (match) { root = match[1]; channel = match[2]; } fetch(root + '/versions.json') .then(function(res) { return res.ok ? res.json() : Promise.reject(); }) .then(function(data) { var switcher = document.getElementById('version-switcher'); var list = document.getElementById('version-switcher-list'); var current = document.getElementById('version-current'); if (!switcher || !list || !current) return; // Keep the reader on the same page when switching channels; a page // that doesn't exist in the target version lands on the 404 page. var relativePath = location.pathname.slice(baseurl.length) + location.hash; var entries = []; if (data.latest) { entries.push({ id: 'latest', label: 'latest (v' + data.latest + ')', base: root }); } // Prereleases are documented ahead of general availability, so the track // is always spelled out — a reader must not mistake a closed-testing // snapshot for a shipped release. (data.prereleases || []).forEach(function(p) { entries.push({ id: p.dir, label: p.dir.replace(/^v/, 'v') + ' — ' + p.track + ' testing', base: root + '/' + p.dir }); }); if (data.hasMain) { entries.push({ id: 'main', label: 'main (unreleased snapshot)', base: root + '/main' }); } (data.versions || []).forEach(function(v) { entries.push({ id: 'v' + v, label: 'v' + v, base: root + '/v' + v }); }); var currentEntry = entries.filter(function(e) { return e.id === channel; })[0]; current.textContent = currentEntry ? currentEntry.label : channel; var others = entries.filter(function(e) { return e.id !== channel; }); if (others.length === 0) return; others.forEach(function(e) { var li = document.createElement('li'); var a = document.createElement('a'); a.href = e.base + relativePath; a.textContent = e.label; li.appendChild(a); list.appendChild(li); }); switcher.hidden = false; }) .catch(function() { /* no manifest (local build) — leave switcher hidden */ }); })(); </script> <a class="upstream-docs-link" href="https://meshtastic.org/docs/" title="Official Meshtastic documentation (meshtastic.org)">Meshtastic Docs ↗</a> </div> <script> function toggleMeshtasticTheme() { var current = localStorage.getItem('jtd-theme') || 'meshtastic'; var next = (current === 'meshtastic-dark') ? 'meshtastic' : 'meshtastic-dark'; if (typeof jtd !== 'undefined' && typeof jtd.setTheme === 'function') { jtd.setTheme(next); } localStorage.setItem('jtd-theme', next); var icon = document.getElementById('theme-icon'); var label = document.getElementById('theme-label'); if (icon && label) { icon.textContent = (next === 'meshtastic-dark') ? '☀️' : '🌙'; label.textContent = (next === 'meshtastic-dark') ? 'Light' : 'Dark'; } } // Apply stored/OS theme on page load (function() { var theme = localStorage.getItem('jtd-theme') || 'meshtastic'; // Sync toggle label immediately var icon = document.getElementById('theme-icon'); var label = document.getElementById('theme-label'); if (icon && label) { icon.textContent = (theme === 'meshtastic-dark') ? '☀️' : '🌙'; label.textContent = (theme === 'meshtastic-dark') ? 'Light' : 'Dark'; } // Apply theme once jtd is ready function tryApply() { if (typeof jtd !== 'undefined' && typeof jtd.setTheme === 'function') { jtd.setTheme(theme); } else { setTimeout(tryApply, 50); } } tryApply(); })(); </script> </div> <div class="main-content-wrap"> <nav aria-label="Breadcrumb" class="breadcrumb-nav"> <ol class="breadcrumb-nav-list"> <li class="breadcrumb-nav-list-item"><a href="/Meshtastic-Android/en/developer.html">Developer Guide</a></li> <li class="breadcrumb-nav-list-item"><span>Navigation & Deep Links</span></li> </ol> </nav> <div id="main-content" class="main-content"> <main> <h1 id="navigation--deep-links"> <a href="#navigation--deep-links" class="anchor-heading" aria-labelledby="navigation--deep-links"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Navigation & Deep Links </h1> <p>The app uses <strong>Navigation 3</strong> with typed, serializable routes and centralized deep link resolution.</p> <h2 id="route-architecture"> <a href="#route-architecture" class="anchor-heading" aria-labelledby="route-architecture"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Route Architecture </h2> <p>All routes are defined in <code class="language-plaintext highlighter-rouge">core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/Routes.kt</code>.</p> <h3 id="route-hierarchy"> <a href="#route-hierarchy" class="anchor-heading" aria-labelledby="route-hierarchy"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Route Hierarchy </h3> <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">interface</span> <span class="nc">Route</span> <span class="p">:</span> <span class="nc">NavKey</span> <span class="c1">// All routes implement NavKey</span>
|
||
<span class="kd">interface</span> <span class="nc">Graph</span> <span class="p">:</span> <span class="nc">Route</span> <span class="c1">// Graph roots for navigation hierarchies</span>
|
||
|
||
<span class="nd">@Serializable</span>
|
||
<span class="k">sealed</span> <span class="kd">interface</span> <span class="nc">SettingsRoute</span> <span class="p">:</span> <span class="nc">Route</span> <span class="p">{</span>
|
||
<span class="nd">@Serializable</span> <span class="kd">data class</span> <span class="nc">Settings</span><span class="p">(</span><span class="kd">val</span> <span class="py">destNum</span><span class="p">:</span> <span class="nc">Int</span><span class="p">?</span> <span class="p">=</span> <span class="k">null</span><span class="p">)</span> <span class="p">:</span> <span class="nc">SettingsRoute</span><span class="p">,</span> <span class="nc">Graph</span>
|
||
<span class="nd">@Serializable</span> <span class="n">data</span> <span class="kd">object</span> <span class="nc">DeviceConfiguration</span> <span class="p">:</span> <span class="nc">SettingsRoute</span>
|
||
<span class="nd">@Serializable</span> <span class="n">data</span> <span class="kd">object</span> <span class="nc">HelpDocs</span> <span class="p">:</span> <span class="nc">SettingsRoute</span>
|
||
<span class="nd">@Serializable</span> <span class="kd">data class</span> <span class="nc">HelpDocPage</span><span class="p">(</span><span class="kd">val</span> <span class="py">pageId</span><span class="p">:</span> <span class="nc">String</span><span class="p">)</span> <span class="p">:</span> <span class="nc">SettingsRoute</span>
|
||
<span class="c1">// ...</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div></div> <h3 id="conventions"> <a href="#conventions" class="anchor-heading" aria-labelledby="conventions"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Conventions </h3> <ul> <li>Routes are <code class="language-plaintext highlighter-rouge">@Serializable</code> for state restoration</li> <li>Use <code class="language-plaintext highlighter-rouge">data object</code> for routes without parameters</li> <li>Use <code class="language-plaintext highlighter-rouge">data class</code> for parameterized routes</li> <li>Group related routes under a <code class="language-plaintext highlighter-rouge">sealed interface</code></li> <li>Graph entry points implement both the route interface and <code class="language-plaintext highlighter-rouge">Graph</code></li> </ul> <h2 id="deep-link-router"> <a href="#deep-link-router" class="anchor-heading" aria-labelledby="deep-link-router"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Deep Link Router </h2> <p><code class="language-plaintext highlighter-rouge">DeepLinkRouter</code> in <code class="language-plaintext highlighter-rouge">core/navigation</code> maps URI deep links to typed backstack lists.</p> <h3 id="uri-format"> <a href="#uri-format" class="anchor-heading" aria-labelledby="uri-format"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> URI Format </h3> <p>Both forms resolve through the same <code class="language-plaintext highlighter-rouge">DeepLinkRouter</code>, so any path below works with either scheme:</p> <div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>meshtastic://meshtastic/{path}
|
||
https://meshtastic.org/{path} # App Link, android:autoVerify — also opens in-app on a real device/adb
|
||
</code></pre></div></div> <p><code class="language-plaintext highlighter-rouge">adb shell am start -a android.intent.action.VIEW -d "meshtastic://meshtastic/{path}"</code> is the fastest way to trigger any route below from a shell or automation script without touching the UI.</p> <p>For the <code class="language-plaintext highlighter-rouge">https</code> form to open in-app, each top-level path segment must also be declared as an <code class="language-plaintext highlighter-rouge">android:pathPrefix</code> in the <code class="language-plaintext highlighter-rouge">android:autoVerify</code> intent-filter in <code class="language-plaintext highlighter-rouge">androidApp/src/main/AndroidManifest.xml</code> — otherwise the link opens in the browser. Adding a new top-level route therefore takes three steps: add the segment to <code class="language-plaintext highlighter-rouge">DeepLinkRouter.topLevelPathSegments</code> (the router refuses to dispatch segments outside that set), add its <code class="language-plaintext highlighter-rouge">when</code> branch in <code class="language-plaintext highlighter-rouge">DeepLinkRouter.route()</code>, and add the matching <code class="language-plaintext highlighter-rouge">pathPrefix</code> to the manifest. <code class="language-plaintext highlighter-rouge">DeepLinkManifestConsistencyTest</code> (androidApp unit tests) checks the manifest against the set, so a missing manifest entry fails CI.</p> <p><strong>Source of truth:</strong> the always-current list of top-level segments is <code class="language-plaintext highlighter-rouge">topLevelPathSegments</code> in <a href="https://github.com/meshtastic/Meshtastic-Android/blob/main/core/navigation/src/commonMain/kotlin/org/meshtastic/core/navigation/DeepLinkRouter.kt"><code class="language-plaintext highlighter-rouge">DeepLinkRouter</code></a> — sub-paths live in the <code class="language-plaintext highlighter-rouge">route()</code> <code class="language-plaintext highlighter-rouge">when</code> block plus its helper maps (<code class="language-plaintext highlighter-rouge">settingsSubRoutes</code>, <code class="language-plaintext highlighter-rouge">nodeDetailSubRoutes</code>); the class-level KDoc is illustrative, not exhaustive. It also exists as executable spec in <a href="https://github.com/meshtastic/Meshtastic-Android/blob/main/core/navigation/src/commonTest/kotlin/org/meshtastic/core/navigation/DeepLinkRouterTest.kt"><code class="language-plaintext highlighter-rouge">DeepLinkRouterTest.kt</code></a>. The table below is a snapshot for quick reference — check those two files if it looks out of date.</p> <h3 id="supported-deep-links"> <a href="#supported-deep-links" class="anchor-heading" aria-labelledby="supported-deep-links"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Supported Deep Links </h3> <div class="table-wrapper"><table> <thead> <tr> <th>URI Path</th> <th>Route</th> <th>Notes</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">/connections</code></td> <td><code class="language-plaintext highlighter-rouge">ConnectionsRoute.Connections(null)</code></td> <td>Connections screen</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/connections?address={prefixedAddress}</code></td> <td><code class="language-plaintext highlighter-rouge">ConnectionsRoute.Connections(address)</code></td> <td>Auto-connects to a device without manual selection — the address uses the app’s internal transport-prefixed format: <code class="language-plaintext highlighter-rouge">t192.168.1.1:4403</code> (TCP), <code class="language-plaintext highlighter-rouge">xAA:BB:CC:DD:EE:FF</code> (BLE), <code class="language-plaintext highlighter-rouge">s/dev/ttyUSB0</code> (serial). Intended for scripts/AI tooling driving the app.</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/connections?address=n</code></td> <td><code class="language-plaintext highlighter-rouge">ConnectionsRoute.Connections("n")</code></td> <td>Disconnects the current device instead of connecting (<code class="language-plaintext highlighter-rouge">n</code> = the internal “no device selected” sentinel).</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/wifi-provision</code></td> <td><code class="language-plaintext highlighter-rouge">WifiProvisionRoute.WifiProvision(null)</code></td> <td>WiFi provisioning screen</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/wifi-provision?address={mac}</code></td> <td><code class="language-plaintext highlighter-rouge">WifiProvisionRoute.WifiProvision(mac)</code></td> <td>Provisioning targeting a specific device MAC</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/settings</code></td> <td><code class="language-plaintext highlighter-rouge">SettingsRoute.Settings(null)</code></td> <td>Settings root</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/settings/helpDocs</code></td> <td><code class="language-plaintext highlighter-rouge">SettingsRoute.HelpDocs</code></td> <td>Docs browser</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/settings/helpDocs/{pageId}</code></td> <td><code class="language-plaintext highlighter-rouge">SettingsRoute.HelpDocPage(pageId)</code></td> <td>Specific doc page</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/settings/help-docs</code></td> <td><code class="language-plaintext highlighter-rouge">SettingsRoute.HelpDocs</code></td> <td>Compatibility alias</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/discovery</code></td> <td><code class="language-plaintext highlighter-rouge">DiscoveryRoute.DiscoveryGraph</code></td> <td>Local Mesh Discovery entry point</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/settings/local-mesh-discovery/session/{sessionId}</code></td> <td><code class="language-plaintext highlighter-rouge">DiscoveryRoute.DiscoverySummary(sessionId)</code></td> <td>Discovery session result</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/nodes</code></td> <td><code class="language-plaintext highlighter-rouge">NodesRoute.Nodes</code></td> <td>Node list</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/nodes/{destNum}</code></td> <td><code class="language-plaintext highlighter-rouge">NodesRoute.NodeDetail(destNum)</code></td> <td>Node detail</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/nodes/{destNum}/{metric}</code></td> <td>e.g. <code class="language-plaintext highlighter-rouge">NodeDetailRoute.DeviceMetrics(destNum)</code></td> <td>Specific node metric tab (<code class="language-plaintext highlighter-rouge">device-metrics</code>, <code class="language-plaintext highlighter-rouge">signal</code>, <code class="language-plaintext highlighter-rouge">power</code>, <code class="language-plaintext highlighter-rouge">traceroute</code>, <code class="language-plaintext highlighter-rouge">pax</code>, <code class="language-plaintext highlighter-rouge">neighbors</code>, …)</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/messages</code></td> <td><code class="language-plaintext highlighter-rouge">ContactsRoute.Contacts</code></td> <td>Conversation list</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/messages/{contactKey}</code></td> <td><code class="language-plaintext highlighter-rouge">ContactsRoute.Messages(contactKey)</code></td> <td>Specific conversation</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/share?message={text}</code></td> <td><code class="language-plaintext highlighter-rouge">ContactsRoute.Share(message)</code></td> <td>Share-to-contact composer</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/quickchat</code></td> <td><code class="language-plaintext highlighter-rouge">ContactsRoute.QuickChat</code></td> <td>Quick chat picker</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/map</code></td> <td><code class="language-plaintext highlighter-rouge">MapRoute.Map(null)</code></td> <td>Map view</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/map/{waypointId}</code></td> <td><code class="language-plaintext highlighter-rouge">MapRoute.Map(waypointId)</code></td> <td>Map centered on a waypoint</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/channels</code></td> <td><code class="language-plaintext highlighter-rouge">ChannelsRoute.Channels</code></td> <td>Channel list</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/firmware</code></td> <td><code class="language-plaintext highlighter-rouge">FirmwareRoute.FirmwareGraph</code></td> <td>Firmware screen</td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">/firmware/update</code></td> <td><code class="language-plaintext highlighter-rouge">FirmwareRoute.FirmwareUpdate</code></td> <td>Firmware update flow</td> </tr> </tbody> </table></div> <h3 id="backstack-synthesis"> <a href="#backstack-synthesis" class="anchor-heading" aria-labelledby="backstack-synthesis"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Backstack Synthesis </h3> <p>Deep links synthesize a full backstack, not just the target screen:</p> <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// /settings/helpDocs/messages-and-channels produces:</span>
|
||
<span class="nf">listOf</span><span class="p">(</span>
|
||
<span class="nc">SettingsRoute</span><span class="p">.</span><span class="nc">Settings</span><span class="p">(</span><span class="k">null</span><span class="p">),</span>
|
||
<span class="nc">SettingsRoute</span><span class="p">.</span><span class="nc">HelpDocs</span><span class="p">,</span>
|
||
<span class="nc">SettingsRoute</span><span class="p">.</span><span class="nc">HelpDocPage</span><span class="p">(</span><span class="s">"messages-and-channels"</span><span class="p">),</span>
|
||
<span class="p">)</span>
|
||
</code></pre></div></div> <p>This ensures the user can navigate “up” correctly.</p> <h2 id="adding-a-deep-link"> <a href="#adding-a-deep-link" class="anchor-heading" aria-labelledby="adding-a-deep-link"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Adding a Deep Link </h2> <ol> <li>Define the typed route in <code class="language-plaintext highlighter-rouge">Routes.kt</code>.</li> <li>Add the mapping in <code class="language-plaintext highlighter-rouge">DeepLinkRouter.settingsSubRoutes</code> (or equivalent for other graphs).</li> <li>Add a test in <code class="language-plaintext highlighter-rouge">DeepLinkRouterTest.kt</code>.</li> <li>Register the navigation entry in the appropriate feature module.</li> <li>Update the KDoc list on <code class="language-plaintext highlighter-rouge">DeepLinkRouter.route()</code> and the table above — they’re the two places tooling/agents look to discover what deep links exist.</li> </ol> <h2 id="navigation-entry-registration"> <a href="#navigation-entry-registration" class="anchor-heading" aria-labelledby="navigation-entry-registration"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Navigation Entry Registration </h2> <p>Each feature module provides entries via an extension function:</p> <div class="language-kotlin highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fun</span> <span class="nf">EntryProviderScope</span><span class="p"><</span><span class="nc">NavKey</span><span class="p">>.</span><span class="nf">docsEntries</span><span class="p">(</span><span class="n">backStack</span><span class="p">:</span> <span class="nc">NavBackStack</span><span class="p"><</span><span class="nc">NavKey</span><span class="p">>)</span> <span class="p">{</span>
|
||
<span class="n">entry</span><span class="p"><</span><span class="nc">SettingsRoute</span><span class="p">.</span><span class="nc">HelpDocs</span><span class="p">></span> <span class="p">{</span> <span class="nc">DocsBrowserScreen</span><span class="p">(</span><span class="n">backStack</span><span class="p">)</span> <span class="p">}</span>
|
||
<span class="n">entry</span><span class="p"><</span><span class="nc">SettingsRoute</span><span class="p">.</span><span class="nc">HelpDocPage</span><span class="p">></span> <span class="p">{</span> <span class="n">route</span> <span class="p">-></span> <span class="nc">DocsPageRouteScreen</span><span class="p">(</span><span class="n">route</span><span class="p">.</span><span class="n">pageId</span><span class="p">,</span> <span class="n">backStack</span><span class="p">)</span> <span class="p">}</span>
|
||
<span class="p">}</span>
|
||
</code></pre></div></div> <p>These are called from the settings navigation composition.</p> <h2 id="testing"> <a href="#testing" class="anchor-heading" aria-labelledby="testing"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Testing </h2> <p>Deep link routing is tested in:</p> <div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>core/navigation/src/commonTest/kotlin/org/meshtastic/core/navigation/DeepLinkRouterTest.kt
|
||
</code></pre></div></div><hr /> </main> <hr> <footer> <footer class="site-footer"> Copyright © 2026 Meshtastic LLC. Distributed under the <a href="https://www.gnu.org/licenses/gpl-3.0.html">GPL v3 License.</a> </footer> <div class="d-sm-block d-md-none"> <div class="mt-4 fs-2"> This site uses <a href="https://github.com/just-the-docs/just-the-docs">Just the Docs</a>, a documentation theme for Jekyll. </div> </div> </footer> </div> </div> <div class="search-overlay"></div> </div> </body> </html>
|