fdroid-inclusion-gate / index.html
getreadystack's picture
Upload index.html with huggingface_hub
fed129c verified
Raw History Blame Contribute Delete
51.4 kB
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Fdroid Inclusion Policy check — check gradle for forbidden</title>
<meta name="description" content="This free browser tool checks Gradle files for dependencies forbidden by the F-Droid Inclusion Policy and lists FOSS replacements.">
<style>
:root{color-scheme:light}body{margin:0;font:15px/1.55 system-ui,-apple-system,"Segoe UI",sans-serif;background:#faf9f5;color:#1a1512}
.wrap{max-width:960px;margin:0 auto;padding:28px 18px 60px}h1{font-size:26px;margin:0 0 6px}.lede{color:#4a403a;margin:0 0 16px}
textarea{width:100%;min-height:260px;font:13px/1.45 ui-monospace,Menlo,Consolas,monospace;padding:12px;border:1px solid #d9d0c4;border-radius:8px;box-sizing:border-box}
.bar{display:flex;gap:10px;align-items:center;margin:12px 0}button{font:600 15px system-ui;padding:10px 18px;border:0;border-radius:8px;background:#1a1512;color:#fff;cursor:pointer}
input[type=date]{font:14px system-ui;padding:8px;border:1px solid #d9d0c4;border-radius:6px}
table{border-collapse:collapse;width:100%;font-size:14px;margin-top:10px}th,td{text-align:left;padding:8px 10px;border-bottom:1px solid #eee;vertical-align:top}th{font-size:12px;letter-spacing:.06em;text-transform:uppercase;color:#7a6e65}
.sev.error{color:#a8551f;font-weight:700}.sev.warn,.sev.warning{color:#96691f}.ok{color:#2a6446;font-weight:600}.sum{font-weight:600}
.note{font-size:13px;color:#7a6e65;margin-top:18px}#paid{margin-top:22px;padding:14px 16px;border:1px solid #e2d8cb;border-radius:10px;background:#fffdfa}
</style>
<script data-grow-initializer="">!(function(){window.growMe||((window.growMe=function(e){window.growMe._.push(e);}),(window.growMe._=[]));var e=document.createElement("script");(e.type="text/javascript"),(e.src="https://faves.grow.me/main.js"),(e.defer=!0),e.setAttribute("data-grow-faves-site-id","U2l0ZTphZjljMzgyMC0yMmU5LTRhYjgtYTIxNS03OTRhMGY1N2ExYzM=");var t=document.getElementsByTagName("script")[0];t.parentNode.insertBefore(e,t);})();</script>
<!--yj-canon-s101--><link rel="canonical" href="https://getreadystack.com/tools/fdroid-inclusion-gate"><style data-yj-mfit="1">#out{max-width:100%}#out td,#out th{overflow-wrap:anywhere}#out pre,#out code{white-space:pre-wrap;overflow-wrap:anywhere}@media (max-width:640px){table{display:block;max-width:100%;overflow-x:auto}pre{max-width:100%;overflow-x:auto}code,a{overflow-wrap:anywhere}img,svg,video,canvas,iframe{max-width:100%}textarea,input,select{max-width:100%;box-sizing:border-box}.yj-lgbar{position:static!important;justify-content:flex-end;margin:0 0 10px}h1,h2,h3,h4,p,li,dd,dt,blockquote,figcaption,summary,label,.res,.lead{overflow-wrap:anywhere}label{max-width:100%;box-sizing:border-box}#out table:not(:has(tr>:nth-child(4))){display:table;table-layout:fixed;width:100%}#out table:has(tr>:nth-child(4)),#out table:has(tr>:nth-child(4)) tbody,#out table:has(tr>:nth-child(4)) tr,#out table:has(tr>:nth-child(4)) td{display:block;width:auto;overflow:visible}#out table:has(tr>:nth-child(4)) thead{display:none}#out table:has(tr>:nth-child(4)) tr{padding:10px 0;border-bottom:1px solid rgba(128,128,128,.28)}#out table:has(tr>:nth-child(4)) td{border:0;padding:1px 0}#out table:has(tr>:nth-child(4)) td:nth-child(-n+3){display:inline-block;margin-right:10px;font-size:12px;opacity:.78}}</style>
</head>
<body>
<div class="wrap">
<h1>Fdroid Inclusion Policy check — check gradle for forbidden</h1>
<p class="lede">F-Droid migration check for build.gradle: flags Play Services, Firebase, Crashlytics, Play Billing and tracker SDKs the Inclusion Policy forbids, with the FOSS replacement line for each. Runs entirely in your browser — nothing is uploaded.</p>
<textarea id="src" spellcheck="false">plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("com.google.gms.google-services")
id("com.google.firebase.crashlytics")
}
android {
namespace = "com.example.trailnotes"
compileSdk = 36
defaultConfig {
applicationId = "com.example.trailnotes"
minSdk = 24
targetSdk = 36
versionCode = 42
versionName = "2.3.0"
}
}
repositories {
google()
mavenCentral()
jcenter()
maven { url = uri("https://maven.example-sdk.com/releases") }
}
dependencies {
implementation("androidx.core:core-ktx:1.17.0")
implementation("androidx.appcompat:appcompat:1.7.1")
implementation(platform("com.google.firebase:firebase-bom:34.3.0"))
implementation("com.google.firebase:firebase-messaging")
implementation("com.google.firebase:firebase-analytics")
implementation("com.google.firebase:firebase-crashlytics")
implementation("com.google.android.gms:play-services-location:21.3.0")
implementation("com.google.android.gms:play-services-maps:19.2.0")
implementation("com.google.android.play:review-ktx:2.0.2")
implementation("com.android.billingclient:billing-ktx:8.0.0")
implementation("com.google.mlkit:barcode-scanning:17.3.0")
testImplementation("junit:junit:4.13.2")
}</textarea>
<div class="bar"><button id="go">Check</button><label>as of <input type="date" id="today"></label></div>
<div id="out" aria-live="polite"></div>
<div id="paid"><strong>Free here:</strong> Maps every non-free dependency, plugin and Maven repo in the open Gradle file to its line, with the FOSS replacement line from the F-Droid Inclusion Policy. <strong>In the editor:</strong> The whole workspace: a dated F-Droid migration plan across every module, foss/play flavour split per item, and a CI gate that fails a build adding new non-free use. — that part asks for a licence key.</div>
<p id="buy"><a href="https://getreadystack.com/api/buy/cl/polar_cl_cwMtl6XHYorRHyRdNoC1vSKhkAoFBIfoP1YYc1Tj4Be">Full version: whole-workspace F-Droid migration plan + CI gate — $29 once, one licence key per person or team seat</a></p>
<p class="note">Same engine as the VS Code extension, byte for byte.</p>
</div>
<script>var FDG_RULES = [
{
"id": "gms-location",
"sev": "error",
"re": "com\\.google\\.android\\.gms[.:]play-services-location|libs\\.play\\.services\\.location",
"title": "Play Services Location",
"af": "NonFreeDep",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "android.location.LocationManager (platform API, no dependency) in the foss flavour; keep playImplementation(\"com.google.android.gms:play-services-location\") only in play",
"msg": "Play Services Location (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "gms-maps",
"sev": "error",
"re": "com\\.google\\.android\\.gms[.:]play-services-maps|libs\\.play\\.services\\.maps|com\\.google\\.maps\\.android",
"title": "Google Maps SDK",
"af": "NonFreeDep",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "implementation(\"org.osmdroid:osmdroid-android\") (Apache-2.0) or implementation(\"org.maplibre.gl:android-sdk\") (BSD-2) in the foss flavour",
"msg": "Google Maps SDK (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "gms-ads",
"sev": "error",
"re": "com\\.google\\.android\\.gms[.:]play-services-ads|com\\.google\\.android\\.ump|libs\\.play\\.services\\.ads",
"title": "AdMob / UMP consent SDK",
"af": "Ads",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "Remove from the foss flavour: apps built on AdMob are not found in the F-Droid repo at all",
"msg": "AdMob / UMP consent SDK (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "gms-auth",
"sev": "error",
"re": "com\\.google\\.android\\.gms[.:]play-services-auth|androidx\\.credentials:credentials-play-services-auth",
"title": "Google Sign-In (Play Services Auth)",
"af": "NonFreeDep",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "implementation(\"net.openid:appauth\") (Apache-2.0) for OAuth in the foss flavour; keep credentials-play-services-auth as playImplementation",
"msg": "Google Sign-In (Play Services Auth) (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "gms-shortcuts",
"sev": "error",
"re": "androidx\\.core:core-google-shortcuts",
"title": "core-google-shortcuts",
"af": "NonFreeDep",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "Drop it: androidx.core:core ShortcutManagerCompat works without the Google shortcuts bridge",
"msg": "core-google-shortcuts (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "gms",
"sev": "error",
"re": "com\\.google\\.android\\.gms[.:](?!oss-licenses-plugin|strict-version-matcher-plugin)|libs\\.play\\.services\\.|libs\\.gms\\.",
"title": "Google Play Services",
"af": "NonFreeDep",
"src": "F-Droid Inclusion Policy",
"fix": "Move to playImplementation(...) inside a play product flavour and add a foss flavour that builds without it",
"msg": "Google Play Services (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "firebase-messaging",
"sev": "error",
"re": "com(\\.google)?\\.firebase[.:]firebase-messaging|libs\\.firebase\\.messaging|com\\.onesignal:OneSignal|me\\.pushy|com\\.microsoft\\.appcenter:appcenter-push",
"title": "FCM / proprietary push",
"af": "NonFreeNet",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "implementation(\"org.unifiedpush.android:connector\") (UnifiedPush) in the foss flavour; keep FCM as playImplementation",
"msg": "FCM / proprietary push (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "crashlytics",
"sev": "error",
"re": "crashlytics|com\\.bugsense|com\\.crittercism|com\\.tencent\\.bugly",
"title": "Crashlytics / closed crash reporter",
"af": "Tracking",
"src": "F-Droid Inclusion Policy",
"fix": "implementation(\"ch.acra:acra-mail\") (ACRA, Apache-2.0), opt-in and off by default, or F-Droid adds the Tracking anti-feature",
"msg": "Crashlytics / closed crash reporter (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "firebase-analytics",
"sev": "error",
"re": "com(\\.google)?\\.firebase[.:]firebase-analytics|libs\\.firebase\\.analytics|com\\.flurry\\.android|com\\.umeng|com\\.baidu\\.mobstat|com\\.yandex\\.android(?!:authsdk)|com\\.hypertrack(?!:hyperlog)|com\\.facebook\\.android",
"title": "Analytics / tracker SDK",
"af": "Tracking",
"src": "F-Droid Inclusion Policy",
"fix": "Remove from the foss flavour: proprietary tracking and analytics libraries are forbidden in every F-Droid app",
"msg": "Analytics / tracker SDK (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "firebase",
"sev": "error",
"re": "com(\\.google)?\\.firebase[.:](?!firebase-jobdispatcher|geofire-java)|libs\\.firebase\\.",
"title": "Firebase",
"af": "NonFreeNet",
"src": "F-Droid Inclusion Policy",
"fix": "Move to playImplementation(...) in a play flavour; the foss flavour must build with no Firebase artifact",
"msg": "Firebase (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "google-services-plugin",
"sev": "warn",
"re": "com\\.google\\.gms[.:]google-services|libs\\.plugins\\.google\\.services|gms\\.google\\.services",
"title": "google-services Gradle plugin",
"af": "NonFreeNet",
"src": "F-Droid Inclusion Policy",
"fix": "Apply it only when the play flavour is built (it reads google-services.json for Firebase/GMS); the foss build needs neither",
"msg": "google-services Gradle plugin"
},
{
"id": "play-core",
"sev": "error",
"re": "com\\.google\\.android\\.play:(app-update|asset-delivery|core|feature-delivery|review)|androidx\\.navigation:navigation-dynamic-features|com\\.github\\.SanojPunchihewa:InAppUpdater|com\\.suddenh4x\\.ratingdialog:awesome-app-rating|libs\\.play\\.(app\\.update|review|core)",
"title": "Play Core (in-app update / review / delivery)",
"af": "NonFreeDep",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "Move to playImplementation(...); on F-Droid the client itself delivers updates, so the foss flavour needs no in-app updater",
"msg": "Play Core (in-app update / review / delivery) (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "billing",
"sev": "error",
"re": "com\\.android\\.billingclient|com\\.google\\.androidbrowserhelper:billing|com\\.anjlab\\.android\\.iab\\.v3:library|com\\.github\\.penn5:donations|com\\.revenuecat\\.purchases|com\\.paypal|libs\\.billing",
"title": "Play Billing / closed payment SDK",
"af": "NonFreeNet",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "Move to playImplementation(...); in the foss flavour link a donation page (funding.json / FUNDING.yml)",
"msg": "Play Billing / closed payment SDK (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "install-referrer",
"sev": "error",
"re": "com\\.android\\.installreferrer",
"title": "Play Install Referrer",
"af": "NonFreeDep",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "Move to playImplementation(...) or remove: installs from F-Droid carry no Play referrer",
"msg": "Play Install Referrer (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "ima",
"sev": "error",
"re": "com\\.google\\.ads|com\\.google\\.android\\.exoplayer:extension-ima|androidx\\.media3:media3-exoplayer-ima",
"title": "Google IMA ads SDK",
"af": "Ads",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "Remove from the foss flavour; keep implementation(\"androidx.media3:media3-exoplayer\") without the IMA extension",
"msg": "Google IMA ads SDK (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "mlkit",
"sev": "error",
"re": "com\\.google\\.mlkit|io\\.github\\.g00fy2\\.quickie",
"title": "ML Kit",
"af": "NonFreeDep",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "implementation(\"com.journeyapps:zxing-android-embedded\") (Apache-2.0) for barcode scanning in the foss flavour",
"msg": "ML Kit (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "google-libraries",
"sev": "error",
"re": "com\\.google\\.android\\.libraries(?!\\.mapsplatform\\.secrets-gradle-plugin)",
"title": "Google Android Libraries (Places etc.)",
"af": "NonFreeNet",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "Move to playImplementation(...); for place search use a free geocoder such as Nominatim over HTTPS",
"msg": "Google Android Libraries (Places etc.) (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "mapbox",
"sev": "error",
"re": "com\\.mapbox(?!\\.mapboxsdk:mapbox-sdk-(services|geojson|turf):[3-5])",
"title": "Mapbox SDK (v2+ licence)",
"af": "NonFreeNet",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "implementation(\"org.maplibre.gl:android-sdk\") (MapLibre, BSD-2), the open fork of Mapbox GL Native",
"msg": "Mapbox SDK (v2+ licence) (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "aws",
"sev": "error",
"re": "com\\.amazonaws",
"title": "AWS Android SDK",
"af": "NonFreeNet",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "Move to playImplementation(...) or call the service REST API over plain OkHttp in the foss flavour",
"msg": "AWS Android SDK (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "objectbox",
"sev": "error",
"re": "io\\.objectbox:objectbox-gradle-plugin",
"title": "ObjectBox Gradle plugin",
"af": "NonFreeDep",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "implementation(\"androidx.room:room-runtime\") (Apache-2.0) as the on-device database",
"msg": "ObjectBox Gradle plugin (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "other-nonfree",
"sev": "error",
"re": "com\\.github\\.junrar:junrar|7-Zip-JBinding-4Android|MathParser\\.org-mXparser:[5-9]|com\\.geetest|com\\.cloudrail|com\\.garmin\\.connectiq:ciq-companion-app-sdk|com\\.google\\.android\\.(support|wearable):wearable|io\\.github\\.sinaweibosdk|fingerprintidentify|FingerprintIdentify",
"title": "Library listed NonFree by fdroidserver",
"af": "NonFreeDep",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "Replace with a FLOSS library or move to a non-F-Droid flavour; fdroidserver's scanner rejects this artifact",
"msg": "Library listed NonFree by fdroidserver (non-free, forbidden by the F-Droid Inclusion Policy)"
},
{
"id": "jcenter",
"sev": "warn",
"re": "\\bjcenter\\s*\\(\\s*\\)",
"title": "jcenter() repository",
"af": "",
"src": "fdroidserver scanner.py SUSS_DEFAULT",
"fix": "mavenCentral(): jcenter is not in fdroidserver's allowed repository list",
"msg": "jcenter() repository"
},
{
"id": "repo-unknown",
"sev": "warn",
"doc": "repo",
"title": "Maven repo outside the trusted list",
"af": "",
"src": "F-Droid Inclusion Policy",
"fix": "Use Maven Central, Google Maven, OSS Sonatype, OSS JFrog, JitPack.io or Clojars, or build the library from source",
"msg": "Maven repo outside the trusted list"
},
{
"id": "no-foss-flavour",
"sev": "warn",
"doc": "flavour",
"title": "No foss product flavour",
"af": "",
"src": "F-Droid Inclusion Policy",
"fix": "productFlavors { create(\"foss\") { dimension = \"store\" }; create(\"play\") { dimension = \"store\" } } and move every item above to playImplementation",
"msg": "No foss product flavour"
},
{
"id": "isolated",
"sev": "info",
"doc": "isolated",
"title": "Non-free artifact isolated in a flavour",
"af": "",
"src": "F-Droid Inclusion Policy",
"fix": "None needed if the foss flavour builds: point F-Droid's build recipe at gradle: [foss]",
"msg": "Non-free artifact isolated in a flavour"
}
];</script>
<script>// F-Droid Inclusion Gate engine: maps non-free Gradle dependencies, plugins and Maven repos
// to their line, with the FOSS replacement. Same file runs in Node (extension) and the browser (web page).
(function () {
var RULES = (typeof module !== 'undefined') ? require('./rules.json') : window.FDG_RULES;
// fdroidserver scanner.py allowed_repos (Maven Central, Google Maven, Sonatype, JFrog, JitPack, Clojars, Gradle plugins)
var ALLOWED = ['repo1.maven.org/maven2', 'jitpack.io', 'www.jitpack.io', 'repo.maven.apache.org/maven2',
'oss.jfrog.org/artifactory/oss-snapshot-local', 'central.sonatype.com/repository/maven-snapshots',
'oss.sonatype.org/content/', 's01.oss.sonatype.org/content/', 'oss.sonatype.org/service/local/staging/deploy/maven2',
's01.oss.sonatype.org/service/local/staging/deploy/maven2', 'clojars.org/repo', 'repo.clojars.org',
's3.amazonaws.com/repo.commonsware.com', 'plugins.gradle.org/m2', 'maven.google.com'];
var FOSS = /^(foss|fdroid|libre|free|floss|oss|nogms|nogoogle)/i;
var NEUTRAL = /^(test|androidTest|debug|release|kapt|ksp|annotationProcessor|coreLibraryDesugaring|lint|testFixtures|detekt)$/i;
var CFG = /^\s*([a-z][A-Za-z0-9]*?)(Implementation|Api|CompileOnly|RuntimeOnly)\b/;
var REPO = /\bmaven\b[^"'\n]*["']((?:https?|file):\/\/[^"']+)["']/;
var FOSS_FLAVOUR = /(?:create|register|maybeCreate)\s*\(\s*["'](foss|fdroid|libre|floss)["']|^\s*(foss|fdroid|libre|floss)\s*\{/im;
var byId = {};
RULES.forEach(function (r) { byId[r.id] = r; if (r.re) r._re = new RegExp(r.re); });
function strip(lines) {
var inBlock = false;
return lines.map(function (l) {
var out = l;
if (inBlock) { var e = out.indexOf('*/'); if (e < 0) return ''; out = out.slice(e + 2); inBlock = false; }
out = out.replace(/\/\*.*?\*\//g, '');
var s = out.indexOf('/*'); if (s >= 0) { out = out.slice(0, s); inBlock = true; }
out = out.replace(/(^|[^:"'])\/\/.*$/, '$1');
if (/^\s*#/.test(out)) out = '';
return out;
});
}
function repoAllowed(url) {
var u = url.replace(/^https?:\/\//, '');
if (/^file:\/\/\/usr\/share\/maven-repo/.test(url)) return true;
return ALLOWED.some(function (a) { return u.indexOf(a) === 0; });
}
function item(r, i, raw, extra) {
var f = { check: r.id, sev: r.sev, line: i + 1, msg: (extra || r.msg) + ' → ' + r.fix, fix: r.fix,
antiFeature: r.af || '', source: r.src, text: raw.trim().slice(0, 160) };
return f;
}
function check(text, opts) {
var raw = String(text || '').split(/\r?\n/);
var code = strip(raw);
var joined = code.join('\n');
var hasFlavours = /productFlavors/.test(joined);
var hasFoss = FOSS_FLAVOUR.test(joined);
var findings = [], nonfree = 0, isolated = 0, firstDep = -1;
code.forEach(function (l, i) {
if (!l.trim()) return;
if (firstDep < 0 && /^\s*dependencies\s*\{/.test(l)) firstDep = i;
var m = REPO.exec(l);
if (m && !repoAllowed(m[1])) {
findings.push(item(byId['repo-unknown'], i, raw[i], byId['repo-unknown'].title + ': ' + m[1]));
return;
}
for (var k = 0; k < RULES.length; k++) {
var r = RULES[k];
if (!r._re || !r._re.test(l)) continue;
var c = CFG.exec(l);
if (c && hasFlavours && !FOSS.test(c[1]) && !NEUTRAL.test(c[1])) {
isolated++;
findings.push(item(byId.isolated, i, raw[i], r.title + ' isolated in the "' + c[1] + '" flavour'));
} else {
if (r.sev === 'error') nonfree++;
findings.push(item(r, i, raw[i]));
}
break;
}
});
if (nonfree > 0 && !hasFoss) {
var nf = byId['no-foss-flavour'];
findings.push(item(nf, firstDep < 0 ? 0 : firstDep, raw[firstDep < 0 ? 0 : firstDep] || '',
nf.title + ': ' + nonfree + ' non-free item' + (nonfree === 1 ? '' : 's') + ' and no build variant F-Droid can build'));
}
findings.sort(function (a, b) { return a.line - b.line; });
return { findings: findings, summary: { nonfree: nonfree, isolated: isolated, fossFlavour: hasFoss, today: (opts && opts.today) || '' } };
}
var api = { engine: { check: check }, RULES: RULES, RULE_COUNT: RULES.length };
if (typeof window !== 'undefined') window.FDGENGINE = api;
if (typeof module !== 'undefined') module.exports = api;
})();
</script>
<script>// ⛔뼈대 — kit/scaffold.py 가 찍는다 (s143). findings [{check,sev,msg,line?}] → 글/HTML. 같은 파일이 Node 와 브라우저에서 산다.
'use strict';
function escapeHtml(s) { return String(s == null ? '' : s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;'); }
function summaryLine(res) {
const f = (res && res.findings) || []; const e = f.filter(function (x) { return String(x.sev || 'error').toLowerCase().startsWith('err'); }).length;
return f.length ? (f.length + ' finding' + (f.length === 1 ? '' : 's') + ' (' + e + ' error' + (e === 1 ? '' : 's') + ')') : 'Clean — no findings.';
}
function toText(res, label) {
const f = (res && res.findings) || [];
const lines = ['== ' + (label || '') + ' — ' + summaryLine(res)];
f.forEach(function (x) { lines.push(' ' + String(x.sev || 'error').toUpperCase().slice(0, 4) + ' ' + (x.line ? 'L' + x.line + ' ' : '') + (x.check || x.id || '') + ' ' + (x.msg || x.message || '')); });
return lines.join('\n');
}
function toHtml(res) {
const f = (res && res.findings) || [];
if (!f.length) return '<p class="ok">' + escapeHtml(summaryLine(res)) + '</p>';
const rows = f.map(function (x) { return '<tr><td class="sev ' + escapeHtml(String(x.sev || 'error')) + '">' + escapeHtml(String(x.sev || 'error')) + '</td><td>' + (x.line ? 'L' + escapeHtml(x.line) : '') + '</td><td><code>' + escapeHtml(x.check || x.id || '') + '</code></td><td>' + escapeHtml(x.msg || x.message || '') + '</td></tr>'; }).join('');
return '<p class="sum">' + escapeHtml(summaryLine(res)) + '</p><table><thead><tr><th>Level</th><th>Line</th><th>Check</th><th>What</th></tr></thead><tbody>' + rows + '</tbody></table>';
}
var FDGREPORT = { escapeHtml: escapeHtml, summaryLine: summaryLine, toText: toText, toHtml: toHtml };
if (typeof module !== 'undefined' && module.exports) module.exports = FDGREPORT;
else window.FDGREPORT = FDGREPORT;
</script>
<script>
(function(){
var src=document.getElementById('src'), out=document.getElementById('out'), go=document.getElementById('go'), today=document.getElementById('today');
today.value=new Date().toISOString().slice(0,10);
function run(){ try { var res=window.FDGENGINE.engine.check(src.value,{today:today.value}); out.innerHTML=window.FDGREPORT.toHtml(res); } catch(e){ out.innerHTML='<p class="sev error">Could not run: '+String(e.message||e)+'</p>'; } }
go.onclick=run; if (src.value.trim()) run();
})();
</script>
<section data-yj-install="free" style="margin:2rem auto 1rem;max-width:46rem;padding:1.25rem 1.25rem;border:1px solid #cfe3d6;border-radius:12px;background:#f6fbf8;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .4rem;font-size:1.2rem">Install free</h2><p style="margin:0 0 .8rem;color:#41505f">Free for the file open in your editor - no key, no limit. The workspace sweep and the report ask for a key.</p><a href="https://open-vsx.org/extension/readystack/fdroid-inclusion-gate" rel="noopener" data-yj-install-door="ovsx" onclick="try{navigator.sendBeacon('/api/ev',new Blob([JSON.stringify({t:'install',slug:'fdroid-inclusion-gate',src:'ovsx',path:location.pathname})],{type:'application/json'}))}catch(e){}" style="display:inline-block;margin:0 .5rem .5rem 0;padding:.6rem 1rem;border-radius:8px;background:#0f7b3f;color:#fff;text-decoration:none;font-weight:600">Open VSX (Cursor, VSCodium)</a><p style="margin:.6rem 0 0;font-size:.92rem;color:#41505f"><a href="https://getreadystack.com/api/buy/team" rel="nofollow" style="color:#0f7b3f;text-decoration:underline">Team? One key for every ReadyStack linter, 5 seats, $149 once</a></p></section>
<section data-yj-alert="rule" style="margin:1rem auto 1.5rem;max-width:46rem;padding:1.1rem 1.25rem;border:1px solid #d9dee6;border-radius:12px;background:#fbfcfd;font:15px/1.55 system-ui,-apple-system,Segoe UI,sans-serif"><b style="font-size:1.05rem">Get one email when this rule changes</b><div style="opacity:.78;margin:.35rem 0 .8rem">We watch the regulation and vendor sources behind F-Droid Inclusion Gate - non-free Gradle dependency map every day. When a rule changes, you get a single email with what changed and the updated check. No newsletter.</div><div style="display:flex;gap:8px;flex-wrap:wrap"><input id="yjae" type="email" placeholder="you@example.com" style="flex:1;min-width:210px;padding:11px 13px;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:#fff;color:inherit"><button id="yjab" style="padding:11px 18px;border-radius:8px;border:0;cursor:pointer;background:#0f7b3f;color:#fff;font:600 15px system-ui,sans-serif">Notify me</button></div><div id="yjao" hidden style="margin-top:.6rem;color:#0f7b3f">You're on the list. One email when it changes.</div><script>(function(){var b=document.getElementById("yjab"),e=document.getElementById("yjae");if(!b||!e)return;b.addEventListener("click",function(){var v=e.value.trim();if(!/.+@.+\..+/.test(v))return;try{localStorage.setItem("yj_em","1");}catch(_){}fetch(location.origin+"/api/subscribe",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:v,slug:"fdroid-inclusion-gate",lang:"en",source:"rule_alert"+((b.closest&&b.closest("section")&&b.closest("section").getAttribute("data-yj-moved"))?"_at_result":"")})}).then(function(r){var o=document.getElementById("yjao");o.hidden=false;if(!r.ok){o.textContent="Could not save - please try again.";}}).catch(function(){var o=document.getElementById("yjao");o.hidden=false;o.textContent="Could not save - please try again.";});});})();</script><script>(function(){var e=document.getElementById("yjae"),s=e&&e.closest?e.closest("section"):null,o=document.getElementById("out");if(!s||!o)return;var d=false;function mv(ev){if(d)return;if(ev&&ev.target&&s.contains(ev.target))return;d=true;var b=o;while(b.parentNode&&b.parentNode!==document.body&&b.parentNode.tagName!=="MAIN"&&b.parentNode.tagName!=="ARTICLE")b=b.parentNode;if(!b.parentNode||b===s||b.contains(s))return;try{b.parentNode.insertBefore(s,b.nextSibling);s.setAttribute("data-yj-moved","1");}catch(_){}}document.addEventListener("input",mv,true);document.addEventListener("change",mv,true);document.addEventListener("click",function(ev){var b=ev.target&&ev.target.closest?ev.target.closest("button,input[type=submit]"):null;if(b&&!b.closest("[data-yj],[data-yj-cta],[data-yj-install],[data-yj-alert],.yj-find,.yj-related,#yj-ask,.yjp"))mv(ev);},true);})();</script></section>
<section data-yj-cta="paid" style="margin:3rem auto;max-width:46rem;padding:1.5rem 1.25rem;border:1px solid #d8dee9;border-radius:12px;background:#fbfcfe;font:16px/1.6 system-ui,-apple-system,Segoe UI,sans-serif"><h2 style="margin:0 0 .5rem;font-size:1.25rem">Get the full version <span class="yj-price">$29</span></h2><p class="yj-cta-paid" style="margin:.25rem 0 .6rem;color:#1f2a37">The whole workspace: a dated F-Droid migration plan across every module, the foss/play flavour split per item, and a CI gate that fails a build adding new non-free use.</p><p class="yj-cta-pay" style="margin:.25rem 0 1rem;color:#5b6b7c;font-size:.94rem">One payment, one licence key for this tool. The key is shown right after payment.</p><a href="https://getreadystack.com/api/buy/cl/polar_cl_cwMtl6XHYorRHyRdNoC1vSKhkAoFBIfoP1YYc1Tj4Be" rel="nofollow" style="display:inline-block;padding:.7rem 1.3rem;border-radius:8px;background:#1b64f2;color:#fff;text-decoration:none;font-weight:600">Buy the full version — $29</a></section>
<!--yj-share-s101--><div style="margin:28px 0 8px;text-align:center"><button id="yjcp" type="button" style="font:inherit;padding:10px 18px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Copy link</button><button id="yjimg" type="button" style="font:inherit;padding:10px 18px;margin-left:8px;border:1px solid #8884;border-radius:10px;background:transparent;cursor:pointer">Save result as image</button></div><script>(function(){var b=document.getElementById("yjimg");if(!b)return;b.addEventListener("click",function(){var o=document.getElementById("out");var txt=(o&&(o.innerText||o.textContent)||"").trim();if(!txt){b.textContent="(no result yet)";setTimeout(function(){b.textContent="Save result as image";},1500);return;}var lines=txt.split(String.fromCharCode(10)).filter(function(x){return x.trim();}).slice(0,10);var W=1200,P=56,c=document.createElement("canvas");c.width=W;var lh=38,H=P*2+70+lines.length*lh+60;c.height=H;var x=c.getContext("2d");x.fillStyle="#1a1512";x.fillRect(0,0,W,H);x.fillStyle="#f3e9d2";x.font="600 34px system-ui,Segoe UI,Arial";var t=(document.title||"").split("|")[0].trim();x.fillText(t.slice(0,54),P,P+34);x.font="22px ui-monospace,Menlo,Consolas,monospace";x.fillStyle="#e8dcc4";for(var i=0;i<lines.length;i++){x.fillText(lines[i].slice(0,88),P,P+70+i*lh+22);}x.font="600 22px system-ui,Segoe UI,Arial";x.fillStyle="#c9a227";var d=location.hostname.replace("www.","");x.fillText(d,W-P-x.measureText(d).width,H-P+6);c.toBlob(function(bl){var u=URL.createObjectURL(bl),a=document.createElement("a");a.href=u;a.download=(t||"result").replace(/[^a-z0-9]+/gi,"-").toLowerCase()+"-"+d+".png";document.body.appendChild(a);a.click();setTimeout(function(){URL.revokeObjectURL(u);a.remove();},2000);},"image/png");});})();</script><script>(function(){var b=document.getElementById("yjcp");if(!b)return;var t=b.textContent;b.addEventListener("click",function(){var u=location.href.split("#")[0];function d(){b.textContent="Link copied";setTimeout(function(){b.textContent=t;},1800);}if(navigator.clipboard&&navigator.clipboard.writeText){navigator.clipboard.writeText(u).then(d,function(){prompt("",u);});}else{prompt("",u);}});})();</script>
<script data-yj="yj-view-beacon">(function(){try{var p=location.pathname;var k='yjv:'+p;if(sessionStorage.getItem(k))return;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",ref:(document.referrer||''),src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}catch(e){}})();</script>
<script data-yj="yj-use-beacon">(function(){try{var o=document.getElementById('out');if(!o)return;var p=location.pathname,k='yju:'+p,sent=false;function fire(){if(sent||sessionStorage.getItem(k))return;if(!(o.textContent||'').replace(/\s+/g,'').length)return;sent=true;sessionStorage.setItem(k,'1');fetch('/api/ev',{method:'POST',keepalive:true,headers:{'Content-Type':'application/json'},body:JSON.stringify({path:p,lang:"en",t:'use',src:(function(){try{return (new URLSearchParams(location.search).get('ref')||'').replace(/[^a-z_]/g,'').slice(0,12)}catch(e){return ''}})()})}).catch(function(){});}var mo=new MutationObserver(function(){fire();if(sent)mo.disconnect();});mo.observe(o,{childList:true,characterData:true,subtree:true});}catch(e){}})();</script><div class="yj-home-find"><div class="yj-lgbar"><a href="/" class="yj-lg on">EN</a><a href="/de/" class="yj-lg">DE</a><a href="/ja/" class="yj-lg">JA</a><a href="/es/" class="yj-lg">ES</a><a href="/pt/" class="yj-lg">PT</a></div><section class="yj-find"><h2>Find a tool</h2><input id="yjq" type="search" autocomplete="off" placeholder="Type a product name, format or task"><div id="yjr"></div></section><style>.yj-lgbar{position:absolute;top:14px;right:18px;display:flex;gap:6px;z-index:60}.yj-lg{font:600 12px/1 system-ui,sans-serif;padding:6px 9px;border-radius:4px;text-decoration:none;color:#8b96a3;border:1px solid rgba(140,150,165,.28)}.yj-lg:hover{color:#e8eaed;border-color:rgba(160,175,195,.6)}.yj-lg.on{color:#0e1116;background:#cfd6de;border-color:#cfd6de}.yj-find{max-width:760px;margin:34px auto 8px;padding:0 20px}.yj-find h2{font:600 20px/1.3 system-ui,sans-serif;margin:0 0 10px}#yjq{width:100%;box-sizing:border-box;padding:13px 15px;font:400 15.5px system-ui,sans-serif;border:1px solid rgba(140,150,165,.34);border-radius:8px;background:rgba(255,255,255,.04);color:inherit}#yjq:focus{outline:none;border-color:#7FD1C4}#yjr{margin-top:10px;display:flex;flex-direction:column;gap:6px}#yjr a{display:flex;justify-content:space-between;gap:12px;padding:11px 14px;border:1px solid rgba(140,150,165,.22);border-radius:6px;text-decoration:none;font:400 14.5px/1.4 system-ui,sans-serif;color:inherit}#yjr a:hover{border-color:#7FD1C4}#yjr .pr{font:600 13px system-ui,sans-serif;color:#5fbf9a;white-space:nowrap}</style><script>(function(){var q=document.getElementById("yjq"),r=document.getElementById("yjr"),D=null;function load(){if(D)return Promise.resolve(D);return fetch("/search-index.json").then(function(x){return x.json();}).then(function(j){D=j;return j;}).catch(function(){D=[];return D;});}function esc(t){return String(t).replace(/[&<>"]/g,function(c){return {"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;"}[c];});}function run(){var v=q.value.trim().toLowerCase();if(v.length<2){r.innerHTML="";return;}load().then(function(j){var w=v.split(/\s+/),o=[];for(var i=0;i<j.length&&o.length<12;i++){var t=j[i].t.toLowerCase();var ok=true;for(var k=0;k<w.length;k++){if(t.indexOf(w[k])<0){ok=false;break;}}if(ok)o.push(j[i]);}r.innerHTML=o.length?o.map(function(x){return "<a href=\""+esc(x.u)+"\">"+"<span>"+esc(x.t)+"</span>"+(x.p?"<span class=\"pr\">$"+x.p+"</span>":"")+"</a>";}).join(""):"";});}q.addEventListener("input",run);q.addEventListener("focus",load);})();</script></div><section data-yj="yj-facts-v1" style="max-width:920px;margin:48px auto 0;padding:0 20px;font-family:system-ui,sans-serif"><style>.yj-article p{font:400 15.5px/1.7 system-ui,sans-serif;color:#26313b;margin:0 0 12px;max-width:72ch}.yj-article h2,.yj-article h3,.yj-article h4{font:600 17px/1.35 system-ui,sans-serif;color:#1b1f24;margin:22px 0 8px}.yj-article table{border-collapse:collapse;margin:10px 0 16px;font:14px/1.45 system-ui,sans-serif}.yj-article th,.yj-article td{border:1px solid #e2e6ea;padding:6px 10px;text-align:left}.yj-article th{background:#f4f6f8}.yj-article pre{background:#0f1720;color:#e6edf3;padding:12px 14px;border-radius:8px;overflow:auto;font:13px/1.5 ui-monospace,Menlo,monospace}.yj-article code{font:13px ui-monospace,Menlo,monospace;background:#f1f3f5;padding:1px 4px;border-radius:3px}.yj-article pre code{background:none;padding:0}.yj-article ul,.yj-article ol{font:15px/1.6 system-ui,sans-serif;color:#26313b}</style><p style="font:400 13px/1.5 system-ui,sans-serif;color:#6b7680;margin:0 0 14px"><time datetime="2026-09-30">Updated 2026-09-30</time> · ReadyStack</p><script type="application/ld+json">{"@context":"https://schema.org","@type":"WebPage","dateModified":"2026-09-30","author":{"@type":"Organization","name":"ReadyStack"}}</script><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Worked example</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 16px;color:#5b6570">Real numbers from this tool, line by line.</p><div class="yj-article"><p><img src="https://getreadystack.com/img/promo/sku425922_result_card.jpg" alt="F-Droid Inclusion Gate - non-free Gradle dependency map" loading="lazy" style="max-width:100%;border-radius:6px"></p>
<p>Migrate from Google Play Services to F-Droid: the build.gradle lines that fail F-Droid&#x27;s build</p>
<p>10 non-free items in one sample build.gradle.kts stop an Android developer from shipping that app on F-Droid; the scan gave 14 findings in all. Six of them carry most of the work: Firebase Cloud Messaging, Crashlytics, Play Services Location, Google Maps, ML Kit barcode scanning and Play Billing.</p>
<p>F-Droid&#x27;s Inclusion Policy is direct about it. Proprietary tracking or advertising libraries and analytics tools &quot;such as Google Play Services and Firebase and Crashlytics and proprietary ad/tracking SDKs are strictly forbidden in all applications.&quot; Developers must implement &quot;either a FLOSS alternative or a build flavour that does not require these dependencies.&quot; And apps &quot;which fail to rebuild (such as requiring Play Services) or contain undisclosed anti-features will receive a rejection.&quot;</p>
<p>Why this quarter? Since August 31 2026, Google Play requires new apps and app updates to target Android 16 (API level 36). Teams making that change are already inside build.gradle, and a foss product flavour next to play is one more change in the same file.</p>
<p>What the check reads</p>
<p>F-Droid builds every app from source. Its tool, fdroidserver, carries a list of non-free library signatures and a list of allowed Maven repositories: Maven Central, Google Maven, Sonatype, JFrog, JitPack and Clojars. F-Droid Inclusion Gate turns those two lists and the policy text into 25 rules that run on the Gradle file you have open in VS Code, or on text you paste into the free web page.</p>
<p>Six lines and their replacements</p>
<ol><li>com.google.firebase:firebase-messaging becomes org.unifiedpush.android:connector.</li><li>com.google.firebase:firebase-crashlytics becomes ch.acra:acra-mail. ACRA is Apache-2.0. Keep it opt-in and off by default, or F-Droid adds the Tracking anti-feature.</li><li>com.google.android.gms:play-services-location becomes android.location.LocationManager, which is part of the platform and needs no dependency.</li><li>com.google.android.gms:play-services-maps becomes org.osmdroid:osmdroid-android (Apache-2.0) or org.maplibre.gl:android-sdk (BSD-2).</li><li>com.google.mlkit:barcode-scanning becomes com.journeyapps:zxing-android-embedded (Apache-2.0).</li><li>com.android.billingclient:billing-ktx moves to playImplementation inside a play flavour. The foss flavour links a donation page instead.</li></ol>
<p>The other four items in the sample were the Firebase BOM, Firebase Analytics, the Crashlytics Gradle plugin and Play in-app review. There were also four warnings: the google-services plugin, jcenter(), a Maven repository outside the allowed list, and no foss flavour at all.</p>
<p>The flavour split</p>
<p>You do not have to delete Firebase from the Play build. Declare two flavours in one dimension, foss and play, and change implementation to playImplementation for every non-free line. The tool then reports those lines as info, isolated in the play flavour. The migrated sample gives 0 errors and 2 info lines. F-Droid&#x27;s build recipe then points at the foss flavour.</p>
<p>What it does not do</p>
<p>It reads Gradle text. It does not open an APK and cannot see a non-free library pulled in transitively by a free-looking one. F-Droid reviewers still read the source and decide. The check removes the obvious build failures before that review.</p>
<p>Free and paid</p>
<p>The open file is free, with every item, line and replacement, and no key. A licence key covers the whole workspace: a dated migration plan across every module, the flavour split per item, and a CI gate that fails a build that adds new non-free use.</p></div><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:30px 0 10px;color:#1b1f24">15 seconds — what it actually does</h2><video controls muted playsinline preload="none" poster="/img/promo/sku425922_result_card.jpg" style="width:100%;max-width:360px;border-radius:10px;display:block;margin:0 auto"><source src="/media/sku425922_short.mp4" type="video/mp4"></video><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:34px 0 6px;color:#1b1f24">Questions people ask</h2><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does F-Droid Inclusion Gate do?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">It reads an Android build.gradle, build.gradle.kts or libs.versions.toml and lists every dependency, plugin and Maven repository that the F-Droid Inclusion Policy treats as non-free, with the line number and the FOSS replacement line: UnifiedPush for Firebase Cloud Messaging, ACRA for Crashlytics, osmdroid or MapLibre for Google Maps, ZXing for ML Kit barcode scanning. It has 25 rules.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Who is F-Droid Inclusion Gate for?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Android developers and small app teams who ship on Google Play and want the same app on F-Droid, maintainers answering an F-Droid reviewer about Play Services or Firebase, and open-source projects adding a foss product flavour next to their play flavour, for example while they update targetSdk to API 36 for Google Play&#x27;s August 31 2026 requirement.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">Why not just run fdroidserver or ask a chatbot?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">fdroidserver&#x27;s scanner is free software, but it expects F-Droid&#x27;s build metadata and a checked-out source tree, so it is not an editor check. A chatbot does not carry fdroidserver&#x27;s signature list or its allowed Maven repositories, and may suggest microG, which replaces Play Services on the phone but leaves the proprietary client library in your build. This extension flags each line in the editor, before you submit.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What is free and what needs a licence key?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">Free, with no key: the open Gradle file is checked in full, every non-free item is named with file, line and replacement, and the web version at getreadystack.com runs the same engine. The $29 licence key adds the whole workspace: a dated migration plan across every module, the foss/play flavour split for each item, and a CI gate that fails a build adding new non-free use.</p></details><details style="border-bottom:1px solid #e6e9ed;padding:10px 0"><summary style="cursor:pointer;font:600 15px/1.4 system-ui,sans-serif;color:#1b1f24">What does the alternative cost?</summary><p style="margin:8px 0 2px;font:400 14.5px/1.6 system-ui,sans-serif;color:#33404b">The F-Droid route itself costs nothing: F-Droid builds and signs the app, and its scanner is free software. The real cost is time. A build that fails on a leftover Firebase or Play Services line goes back to the developer, so fixing it after submission means another wait on review. The $29 key is paid once, not a subscription, and the free check needs no key.</p></details><script type="application/ld+json" data-yj="yj-faq-ld">{"@context": "https://schema.org", "@type": "FAQPage", "mainEntity": [{"@type": "Question", "name": "What does F-Droid Inclusion Gate do?", "acceptedAnswer": {"@type": "Answer", "text": "It reads an Android build.gradle, build.gradle.kts or libs.versions.toml and lists every dependency, plugin and Maven repository that the F-Droid Inclusion Policy treats as non-free, with the line number and the FOSS replacement line: UnifiedPush for Firebase Cloud Messaging, ACRA for Crashlytics, osmdroid or MapLibre for Google Maps, ZXing for ML Kit barcode scanning. It has 25 rules."}}, {"@type": "Question", "name": "Who is F-Droid Inclusion Gate for?", "acceptedAnswer": {"@type": "Answer", "text": "Android developers and small app teams who ship on Google Play and want the same app on F-Droid, maintainers answering an F-Droid reviewer about Play Services or Firebase, and open-source projects adding a foss product flavour next to their play flavour, for example while they update targetSdk to API 36 for Google Play's August 31 2026 requirement."}}, {"@type": "Question", "name": "Why not just run fdroidserver or ask a chatbot?", "acceptedAnswer": {"@type": "Answer", "text": "fdroidserver's scanner is free software, but it expects F-Droid's build metadata and a checked-out source tree, so it is not an editor check. A chatbot does not carry fdroidserver's signature list or its allowed Maven repositories, and may suggest microG, which replaces Play Services on the phone but leaves the proprietary client library in your build. This extension flags each line in the editor, before you submit."}}, {"@type": "Question", "name": "What is free and what needs a licence key?", "acceptedAnswer": {"@type": "Answer", "text": "Free, with no key: the open Gradle file is checked in full, every non-free item is named with file, line and replacement, and the web version at getreadystack.com runs the same engine. The $29 licence key adds the whole workspace: a dated migration plan across every module, the foss/play flavour split for each item, and a CI gate that fails a build adding new non-free use."}}, {"@type": "Question", "name": "What does the alternative cost?", "acceptedAnswer": {"@type": "Answer", "text": "The F-Droid route itself costs nothing: F-Droid builds and signs the app, and its scanner is free software. The real cost is time. A build that fails on a leftover Firebase or Play Services line goes back to the developer, so fixing it after submission means another wait on review. The $29 key is paid once, not a subscription, and the free check needs no key."}}]}</script><div id="yj-ask" style="margin:36px 0 0;padding:18px 18px 16px;border:1px solid #e2e6ea;border-radius:10px;background:#fbfbfc"><h2 style="font:600 19px/1.3 system-ui,sans-serif;margin:0 0 4px;color:#1b1f24">Ask about this tool</h2><p style="font:400 13.5px/1.5 system-ui,sans-serif;margin:0 0 12px;color:#5b6570">One question, answered by the person who built it. Your email only if you want the answer sent.</p><form onsubmit="return yjAsk(event)" style="display:grid;gap:8px"><textarea name="question" required minlength="10" maxlength="2000" rows="3" placeholder="Your question" style="font:14px/1.5 system-ui,sans-serif;padding:10px;border:1px solid #cfd6dd;border-radius:6px"></textarea><div style="display:flex;gap:8px;flex-wrap:wrap"><input type="email" name="email" placeholder="Email (optional)" style="flex:1;min-width:200px;font:14px system-ui,sans-serif;padding:9px 10px;border:1px solid #cfd6dd;border-radius:6px"><button type="submit" style="font:600 14px system-ui,sans-serif;padding:9px 18px;border:0;border-radius:6px;background:#1b1f24;color:#fff;cursor:pointer">Send</button></div><p id="yj-ask-msg" style="font:13px system-ui,sans-serif;margin:2px 0 0;color:#0a6238;min-height:1em"></p></form><script>function yjAsk(e){e.preventDefault();var f=e.target,m=document.getElementById("yj-ask-msg");m.style.color="#0a6238";fetch(location.origin+"/api/ask",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sku:425922,slug:"fdroid-inclusion-gate",lang:"en",question:f.question.value,email:f.email.value,page:location.pathname})}).then(function(r){if(!r.ok)throw 0;m.textContent="Thanks \u2014 noted. Answers land on this page and, if you left an email, in your inbox.";f.question.value="";}).catch(function(){m.style.color="#a8551f";m.textContent="Could not send. Please try again in a minute.";});return false;}</script></div></section><section class="yj-related" style="max-width:920px;margin:56px auto 40px;padding:0 20px"><h2 style="font:600 21px/1.3 system-ui,sans-serif;margin:0 0 6px;color:#1b1f24">Same job, next tool</h2><p style="font:400 14.5px/1.5 system-ui,sans-serif;margin:0 0 20px;color:#5b6570">Free in the browser. The full version is a one-time licence.</p><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:14px"><a class="yj-rel-c" href="https://getreadystack.com/tools/play-release-blocker-lint"><span class="yj-rel-t">Google Play Release Blocker Lint - target API 36, Billing 8, 16 KB</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/crashlytics-migration-lint"><span class="yj-rel-t">Crashlytics Migration Lint for App Center</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/mobile-app-ui-kit-account-deletion"><span class="yj-rel-t">Mobile App UI Kit — Account Deletion Flow for App Store 5.1.1(v) &amp; Google Play</span><span class="yj-rel-p">$168</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/robots-txt-ai-crawler-audit"><span class="yj-rel-t">AI Crawler Rules - robots.txt Audit for AI Search</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/brave-search-api-migration-check"><span class="yj-rel-t">Brave Search API Migration Check — Google Custom Search &amp; Bing</span><span class="yj-rel-p">$29</span></a><a class="yj-rel-c" href="https://getreadystack.com/tools/cinc-migration-check"><span class="yj-rel-t">Cinc Migration Check: Chef Infra licence gate lint</span><span class="yj-rel-p">$29</span></a></div><style>.yj-rel-c{display:flex;flex-direction:column;gap:8px;padding:15px 16px;border:1px solid #e2e6ea;border-radius:6px;text-decoration:none;background:#fff}.yj-rel-c:hover{border-color:#b9c2cb;background:#fafbfc}.yj-rel-t{font:500 14.5px/1.42 system-ui,sans-serif;color:#1b1f24}.yj-rel-p{font:600 13px system-ui,sans-serif;color:#0a6238}</style></section></body>
</html>