Viridite

Attach the .apk file directly (uploads straight to storage, no size problem — real APKs run 50-150MB and that's fine), or paste a direct download link below instead. Split/.xapk packages aren't supported yet.

e.g. "APKMirror", "APKPure", "official developer site"

Only used to credit you on the Credits page and in-app About screen, under Testers. Leave blank to stay anonymous — not verified, just for display.

Attach the file directly, or paste the full contents below — no length limit here either way.

Attach the file directly, or paste the full contents below — no length limit here either way.

Attach the file directly, or paste the full contents below — no length limit here either way.

(function () { // Wires each log's file input to read the picked file client-side and // drop its text into the matching textarea — same field the paste path // already fills, so submit() doesn't need to know which one was used. ["launcher_log", "compat_log", "core_log"].forEach(function (id) { var fileInput = document.getElementById(id + "_file"); var filenameEl = document.getElementById(id + "_filename"); var textarea = document.getElementById(id); fileInput.addEventListener("change", function () { var file = fileInput.files[0]; if (!file) return; var reader = new FileReader(); reader.onload = function () { textarea.value = reader.result; filenameEl.textContent = "Loaded " + file.name + " (" + file.size.toLocaleString() + " bytes)"; filenameEl.style.display = "block"; }; reader.onerror = function () { filenameEl.textContent = "Couldn't read " + file.name + " — paste the contents instead."; filenameEl.style.display = "block"; }; reader.readAsText(file); }); }); })(); (function () { var RELAY_URL = "https://android-horizon-compat-relay.zeraoralegendary.workers.dev"; var MAX_APK_UPLOAD_BYTES = 300 * 1024 * 1024; // apkUpload.state: "idle" | "uploading" | "done" | "error" var apkUpload = { state: "idle", objectKey: null }; var apkFileInput = document.getElementById("apk_file"); var apkFilenameEl = document.getElementById("apk_file_filename"); var apkUrlInput = document.getElementById("apk_url"); var submitBtn = document.querySelector("#report-form button[type=submit]"); function xhrPut(url, file, onProgress) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open("PUT", url); xhr.upload.addEventListener("progress", function (e) { if (e.lengthComputable) onProgress(e.loaded / e.total); }); xhr.onload = function () { if (xhr.status >= 200 && xhr.status < 300) resolve(); else reject(new Error("upload failed (HTTP " + xhr.status + ")")); }; xhr.onerror = function () { reject(new Error("upload failed (network error)")); }; xhr.send(file); }); } apkFileInput.addEventListener("change", function () { var file = apkFileInput.files[0]; apkUpload.state = "idle"; apkUpload.objectKey = null; if (!file) { apkFilenameEl.style.display = "none"; return; } if (file.size > MAX_APK_UPLOAD_BYTES) { apkFilenameEl.textContent = file.name + " is over " + (MAX_APK_UPLOAD_BYTES / (1024 * 1024)) + "MB — use a download link instead."; apkFilenameEl.style.display = "block"; apkFileInput.value = ""; return; } apkUpload.state = "uploading"; apkUrlInput.disabled = true; submitBtn.disabled = true; apkFilenameEl.style.display = "block"; apkFilenameEl.textContent = "Uploading " + file.name + "… 0%"; fetch(RELAY_URL + "/apk-upload-url", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ filename: file.name }), }) .then(function (r) { return r.json(); }) .then(function (result) { if (!result.ok) throw new Error(result.error || "couldn't get an upload URL"); return xhrPut(result.upload_url, file, function (fraction) { apkFilenameEl.textContent = "Uploading " + file.name + "… " + Math.round(fraction * 100) + "%"; }).then(function () { return result.object_key; }); }) .then(function (objectKey) { apkUpload.state = "done"; apkUpload.objectKey = objectKey; apkFilenameEl.textContent = "Uploaded " + file.name + " (" + file.size.toLocaleString() + " bytes) — the link field below is ignored while a file is attached."; submitBtn.disabled = false; }) .catch(function (e) { apkUpload.state = "error"; apkFilenameEl.textContent = "Couldn't upload " + file.name + " (" + e.message + ") — try again, or use a download link instead."; apkUrlInput.disabled = false; submitBtn.disabled = false; }); }); var form = document.getElementById("report-form"); var status = document.getElementById("status"); form.addEventListener("submit", function (ev) { ev.preventDefault(); if (apkUpload.state === "uploading") { status.className = "err"; status.textContent = "Still uploading the APK — wait for it to finish."; return; } if (apkUpload.state !== "done" && !apkUrlInput.value.trim()) { status.className = "err"; status.textContent = "Attach the APK file or paste a download link."; return; } var payload = { source_site: document.getElementById("source_site").value.trim(), github_username: document.getElementById("github_username").value.trim(), launcher_log: document.getElementById("launcher_log").value, compat_log: document.getElementById("compat_log").value, core_log: document.getElementById("core_log").value, notes: document.getElementById("notes").value, }; if (apkUpload.state === "done") { payload.apk_object_key = apkUpload.objectKey; } else { payload.apk_url = apkUrlInput.value.trim(); } submitBtn.disabled = true; status.className = "pending"; status.textContent = "Submitting…"; fetch(RELAY_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), }) .then(function (r) { return r.json().then(function (j) { return { ok: r.ok, body: j }; }); }) .then(function (result) { if (result.ok && result.body.ok) { status.className = "ok"; status.textContent = "Submitted! It'll be analyzed automatically — check the compatibility page in a few minutes."; form.reset(); apkUpload.state = "idle"; apkUpload.objectKey = null; apkUrlInput.disabled = false; apkFilenameEl.style.display = "none"; } else { status.className = "err"; status.textContent = "Couldn't submit: " + (result.body.error || "unknown error"); submitBtn.disabled = false; } }) .catch(function (e) { status.className = "err"; status.textContent = "Couldn't reach the submission service — try again shortly."; submitBtn.disabled = false; }); }); })();