mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-09-15 14:48:34 -04:00
2 lines
53 KiB
HTML
2 lines
53 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(10)) > 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(10) > 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(10) > 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(10) > 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>Test Builds & Obtainium | Meshtastic Android</title> <meta name="generator" content="Jekyll v4.4.1" /> <meta property="og:title" content="Test Builds & Obtainium" /> <meta property="og:locale" content="en_US" /> <meta name="description" content="Install and auto-update Meshtastic test builds from GitHub releases with Obtainium — channel configurations, APK selection, and the shareable config format." /> <meta name="twitter:description" property="og:description" content="Install and auto-update Meshtastic test builds from GitHub releases with Obtainium — channel configurations, APK selection, and the shareable config format." /> <link rel="canonical" href="/Meshtastic-Android/en/developer/test-builds.html" /> <meta property="og:url" content="/Meshtastic-Android/en/developer/test-builds.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="Test Builds & Obtainium" /> <script type="application/ld+json"> {"@context":"https://schema.org","@type":"WebPage","description":"Install and auto-update Meshtastic test builds from GitHub releases with Obtainium — channel configurations, APK selection, and the shareable config format.","headline":"Test Builds & Obtainium","url":"/Meshtastic-Android/en/developer/test-builds.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>Test Builds & Obtainium</span></li> </ol> </nav> <div id="main-content" class="main-content"> <main> <h1 id="test-builds--obtainium"> <a href="#test-builds--obtainium" class="anchor-heading" aria-labelledby="test-builds--obtainium"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Test Builds & Obtainium </h1> <p><a href="https://github.com/ImranR98/Obtainium">Obtainium</a> installs and auto-updates Android apps straight from their GitHub releases — no Play Store account, no testing-track invite. This page sets it up for Meshtastic and shows how to follow the <strong>open beta</strong>, <strong>closed beta</strong>, or <strong>snapshot</strong> channels.</p> <blockquote> <p><strong>Heads up — signatures.</strong> Builds from GitHub are signed with the project’s release key (<code class="language-plaintext highlighter-rouge">CN=Kevin Hester, O=Geeksville Industries</code>), <strong>not</strong> Google Play’s. If you already have Meshtastic installed from the Play Store, Android will refuse to update over it: <strong>uninstall the Play Store version first</strong> (this clears app data), then install via Obtainium and stay on Obtainium for updates.</p> <p><strong>Switching between the <code class="language-plaintext highlighter-rouge">fdroid</code> and <code class="language-plaintext highlighter-rouge">google</code> flavors is fine.</strong> Both flavors in a given release carry the <em>same</em> release key, so Obtainium can move you from one to the other in place — verified by installing the <code class="language-plaintext highlighter-rouge">google</code> APK over an installed <code class="language-plaintext highlighter-rouge">fdroid</code> build. Only the <em>origin</em> of a build (Play vs GitHub) determines whether the signatures clash.</p> </blockquote><hr /> <h2 id="which-channels-can-obtainium-reach"> <a href="#which-channels-can-obtainium-reach" class="anchor-heading" aria-labelledby="which-channels-can-obtainium-reach"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Which channels can Obtainium reach? </h2> <p>Meshtastic for Android promotes builds up a ladder: <code class="language-plaintext highlighter-rouge">closed → open → production</code>. Each maps to a Google Play track, and the GitHub release is published (un-drafted) when a build is promoted to closed or higher.</p> <div class="table-wrapper"><table> <thead> <tr> <th>Channel</th> <th>Play track</th> <th>On GitHub</th> <th>Obtainium can install it?</th> </tr> </thead> <tbody> <tr> <td><strong>stable</strong></td> <td>Production</td> <td>published, marked <em>Latest</em></td> <td>✅ Yes</td> </tr> <tr> <td><strong>open</strong> beta</td> <td>Beta (Open)</td> <td>published prerelease, tag <code class="language-plaintext highlighter-rouge">vX.Y.Z-open.N</code></td> <td>✅ Yes</td> </tr> <tr> <td><strong>closed</strong> beta</td> <td>Alpha (Closed)</td> <td>published prerelease, tag <code class="language-plaintext highlighter-rouge">vX.Y.Z-closed.N</code></td> <td>✅ Yes</td> </tr> <tr> <td><strong>snapshot</strong></td> <td>— (not on Play)</td> <td>rolling prerelease, tag <code class="language-plaintext highlighter-rouge">snapshot</code></td> <td>✅ Yes</td> </tr> </tbody> </table></div> <p>Each promotion cuts its <strong>own</strong> tag, and older ones stay published — a whole run of <code class="language-plaintext highlighter-rouge">vX.Y.Z-open.N</code> tags accumulates during a version cycle, each still installable with APKs attached. So a channel gathers releases rather than moving a single one forward, and the channel configs below pick the <strong>newest</strong> release whose title matches the channel.</p> <p>Which release each channel currently resolves to is deliberately <strong>not</strong> listed on this page: it is archived per release tag and bundled into the app, so a live version number here would be frozen at build time and wrong within weeks. The <a href="https://github.com/meshtastic/Meshtastic-Android#get-meshtastic">project README</a> carries the current state, refreshed automatically.</p> <p>A channel can legitimately be empty — most often <code class="language-plaintext highlighter-rouge">-closed</code>, early in a cycle before any build has been cut to it. The number of published <code class="language-plaintext highlighter-rouge">fdroid</code> ABI splits has also changed over time (2.7.14 shipped five, 2.8.0 ships three), which is why the filter matches every split rather than naming ABIs.</p> <p><strong>Snapshot</strong> is different: it’s an automated debug build of the latest commit on <code class="language-plaintext highlighter-rouge">main</code>, rebuilt and re-published under the single moving <code class="language-plaintext highlighter-rouge">snapshot</code> tag on every push. It never goes to Play. Because debug builds use a <code class="language-plaintext highlighter-rouge">.debug</code> application-ID suffix (<code class="language-plaintext highlighter-rouge">com.geeksville.mesh.fdroid.debug</code> / <code class="language-plaintext highlighter-rouge">com.geeksville.mesh.google.debug</code>) and the debug signing key, a snapshot installs as its <strong>own separate app</strong> — it sits alongside a Play/stable/beta install, so the uninstall-first warning above does <strong>not</strong> apply to it.</p><hr /> <h2 id="one-tap-setup"> <a href="#one-tap-setup" class="anchor-heading" aria-labelledby="one-tap-setup"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> One-tap setup </h2> <p>Tap a link below on the device that has Obtainium installed. Every setting from the rest of this page is already baked into it — pick the channel you want and the flavor you want (<a href="#picking-the-apk">which flavor?</a>).</p> <!-- BEGIN GENERATED LINKS: obtainium/generate-links.py --> <div class="table-wrapper"><table> <thead> <tr> <th>Channel</th> <th><code class="language-plaintext highlighter-rouge">google</code> flavor</th> <th><code class="language-plaintext highlighter-rouge">fdroid</code> flavor</th> </tr> </thead> <tbody> <tr> <td>Stable</td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%22%2C%22additionalSettings%22%3A%22%7B%5C%22apkFilterRegEx%5C%22%3A%5C%22google-release%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%5C%22%7D%22%7D">Add</a></td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%22%2C%22additionalSettings%22%3A%22%7B%5C%22apkFilterRegEx%5C%22%3A%5C%22fdroid-.%2A-release%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%5C%22%7D%22%7D">Add</a></td> </tr> <tr> <td>Open beta</td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%20Beta%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22-open%5C%22%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22google-release%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%20Beta%5C%22%7D%22%7D">Add</a></td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%20Beta%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22-open%5C%22%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22fdroid-.%2A-release%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%20Beta%5C%22%7D%22%7D">Add</a></td> </tr> <tr> <td>Closed beta</td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%20Alpha%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22-closed%5C%22%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22google-release%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%20Alpha%5C%22%7D%22%7D">Add</a></td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%20Alpha%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22-closed%5C%22%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22fdroid-.%2A-release%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%20Alpha%5C%22%7D%22%7D">Add</a></td> </tr> <tr> <td>Bleeding edge (newest promoted test build)</td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%20Beta%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22-%28closed%7Copen%29%5C%22%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22google-release%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%20Beta%5C%22%7D%22%7D">Add</a></td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%20Beta%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22-%28closed%7Copen%29%5C%22%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22fdroid-.%2A-release%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%20Beta%5C%22%7D%22%7D">Add</a></td> </tr> <tr> <td>Snapshot (latest commit on <code class="language-plaintext highlighter-rouge">main</code>)</td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh.google.debug%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%20Snapshot%20%28google%29%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22%5ESnapshot%5C%22%2C%5C%22useLatestAssetDateAsReleaseDate%5C%22%3Atrue%2C%5C%22versionDetection%5C%22%3Afalse%2C%5C%22releaseDateAsVersion%5C%22%3Atrue%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22google-.%2A-debug-%5C%5C%5C%5Cd%2B%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%20Snapshot%20%28google%29%5C%22%7D%22%7D">Add</a></td> <td><a href="https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/%7B%22id%22%3A%22com.geeksville.mesh.fdroid.debug%22%2C%22url%22%3A%22https%3A%2F%2Fgithub.com%2Fmeshtastic%2FMeshtastic-Android%22%2C%22author%22%3A%22meshtastic%22%2C%22name%22%3A%22Meshtastic%20Snapshot%20%28fdroid%29%22%2C%22additionalSettings%22%3A%22%7B%5C%22includePrereleases%5C%22%3Atrue%2C%5C%22filterReleaseTitlesByRegEx%5C%22%3A%5C%22%5ESnapshot%5C%22%2C%5C%22useLatestAssetDateAsReleaseDate%5C%22%3Atrue%2C%5C%22versionDetection%5C%22%3Afalse%2C%5C%22releaseDateAsVersion%5C%22%3Atrue%2C%5C%22apkFilterRegEx%5C%22%3A%5C%22fdroid-.%2A-debug-%5C%5C%5C%5Cd%2B%5C%5C%5C%5C.apk%24%5C%22%2C%5C%22autoApkFilterByArch%5C%22%3Atrue%2C%5C%22appName%5C%22%3A%5C%22Meshtastic%20Snapshot%20%28fdroid%29%5C%22%7D%22%7D">Add</a></td> </tr> </tbody> </table></div> <!-- END GENERATED LINKS --> <p>Prefer a file? Import one of these via <strong>Import/Export → Obtainium import</strong>. Pick the one matching the flavor you want for the <em>release</em> build; each also sets up <strong>both</strong> snapshot flavors, which can coexist:</p> <ul> <li><a href="https://github.com/meshtastic/Meshtastic-Android/blob/main/obtainium/meshtastic-obtainium-export-google.json"><code class="language-plaintext highlighter-rouge">meshtastic-obtainium-export-google.json</code></a> — <code class="language-plaintext highlighter-rouge">google</code> stable + both snapshots</li> <li><a href="https://github.com/meshtastic/Meshtastic-Android/blob/main/obtainium/meshtastic-obtainium-export-fdroid.json"><code class="language-plaintext highlighter-rouge">meshtastic-obtainium-export-fdroid.json</code></a> — <code class="language-plaintext highlighter-rouge">fdroid</code> stable + both snapshots</li> </ul> <h3 id="how-many-entries-can-you-have-at-once"> <a href="#how-many-entries-can-you-have-at-once" class="anchor-heading" aria-labelledby="how-many-entries-can-you-have-at-once"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> How many entries can you have at once? </h3> <p>Obtainium keys apps by application ID, so the rule follows from ours:</p> <div class="table-wrapper"><table> <thead> <tr> <th> </th> <th>Application ID</th> <th>Can coexist?</th> </tr> </thead> <tbody> <tr> <td>Release builds — stable, open, closed, bleeding edge, either flavor</td> <td><code class="language-plaintext highlighter-rouge">com.geeksville.mesh</code></td> <td><strong>No</strong> — pick exactly one</td> </tr> <tr> <td>Snapshot, <code class="language-plaintext highlighter-rouge">google</code></td> <td><code class="language-plaintext highlighter-rouge">com.geeksville.mesh.google.debug</code></td> <td>Yes</td> </tr> <tr> <td>Snapshot, <code class="language-plaintext highlighter-rouge">fdroid</code></td> <td><code class="language-plaintext highlighter-rouge">com.geeksville.mesh.fdroid.debug</code></td> <td>Yes</td> </tr> </tbody> </table></div> <p>So one release entry, plus up to both snapshots, is the maximum. Tapping a second <em>release</em> link switches that single entry to the new channel or flavor instead of adding one, and importing both files in turn leaves you with whichever flavor’s release entry came last. The snapshots are unaffected either way.</p> <p>To set things up by hand instead, follow the rest of this page.</p><hr /> <h2 id="manual-setup"> <a href="#manual-setup" class="anchor-heading" aria-labelledby="manual-setup"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Manual setup </h2> <ol> <li>Install Obtainium (<a href="https://github.com/ImranR98/Obtainium/releases">GitHub releases</a> or <a href="https://apt.izzysoft.de/fdroid/index/apk/dev.imranr.obtainium">F-Droid / IzzyOnDroid</a>).</li> <li>Tap <strong>Add App</strong>.</li> <li><strong>App Source URL:</strong> <code class="language-plaintext highlighter-rouge">https://github.com/meshtastic/Meshtastic-Android</code></li> <li>Set the options for the channel you want (below).</li> <li>Tap <strong>Add</strong>, then <strong>Install</strong>.</li> </ol> <h3 id="stable"> <a href="#stable" class="anchor-heading" aria-labelledby="stable"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Stable </h3> <ul> <li><strong>Include prereleases:</strong> off</li> <li><em>(optional, stricter)</em> <strong>Verify the ‘latest’ tag:</strong> on</li> <li><strong>Filter APKs by regular expression:</strong> see <a href="#picking-the-apk">Picking the APK</a></li> </ul> <h3 id="open-beta"> <a href="#open-beta" class="anchor-heading" aria-labelledby="open-beta"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Open beta </h3> <ul> <li><strong>Include prereleases:</strong> on</li> <li><strong>Filter release titles by regular expression:</strong> <code class="language-plaintext highlighter-rouge">-open</code></li> <li><strong>Filter APKs by regular expression:</strong> see <a href="#picking-the-apk">Picking the APK</a></li> </ul> <h3 id="closed-beta"> <a href="#closed-beta" class="anchor-heading" aria-labelledby="closed-beta"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Closed beta </h3> <ul> <li><strong>Include prereleases:</strong> on</li> <li><strong>Filter release titles by regular expression:</strong> <code class="language-plaintext highlighter-rouge">-closed</code></li> <li><strong>Filter APKs by regular expression:</strong> see <a href="#picking-the-apk">Picking the APK</a></li> </ul> <h3 id="bleeding-edge-newest-promoted-test-build-any-channel"> <a href="#bleeding-edge-newest-promoted-test-build-any-channel" class="anchor-heading" aria-labelledby="bleeding-edge-newest-promoted-test-build-any-channel"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Bleeding edge (newest promoted test build, any channel) </h3> <ul> <li><strong>Include prereleases:</strong> on</li> <li><strong>Filter release titles by regular expression:</strong> <code class="language-plaintext highlighter-rouge">-(closed|open)</code></li> <li><strong>Filter APKs by regular expression:</strong> see <a href="#picking-the-apk">Picking the APK</a></li> </ul> <p>Obtainium installs the newest promoted prerelease — whatever is currently in open or closed. The title filter is required to skip the always-newer <code class="language-plaintext highlighter-rouge">snapshot</code> prerelease; without it Obtainium would follow snapshot instead.</p> <h3 id="snapshot-latest-commit-on-main"> <a href="#snapshot-latest-commit-on-main" class="anchor-heading" aria-labelledby="snapshot-latest-commit-on-main"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Snapshot (latest commit on <code class="language-plaintext highlighter-rouge">main</code>) </h3> <ul> <li><strong>Include prereleases:</strong> on</li> <li><strong>Filter release titles by regular expression:</strong> <code class="language-plaintext highlighter-rouge">^Snapshot</code></li> <li><strong>Use the latest asset date as the release date:</strong> on</li> <li><strong>Version detection:</strong> off, with <strong>Release date as version</strong> on</li> <li><strong>Filter APKs by regular expression:</strong> debug-signed names, see below</li> </ul> <p>Follows <code class="language-plaintext highlighter-rouge">main</code> directly — updates on every push. These are <strong>debug builds</strong> (<code class="language-plaintext highlighter-rouge">.debug</code> package, debug key), so they install as a separate app and won’t disturb a stable/beta install. The <code class="language-plaintext highlighter-rouge">snapshot</code> tag never changes, so there is no version string to compare — date-based pseudo-versioning is what makes updates detectable.</p> <p>The APKs are named <code class="language-plaintext highlighter-rouge">…-debug-<versionCode>.apk</code> (not <code class="language-plaintext highlighter-rouge">-release.apk</code>), so use debug-suffixed filters:</p> <div class="table-wrapper"><table> <thead> <tr> <th>You want</th> <th>Regex</th> </tr> </thead> <tbody> <tr> <td><code class="language-plaintext highlighter-rouge">google</code> flavor, most phones (arm64)</td> <td><code class="language-plaintext highlighter-rouge">google-arm64-v8a-debug-\d+\.apk</code></td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">fdroid</code> flavor, most phones (arm64)</td> <td><code class="language-plaintext highlighter-rouge">fdroid-arm64-v8a-debug-\d+\.apk</code></td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">fdroid</code> flavor, one-size-fits-all</td> <td><code class="language-plaintext highlighter-rouge">fdroid-universal-debug-\d+\.apk</code></td> </tr> </tbody> </table></div> <p>Snapshot releases attach only the debug APKs — no <code class="language-plaintext highlighter-rouge">.aab</code> or desktop installers.</p> <blockquote> <p><strong>If you build debug locally, uninstall your local build first.</strong> CI signs snapshots with <em>its own</em> debug keystore, not yours (<code class="language-plaintext highlighter-rouge">07c16b98…</code> from CI versus <code class="language-plaintext highlighter-rouge">88914c61…</code> from a local machine, for example). Since both produce the same <code class="language-plaintext highlighter-rouge">com.geeksville.mesh.<flavor>.debug</code> package name, Android refuses the swap with <code class="language-plaintext highlighter-rouge">INSTALL_FAILED_UPDATE_INCOMPATIBLE</code>. The “installs as a separate app” note above holds against <em>release</em> builds, not against your own debug builds.</p> </blockquote> <p>Two more things about release selection apply to every channel above, not just snapshot:</p> <blockquote> <p><strong>Leave “Fallback to older releases” ON.</strong> It is not a channel-strictness setting, and turning it off breaks every channel config here. Obtainium’s release loop breaks out after the first candidate when fallback is off, so a title filter never gets to skip past releases that don’t match — a config pinned to <code class="language-plaintext highlighter-rouge">-open</code> or <code class="language-plaintext highlighter-rouge">^Snapshot</code> then fails with “Could not find a suitable release” because production <code class="language-plaintext highlighter-rouge">v2.7.14</code> sits at the head of the list. The title filter is what pins the channel; fallback is what lets Obtainium walk down to the newest release that matches it.</p> <p><strong>If your channel filter still finds nothing:</strong> that channel has no published release yet — common early in a version cycle. Check the <a href="https://github.com/meshtastic/Meshtastic-Android#get-meshtastic">project README</a> for what is live, or use the <em>Bleeding edge</em> form to follow whichever of open/closed is newest.</p> </blockquote><hr /> <h2 id="picking-the-apk"> <a href="#picking-the-apk" class="anchor-heading" aria-labelledby="picking-the-apk"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Picking the APK </h2> <p>Each release attaches the <code class="language-plaintext highlighter-rouge">google</code> flavor APK, several <code class="language-plaintext highlighter-rouge">fdroid</code> flavor APKs, plus an <code class="language-plaintext highlighter-rouge">.aab</code> (not installable) and the desktop installers. Obtainium ignores non-APK assets on its own, but the two flavors share the <code class="language-plaintext highlighter-rouge">com.geeksville.mesh</code> package name, so exactly one must be pinned with <strong>Filter APKs by regular expression</strong>:</p> <div class="table-wrapper"><table> <thead> <tr> <th>You want</th> <th>Regex</th> </tr> </thead> <tbody> <tr> <td><strong>Recommended for testing — <code class="language-plaintext highlighter-rouge">google</code> flavor</strong></td> <td><code class="language-plaintext highlighter-rouge">google-release\.apk</code></td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">fdroid</code> flavor, most phones (arm64)</td> <td><code class="language-plaintext highlighter-rouge">fdroid-arm64-v8a-release\.apk</code></td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">fdroid</code> flavor, one-size-fits-all</td> <td><code class="language-plaintext highlighter-rouge">fdroid-universal-release\.apk</code></td> </tr> <tr> <td><code class="language-plaintext highlighter-rouge">fdroid</code> flavor, let Obtainium pick the ABI</td> <td><code class="language-plaintext highlighter-rouge">fdroid-.*-release\.apk</code> with <strong>Auto-select by architecture</strong> on</td> </tr> </tbody> </table></div> <p><strong>Use the <code class="language-plaintext highlighter-rouge">google</code> flavor while testing.</strong> It ships Firebase Crashlytics and Datadog RUM, so the crashes and errors you hit get reported back to the team — which is the whole point of a test phase. It also has Google push (FCM) and Google Maps. Pick an <code class="language-plaintext highlighter-rouge">fdroid</code> flavor only if you’d rather not send that telemetry. You can change your mind later: both flavors share the release key, so Obtainium updates across the flavor boundary without an uninstall. Expect the <code class="language-plaintext highlighter-rouge">google</code> flavor to show its analytics-consent screen the first time it starts.</p><hr /> <h2 id="version-detection-turns-itself-off--thats-expected"> <a href="#version-detection-turns-itself-off--thats-expected" class="anchor-heading" aria-labelledby="version-detection-turns-itself-off--thats-expected"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Version detection turns itself off — that’s expected </h2> <p>Release tags read <code class="language-plaintext highlighter-rouge">vX.Y.Z</code>, while our installed <code class="language-plaintext highlighter-rouge">versionName</code> reads <code class="language-plaintext highlighter-rouge">X.Y.Z (versionCode) <flavor></code>. Obtainium tries to reconcile the two against a list of standard version formats, all of which are <strong>fully anchored</strong> — none of them tolerates the spaces and parentheses in our <code class="language-plaintext highlighter-rouge">versionName</code>. So on the first update check Obtainium logs <code class="language-plaintext highlighter-rouge">Could not reconcile version formats for: com.geeksville.mesh</code>, sets <strong>Version detection</strong> to off for the app, and records the release tag as the installed version.</p> <p><strong>This is harmless and you should not try to fix it.</strong> With detection off, Obtainium compares the release tag it last installed against the newest tag, so updates are still detected and installed normally. What you lose is only the ability to notice a version installed <em>outside</em> Obtainium.</p> <p>In particular, <strong>do not set “Trim the version string”</strong> to <code class="language-plaintext highlighter-rouge">\d+\.\d+\.\d+</code> hoping to fix it. That setting only rewrites the version parsed from the <em>release</em>; the unparseable side is the installed <code class="language-plaintext highlighter-rouge">versionName</code>, which it never touches — so reconciliation fails exactly as before. It also adds a failure mode: if the regex ever matches nothing (a tag without a three-part version), the update check fails outright with a “no version found” error instead of degrading quietly.</p> <p>Snapshot is a separate case: its tag is the literal string <code class="language-plaintext highlighter-rouge">snapshot</code> and never changes, so tag comparison can’t work either. Those configs use date-based pseudo-versioning (<strong>Release date as version</strong> plus <strong>Use the latest asset date as the release date</strong>) instead.</p><hr /> <h2 id="the-config-format"> <a href="#the-config-format" class="anchor-heading" aria-labelledby="the-config-format"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> The config format </h2> <p>An Obtainium app configuration is a flat JSON object. Four keys are required — <code class="language-plaintext highlighter-rouge">id</code> (Android package name), <code class="language-plaintext highlighter-rouge">url</code> (the GitHub repo, which is what selects the source), <code class="language-plaintext highlighter-rouge">author</code>, and <code class="language-plaintext highlighter-rouge">name</code>. Everything else goes in <code class="language-plaintext highlighter-rouge">additionalSettings</code>, a <strong>JSON string nested inside the JSON</strong>, so its quotes and backslashes are escaped one extra level. Any key you omit takes its default.</p> <p>The same object travels in three shapes:</p> <div class="table-wrapper"><table> <thead> <tr> <th>Shape</th> <th>Format</th> <th>Used for</th> </tr> </thead> <tbody> <tr> <td>Import/export file</td> <td><code class="language-plaintext highlighter-rouge">{"apps":[…],"settings":null}</code></td> <td><strong>Import/Export → Obtainium import</strong></td> </tr> <tr> <td>Deep link</td> <td><code class="language-plaintext highlighter-rouge">obtainium://app/<percent-encoded JSON></code></td> <td>one-tap add with settings baked in</td> </tr> <tr> <td>Config-site entry</td> <td><code class="language-plaintext highlighter-rouge">{"configs":[…],"icon","categories","description"}</code></td> <td>PRs to <a href="https://apps.obtainium.imranr.dev">apps.obtainium.imranr.dev</a></td> </tr> </tbody> </table></div> <p><code class="language-plaintext highlighter-rouge">obtainium://add/<url></code> only prefills the Add-App page; <code class="language-plaintext highlighter-rouge">obtainium://app/…</code> is what carries settings. Wrapping a link as <code class="language-plaintext highlighter-rouge">https://apps.obtainium.imranr.dev/redirect.html?r=obtainium://app/…</code> keeps it clickable where a custom scheme wouldn’t linkify.</p> <p>Obtainium keys apps by package name, so the two stable flavors cannot coexist as separate entries — the config-site submission expresses them as alternate <code class="language-plaintext highlighter-rouge">configs</code> with an <code class="language-plaintext highlighter-rouge">altLabel</code> each. See <a href="https://github.com/meshtastic/Meshtastic-Android/blob/main/obtainium/README.md"><code class="language-plaintext highlighter-rouge">obtainium/README.md</code></a> for the full key reference and the submission checklist.</p><hr /> <h2 id="notes"> <a href="#notes" class="anchor-heading" aria-labelledby="notes"><svg viewBox="0 0 16 16" aria-hidden="true"><use xlink:href="#svg-link"></use></svg></a> Notes </h2> <ul> <li><strong>Play Protect may interrupt the install</strong> with “App scan recommended — Play Protect hasn’t seen this app before,” offering <em>Scan app</em> or <em>Don’t install app</em>. <em>Scan app</em> uploads the APK to Google. Declining cancels the install, so on a device with Play Services you’ll need to allow the scan, or install with <code class="language-plaintext highlighter-rouge">adb install</code> instead. This showed up for a debug-signed snapshot but not for a release-signed build.</li> <li><strong>Obtainium’s first launch shows two full-screen dialogs</strong> (a welcome note and a Play-certification note). They swallow an incoming <code class="language-plaintext highlighter-rouge">obtainium://</code> link, so dismiss them before using a one-tap link.</li> <li><strong>Track without installing:</strong> turn on <strong>Track-only</strong> to get update notifications without Obtainium downloading anything.</li> <li><strong>Snapshot filenames carry the versionCode</strong> so that a moving tag, which reuses the same release and asset URLs forever, still produces a distinguishable asset on every build.</li> </ul> </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>
|