| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> |
| <title>Android APK/AAB Builder SaaS</title> |
| <style> |
| *,*::before,*::after{margin:0;padding:0;box-sizing:border-box} |
| :root{ |
| --bg:#0a0a1a; |
| --surface:#111128; |
| --surface2:#1a1a3e; |
| --surface3:#222255; |
| --border:#2a2a5a; |
| --accent:#e94560; |
| --accent2:#ff6b81; |
| --text:#e0e0f0; |
| --text2:#a0a0c0; |
| --success:#00e676; |
| --warning:#ffc107; |
| --error:#ff5252; |
| --radius:12px; |
| --font:'Segoe UI',system-ui,-apple-system,sans-serif; |
| } |
| html{scroll-behavior:smooth} |
| body{ |
| font-family:var(--font); |
| background:var(--bg); |
| color:var(--text); |
| min-height:100vh; |
| line-height:1.6; |
| overflow-x:hidden; |
| } |
| .container{ |
| max-width:800px; |
| margin:0 auto; |
| padding:16px; |
| } |
| header{ |
| text-align:center; |
| padding:30px 16px 20px; |
| background:linear-gradient(135deg,#0f0f2e 0%,#1a1a4e 50%,#0f0f2e 100%); |
| border-bottom:2px solid var(--accent); |
| margin-bottom:20px; |
| } |
| header h1{ |
| font-size:1.6em; |
| background:linear-gradient(135deg,var(--accent),var(--accent2),#a855f7); |
| -webkit-background-clip:text; |
| -webkit-text-fill-color:transparent; |
| background-clip:text; |
| margin-bottom:6px; |
| font-weight:800; |
| letter-spacing:-0.5px; |
| } |
| header p{ |
| color:var(--text2); |
| font-size:0.9em; |
| } |
| .badge{ |
| display:inline-block; |
| background:var(--accent); |
| color:#fff; |
| padding:2px 10px; |
| border-radius:20px; |
| font-size:0.7em; |
| margin-top:8px; |
| font-weight:600; |
| } |
| .accordion{ |
| margin-bottom:12px; |
| border-radius:var(--radius); |
| overflow:hidden; |
| border:1px solid var(--border); |
| background:var(--surface); |
| transition:all 0.3s ease; |
| } |
| .accordion:hover{ |
| border-color:var(--accent); |
| } |
| .accordion-header{ |
| display:flex; |
| align-items:center; |
| justify-content:space-between; |
| padding:14px 18px; |
| cursor:pointer; |
| user-select:none; |
| background:var(--surface); |
| transition:background 0.3s; |
| } |
| .accordion-header:hover{ |
| background:var(--surface2); |
| } |
| .accordion-header .section-icon{ |
| font-size:1.3em; |
| margin-right:12px; |
| flex-shrink:0; |
| } |
| .accordion-header .section-title{ |
| flex:1; |
| font-weight:600; |
| font-size:0.95em; |
| } |
| .accordion-header .section-count{ |
| background:var(--surface3); |
| color:var(--text2); |
| padding:2px 8px; |
| border-radius:10px; |
| font-size:0.75em; |
| margin-right:10px; |
| } |
| .accordion-arrow{ |
| font-size:0.8em; |
| transition:transform 0.3s; |
| color:var(--text2); |
| } |
| .accordion.open .accordion-arrow{ |
| transform:rotate(180deg); |
| } |
| .accordion-body{ |
| max-height:0; |
| overflow:hidden; |
| transition:max-height 0.4s ease; |
| background:var(--surface2); |
| } |
| .accordion.open .accordion-body{ |
| max-height:5000px; |
| } |
| .accordion-content{ |
| padding:16px 18px; |
| } |
| .form-group{ |
| margin-bottom:16px; |
| } |
| .form-group label{ |
| display:block; |
| font-size:0.85em; |
| color:var(--text2); |
| margin-bottom:6px; |
| font-weight:500; |
| } |
| .form-group label .required{ |
| color:var(--accent); |
| } |
| input[type="text"], |
| input[type="number"], |
| input[type="url"], |
| input[type="email"], |
| input[type="color"], |
| select, |
| textarea{ |
| width:100%; |
| padding:10px 14px; |
| background:var(--bg); |
| border:1px solid var(--border); |
| border-radius:8px; |
| color:var(--text); |
| font-size:0.9em; |
| font-family:var(--font); |
| outline:none; |
| transition:border-color 0.3s,box-shadow 0.3s; |
| } |
| input:focus,select:focus,textarea:focus{ |
| border-color:var(--accent); |
| box-shadow:0 0 0 3px rgba(233,69,96,0.15); |
| } |
| textarea{ |
| resize:vertical; |
| min-height:80px; |
| font-family:'Courier New',monospace; |
| font-size:0.8em; |
| } |
| input[type="color"]{ |
| padding:4px; |
| height:42px; |
| cursor:pointer; |
| } |
| select{ |
| cursor:pointer; |
| appearance:none; |
| background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%23a0a0c0' d='M6 8L1 3h10z'/%3E%3C/svg%3E"); |
| background-repeat:no-repeat; |
| background-position:right 12px center; |
| padding-right:32px; |
| } |
| .toggle-row{ |
| display:flex; |
| align-items:center; |
| justify-content:space-between; |
| padding:10px 0; |
| border-bottom:1px solid rgba(255,255,255,0.05); |
| } |
| .toggle-row:last-child{ |
| border-bottom:none; |
| } |
| .toggle-label{ |
| font-size:0.88em; |
| color:var(--text); |
| flex:1; |
| padding-right:10px; |
| } |
| .toggle-label small{ |
| display:block; |
| color:var(--text2); |
| font-size:0.82em; |
| margin-top:2px; |
| } |
| .switch{ |
| position:relative; |
| width:48px; |
| height:26px; |
| flex-shrink:0; |
| } |
| .switch input{ |
| opacity:0; |
| width:0; |
| height:0; |
| } |
| .slider{ |
| position:absolute; |
| cursor:pointer; |
| top:0;left:0;right:0;bottom:0; |
| background:var(--surface3); |
| border-radius:26px; |
| transition:0.3s; |
| } |
| .slider:before{ |
| content:""; |
| position:absolute; |
| height:20px; |
| width:20px; |
| left:3px; |
| bottom:3px; |
| background:#fff; |
| border-radius:50%; |
| transition:0.3s; |
| } |
| .switch input:checked + .slider{ |
| background:var(--accent); |
| } |
| .switch input:checked + .slider:before{ |
| transform:translateX(22px); |
| } |
| .file-upload-zone{ |
| border:2px dashed var(--border); |
| border-radius:var(--radius); |
| padding:20px; |
| text-align:center; |
| cursor:pointer; |
| transition:all 0.3s; |
| background:var(--bg); |
| } |
| .file-upload-zone:hover{ |
| border-color:var(--accent); |
| background:rgba(233,69,96,0.05); |
| } |
| .file-upload-zone p{ |
| color:var(--text2); |
| font-size:0.85em; |
| margin-top:6px; |
| } |
| .file-upload-zone .upload-icon{ |
| font-size:2em; |
| margin-bottom:4px; |
| } |
| .file-upload-zone img{ |
| max-width:80px; |
| max-height:80px; |
| border-radius:8px; |
| margin-top:8px; |
| } |
| .grid-2{ |
| display:grid; |
| grid-template-columns:1fr 1fr; |
| gap:12px; |
| } |
| @media(max-width:500px){ |
| .grid-2{grid-template-columns:1fr} |
| } |
| .build-btn{ |
| display:block; |
| width:100%; |
| padding:16px; |
| background:linear-gradient(135deg,var(--accent),#a855f7); |
| color:#fff; |
| border:none; |
| border-radius:var(--radius); |
| font-size:1.1em; |
| font-weight:700; |
| cursor:pointer; |
| transition:all 0.3s; |
| letter-spacing:0.5px; |
| margin-top:20px; |
| position:relative; |
| overflow:hidden; |
| } |
| .build-btn:hover{ |
| transform:translateY(-2px); |
| box-shadow:0 8px 30px rgba(233,69,96,0.3); |
| } |
| .build-btn:active{ |
| transform:translateY(0); |
| } |
| .build-btn:disabled{ |
| opacity:0.6; |
| cursor:not-allowed; |
| transform:none; |
| } |
| .build-btn .spinner{ |
| display:none; |
| width:20px; |
| height:20px; |
| border:3px solid rgba(255,255,255,0.3); |
| border-top-color:#fff; |
| border-radius:50%; |
| animation:spin 0.8s linear infinite; |
| margin-right:10px; |
| vertical-align:middle; |
| } |
| .build-btn.loading .spinner{ |
| display:inline-block; |
| } |
| @keyframes spin{to{transform:rotate(360deg)}} |
| #log-panel{ |
| display:none; |
| margin-top:20px; |
| border-radius:var(--radius); |
| overflow:hidden; |
| border:1px solid var(--border); |
| background:var(--surface); |
| } |
| #log-panel.active{ |
| display:block; |
| } |
| .log-header{ |
| display:flex; |
| align-items:center; |
| justify-content:space-between; |
| padding:12px 18px; |
| background:var(--surface2); |
| border-bottom:1px solid var(--border); |
| } |
| .log-header h3{ |
| font-size:0.95em; |
| font-weight:600; |
| } |
| .log-status{ |
| padding:3px 12px; |
| border-radius:20px; |
| font-size:0.75em; |
| font-weight:600; |
| } |
| .log-status.running{background:var(--warning);color:#000} |
| .log-status.success{background:var(--success);color:#000} |
| .log-status.failed{background:var(--error);color:#fff} |
| #log-output{ |
| padding:14px; |
| max-height:500px; |
| overflow-y:auto; |
| font-family:'Courier New',monospace; |
| font-size:0.78em; |
| line-height:1.7; |
| background:#050510; |
| color:#88ff88; |
| white-space:pre-wrap; |
| word-break:break-all; |
| } |
| #log-output .error-line{color:var(--error)} |
| #log-output .success-line{color:var(--success)} |
| #log-output .ai-line{color:#ffd700;font-weight:bold} |
| #download-section{ |
| display:none; |
| margin-top:16px; |
| text-align:center; |
| } |
| #download-section.active{ |
| display:block; |
| } |
| .download-btn{ |
| display:inline-block; |
| padding:14px 40px; |
| background:linear-gradient(135deg,var(--success),#00c853); |
| color:#000; |
| border:none; |
| border-radius:var(--radius); |
| font-size:1em; |
| font-weight:700; |
| cursor:pointer; |
| text-decoration:none; |
| transition:all 0.3s; |
| } |
| .download-btn:hover{ |
| transform:translateY(-2px); |
| box-shadow:0 8px 30px rgba(0,230,118,0.3); |
| } |
| footer{ |
| text-align:center; |
| padding:30px 16px; |
| color:var(--text2); |
| font-size:0.8em; |
| border-top:1px solid var(--border); |
| margin-top:30px; |
| } |
| ::-webkit-scrollbar{width:6px} |
| ::-webkit-scrollbar-track{background:var(--bg)} |
| ::-webkit-scrollbar-thumb{background:var(--surface3);border-radius:3px} |
| ::-webkit-scrollbar-thumb:hover{background:var(--accent)} |
| .perm-grid{ |
| display:grid; |
| grid-template-columns:1fr; |
| gap:0; |
| } |
| </style> |
| </head> |
| <body> |
| <header> |
| <h1>Android APK/AAB Builder</h1> |
| <p>Build production-ready Android apps from any URL</p> |
| <span class="badge">45+ Features • Cloud Build</span> |
| </header> |
|
|
| <div class="container"> |
| <form id="buildForm" autocomplete="off"> |
|
|
| |
| <div class="accordion open" id="acc-core"> |
| <div class="accordion-header" onclick="toggleAccordion('acc-core')"> |
| <span class="section-icon">⚙</span> |
| <span class="section-title">Core Basics</span> |
| <span class="section-count">7 options</span> |
| <span class="accordion-arrow">▼</span> |
| </div> |
| <div class="accordion-body"> |
| <div class="accordion-content"> |
| <div class="form-group"> |
| <label>App Name <span class="required">*</span></label> |
| <input type="text" name="app_name" value="MyApp" required> |
| </div> |
| <div class="form-group"> |
| <label>Package Name <span class="required">*</span></label> |
| <input type="text" name="package_name" value="com.example.myapp" required pattern="^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$"> |
| </div> |
| <div class="form-group"> |
| <label>Target Web URL <span class="required">*</span></label> |
| <input type="url" name="target_url" value="https://www.google.com" required> |
| </div> |
| <div class="grid-2"> |
| <div class="form-group"> |
| <label>Build Format</label> |
| <select name="build_format"> |
| <option value="apk" selected>APK</option> |
| <option value="aab">AAB (App Bundle)</option> |
| </select> |
| </div> |
| <div class="form-group"> |
| <label>Screen Orientation</label> |
| <select name="screen_orientation"> |
| <option value="unspecified" selected>Auto</option> |
| <option value="portrait">Portrait Only</option> |
| <option value="landscape">Landscape Only</option> |
| </select> |
| </div> |
| </div> |
| <div class="grid-2"> |
| <div class="form-group"> |
| <label>Version Name</label> |
| <input type="text" name="version_name" value="1.0.0"> |
| </div> |
| <div class="form-group"> |
| <label>Version Code</label> |
| <input type="number" name="version_code" value="1" min="1"> |
| </div> |
| </div> |
| <div class="grid-2"> |
| <div class="form-group"> |
| <label>Developer Name</label> |
| <input type="text" name="dev_name" value="Developer"> |
| </div> |
| <div class="form-group"> |
| <label>Developer Email</label> |
| <input type="email" name="dev_email" value="dev@example.com"> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="accordion" id="acc-branding"> |
| <div class="accordion-header" onclick="toggleAccordion('acc-branding')"> |
| <span class="section-icon">🎨</span> |
| <span class="section-title">Branding & UI</span> |
| <span class="section-count">7 options</span> |
| <span class="accordion-arrow">▼</span> |
| </div> |
| <div class="accordion-body"> |
| <div class="accordion-content"> |
| <div class="form-group"> |
| <label>App Icon (PNG, 512x512 recommended)</label> |
| <div class="file-upload-zone" id="iconUploadZone" onclick="document.getElementById('iconFileInput').click()"> |
| <div class="upload-icon">📷</div> |
| <p>Click to upload icon</p> |
| <img id="iconPreview" style="display:none" alt="icon preview"> |
| </div> |
| <input type="file" id="iconFileInput" accept="image/png,image/jpeg,image/webp" style="display:none" onchange="handleIconUpload(event)"> |
| <input type="hidden" name="app_icon_b64" id="app_icon_b64"> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Splash Screen |
| <small>Show a splash screen before loading the WebView</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="splash_enabled" id="splashToggle" onchange="toggleSplashOptions()"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div id="splashOptions" style="display:none"> |
| <div class="form-group"> |
| <label>Splash Media (Image or GIF)</label> |
| <div class="file-upload-zone" id="splashUploadZone" onclick="document.getElementById('splashFileInput').click()"> |
| <div class="upload-icon">🌌</div> |
| <p>Click to upload splash image/GIF</p> |
| <img id="splashPreview" style="display:none" alt="splash preview"> |
| </div> |
| <input type="file" id="splashFileInput" accept="image/png,image/jpeg,image/gif,image/webp" style="display:none" onchange="handleSplashUpload(event)"> |
| <input type="hidden" name="splash_media_b64" id="splash_media_b64"> |
| </div> |
| <div class="form-group"> |
| <label>Splash Text</label> |
| <input type="text" name="splash_text" value="Loading..."> |
| </div> |
| <div class="grid-2"> |
| <div class="form-group"> |
| <label>Splash Text Color</label> |
| <input type="color" name="splash_text_color" value="#FFFFFF"> |
| </div> |
| <div class="form-group"> |
| <label>Splash Background Color</label> |
| <input type="color" name="splash_bg_color" value="#1a1a2e"> |
| </div> |
| </div> |
| </div> |
| <div class="grid-2"> |
| <div class="form-group"> |
| <label>Status Bar Color</label> |
| <input type="color" name="status_bar_color" value="#1a1a2e"> |
| </div> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Bottom Navigation Bar |
| <small>Home, Refresh, and Back buttons</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="bottom_nav_enabled"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="accordion" id="acc-webengine"> |
| <div class="accordion-header" onclick="toggleAccordion('acc-webengine')"> |
| <span class="section-icon">⚡</span> |
| <span class="section-title">Advanced Web Engine & UX</span> |
| <span class="section-count">9 options</span> |
| <span class="accordion-arrow">▼</span> |
| </div> |
| <div class="accordion-body"> |
| <div class="accordion-content"> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Enable JavaScript |
| <small>Required for most modern websites</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="enable_javascript" checked> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Enable DOM Storage |
| <small>Local Storage for sessions and data persistence</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="enable_dom_storage" checked> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Allow File Access |
| <small>Upload/Download files from WebView</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="allow_file_access" checked> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Media Auto-play |
| <small>Auto-play audio/video without user gesture</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="media_autoplay"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Enable Zoom Controls |
| <small>Pinch-to-zoom and built-in zoom buttons</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="enable_zoom"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="form-group" style="margin-top:12px"> |
| <label>Custom User Agent <small>(leave empty for default)</small></label> |
| <input type="text" name="custom_user_agent" placeholder="Mozilla/5.0 ..."> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Pull-to-Refresh |
| <small>SwipeRefreshLayout for page reload</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="pull_to_refresh" checked> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Multiple Windows Support |
| <small>JavaScript can open windows automatically</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="multi_windows"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Deep Link & Intent Interception |
| <small>Open OAuth URLs (Google, Facebook, WhatsApp) externally</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="deep_link_intercept" checked> |
| <span class="slider"></span> |
| </label> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="accordion" id="acc-security"> |
| <div class="accordion-header" onclick="toggleAccordion('acc-security')"> |
| <span class="section-icon">🔒</span> |
| <span class="section-title">Security & Protection</span> |
| <span class="section-count">9 options</span> |
| <span class="accordion-arrow">▼</span> |
| </div> |
| <div class="accordion-body"> |
| <div class="accordion-content"> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| ProGuard / R8 Obfuscation |
| <small>Minify and obfuscate release code</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="proguard_enabled"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Root & Emulator Detection |
| <small>Auto-exit if device is rooted or emulated</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="root_detection"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Prevent Screenshots |
| <small>FLAG_SECURE blocks screen capture</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="prevent_screenshots"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Clear Cache on Exit |
| <small>Wipe WebView cache when app closes</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="clear_cache_exit"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Clear Cookies on Exit |
| <small>Wipe all cookies & sessions on close</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="clear_cookies_exit"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Keep Screen On |
| <small>Prevent screen from sleeping</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="keep_screen_on"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Fullscreen Immersive Mode |
| <small>Hide nav/status bars, fix notch cutout</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="fullscreen_mode"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Enforce HTTPS Only |
| <small>Block mixed content (HTTP resources)</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="enforce_https" checked> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Tamper Protection |
| <small>Basic APK signature verification check</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="tamper_protection"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="accordion" id="acc-monetize"> |
| <div class="accordion-header" onclick="toggleAccordion('acc-monetize')"> |
| <span class="section-icon">💰</span> |
| <span class="section-title">Monetization & Services</span> |
| <span class="section-count">5 options</span> |
| <span class="accordion-arrow">▼</span> |
| </div> |
| <div class="accordion-body"> |
| <div class="accordion-content"> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Firebase Integration |
| <small>Analytics & Cloud Messaging</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="firebase_enabled" id="firebaseToggle" onchange="toggleFirebaseOptions()"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div id="firebaseOptions" style="display:none"> |
| <div class="form-group"> |
| <label>google-services.json content <small>(paste JSON)</small></label> |
| <textarea name="firebase_json" placeholder='{"project_info":{"project_number":"..."},...}'></textarea> |
| </div> |
| </div> |
| <div class="form-group" style="margin-top:12px"> |
| <label>OneSignal App ID <small>(for Push Notifications)</small></label> |
| <input type="text" name="onesignal_app_id" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| AdMob Ads |
| <small>Enable Google AdMob monetization</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="admob_enabled" id="admobToggle" onchange="toggleAdmobOptions()"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div id="admobOptions" style="display:none"> |
| <div class="form-group"> |
| <label>AdMob Banner ID</label> |
| <input type="text" name="admob_banner_id" placeholder="ca-app-pub-xxxxx/xxxxx"> |
| </div> |
| <div class="form-group"> |
| <label>AdMob Interstitial ID</label> |
| <input type="text" name="admob_interstitial_id" placeholder="ca-app-pub-xxxxx/xxxxx"> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="accordion" id="acc-perms"> |
| <div class="accordion-header" onclick="toggleAccordion('acc-perms')"> |
| <span class="section-icon">🔐</span> |
| <span class="section-title">Dynamic Permissions</span> |
| <span class="section-count">8 options</span> |
| <span class="accordion-arrow">▼</span> |
| </div> |
| <div class="accordion-body"> |
| <div class="accordion-content"> |
| <div class="perm-grid"> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| INTERNET |
| <small>Network access (always enabled)</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" checked disabled> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| CAMERA |
| <small>Camera access for photo/video capture</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="perm_camera"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| RECORD_AUDIO (Microphone) |
| <small>Microphone access for audio recording</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="perm_microphone"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| LOCATION (Fine & Coarse) |
| <small>GPS and network location access</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="perm_location"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| STORAGE (Read/Write) |
| <small>External storage file access</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="perm_storage"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| VIBRATE |
| <small>Device vibration feedback</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="perm_vibrate"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| READ_CONTACTS |
| <small>Access device contacts</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="perm_contacts"> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| POST_NOTIFICATIONS (Android 13+) |
| <small>Auto-prompt for notification permission</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="perm_notifications" checked> |
| <span class="slider"></span> |
| </label> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="accordion" id="acc-resilience"> |
| <div class="accordion-header" onclick="toggleAccordion('acc-resilience')"> |
| <span class="section-icon">🤖</span> |
| <span class="section-title">Resilience & AI</span> |
| <span class="section-count">2 options</span> |
| <span class="accordion-arrow">▼</span> |
| </div> |
| <div class="accordion-body"> |
| <div class="accordion-content"> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| Offline Mode |
| <small>Show local offline.html when no internet detected</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" name="offline_mode" checked> |
| <span class="slider"></span> |
| </label> |
| </div> |
| <div class="toggle-row"> |
| <div class="toggle-label"> |
| AI Log Analyzer |
| <small>Automatic Arabic diagnosis of build errors</small> |
| </div> |
| <label class="switch"> |
| <input type="checkbox" checked disabled> |
| <span class="slider"></span> |
| </label> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <button type="submit" class="build-btn" id="buildBtn"> |
| <span class="spinner" id="buildSpinner"></span> |
| <span id="buildBtnText">Build Android App</span> |
| </button> |
| </form> |
|
|
| <div id="log-panel"> |
| <div class="log-header"> |
| <h3>Build Log</h3> |
| <span class="log-status" id="logStatus">RUNNING</span> |
| </div> |
| <div id="log-output"></div> |
| </div> |
|
|
| <div id="download-section"> |
| <a class="download-btn" id="downloadBtn" href="#">Download Build Artifact</a> |
| </div> |
|
|
| </div> |
|
|
| <footer> |
| <p>Android APK/AAB Builder SaaS • Powered by Gradle & Flask</p> |
| </footer> |
|
|
| <script> |
| function toggleAccordion(id){ |
| var el=document.getElementById(id); |
| if(el.classList.contains('open')){ |
| el.classList.remove('open'); |
| }else{ |
| el.classList.add('open'); |
| } |
| } |
| |
| function toggleSplashOptions(){ |
| var cb=document.getElementById('splashToggle'); |
| var opts=document.getElementById('splashOptions'); |
| if(cb.checked){ |
| opts.style.display='block'; |
| }else{ |
| opts.style.display='none'; |
| } |
| } |
| |
| function toggleFirebaseOptions(){ |
| var cb=document.getElementById('firebaseToggle'); |
| var opts=document.getElementById('firebaseOptions'); |
| if(cb.checked){ |
| opts.style.display='block'; |
| }else{ |
| opts.style.display='none'; |
| } |
| } |
| |
| function toggleAdmobOptions(){ |
| var cb=document.getElementById('admobToggle'); |
| var opts=document.getElementById('admobOptions'); |
| if(cb.checked){ |
| opts.style.display='block'; |
| }else{ |
| opts.style.display='none'; |
| } |
| } |
| |
| function handleIconUpload(event){ |
| var file=event.target.files[0]; |
| if(!file)return; |
| var reader=new FileReader(); |
| reader.onload=function(e){ |
| var b64=e.target.result; |
| document.getElementById('app_icon_b64').value=b64; |
| var preview=document.getElementById('iconPreview'); |
| preview.src=b64; |
| preview.style.display='block'; |
| document.querySelector('#iconUploadZone p').textContent=file.name; |
| }; |
| reader.readAsDataURL(file); |
| } |
| |
| function handleSplashUpload(event){ |
| var file=event.target.files[0]; |
| if(!file)return; |
| var reader=new FileReader(); |
| reader.onload=function(e){ |
| var b64=e.target.result; |
| document.getElementById('splash_media_b64').value=b64; |
| var preview=document.getElementById('splashPreview'); |
| preview.src=b64; |
| preview.style.display='block'; |
| document.querySelector('#splashUploadZone p').textContent=file.name; |
| }; |
| reader.readAsDataURL(file); |
| } |
| |
| var currentBuildId=null; |
| var eventSource=null; |
| |
| document.getElementById('buildForm').addEventListener('submit',function(e){ |
| e.preventDefault(); |
| |
| var btn=document.getElementById('buildBtn'); |
| var btnText=document.getElementById('buildBtnText'); |
| btn.disabled=true; |
| btn.classList.add('loading'); |
| btnText.textContent='Building...'; |
| |
| var logPanel=document.getElementById('log-panel'); |
| var logOutput=document.getElementById('log-output'); |
| var logStatusEl=document.getElementById('logStatus'); |
| var downloadSection=document.getElementById('download-section'); |
| |
| logPanel.classList.add('active'); |
| logOutput.innerHTML=''; |
| logStatusEl.textContent='RUNNING'; |
| logStatusEl.className='log-status running'; |
| downloadSection.classList.remove('active'); |
| |
| if(eventSource){ |
| eventSource.close(); |
| eventSource=null; |
| } |
| |
| var formData=new FormData(this); |
| |
| fetch('/build',{ |
| method:'POST', |
| body:formData |
| }) |
| .then(function(resp){return resp.json()}) |
| .then(function(data){ |
| if(data.status==='started'){ |
| currentBuildId=data.build_id; |
| startLogStream(data.build_id); |
| }else{ |
| btn.disabled=false; |
| btn.classList.remove('loading'); |
| btnText.textContent='Build Android App'; |
| logOutput.innerHTML='<span class="error-line">Error: '+((data.message)||'Unknown error')+'</span>\n'; |
| logStatusEl.textContent='FAILED'; |
| logStatusEl.className='log-status failed'; |
| } |
| }) |
| .catch(function(err){ |
| btn.disabled=false; |
| btn.classList.remove('loading'); |
| btnText.textContent='Build Android App'; |
| logOutput.innerHTML='<span class="error-line">Network Error: '+err.message+'</span>\n'; |
| logStatusEl.textContent='FAILED'; |
| logStatusEl.className='log-status failed'; |
| }); |
| }); |
| |
| function startLogStream(buildId){ |
| var logOutput=document.getElementById('log-output'); |
| var logStatusEl=document.getElementById('logStatus'); |
| var btn=document.getElementById('buildBtn'); |
| var btnText=document.getElementById('buildBtnText'); |
| var downloadSection=document.getElementById('download-section'); |
| var downloadBtn=document.getElementById('downloadBtn'); |
| |
| eventSource=new EventSource('/logs/'+buildId); |
| |
| eventSource.onmessage=function(event){ |
| try{ |
| var data=JSON.parse(event.data); |
| if(data.type==='log'){ |
| var msg=data.message; |
| var span=document.createElement('span'); |
| if(msg.indexOf('ERROR')!==-1||msg.indexOf('FAILED')!==-1||msg.indexOf('error')!==-1||msg.indexOf('Exception')!==-1){ |
| span.className='error-line'; |
| }else if(msg.indexOf('BUILD SUCCESSFUL')!==-1||msg.indexOf('success')!==-1){ |
| span.className='success-line'; |
| }else if(msg.indexOf('AI Log Analysis')!==-1||msg.indexOf('خطأ')!==-1||msg.indexOf('تم البناء')!==-1||msg.indexOf('لم يتم')!==-1||msg.indexOf('راجع')!==-1||msg.indexOf('تحقق')!==-1||msg.indexOf('فشل')!==-1){ |
| span.className='ai-line'; |
| } |
| span.textContent=msg+'\n'; |
| logOutput.appendChild(span); |
| logOutput.scrollTop=logOutput.scrollHeight; |
| }else if(data.type==='status'){ |
| eventSource.close(); |
| eventSource=null; |
| btn.disabled=false; |
| btn.classList.remove('loading'); |
| btnText.textContent='Build Android App'; |
| if(data.status==='success'){ |
| logStatusEl.textContent='SUCCESS'; |
| logStatusEl.className='log-status success'; |
| downloadSection.classList.add('active'); |
| downloadBtn.href='/download/'+buildId; |
| downloadBtn.download=''; |
| }else{ |
| logStatusEl.textContent='FAILED'; |
| logStatusEl.className='log-status failed'; |
| } |
| } |
| }catch(parseErr){ |
| var fallbackSpan=document.createElement('span'); |
| fallbackSpan.textContent=event.data+'\n'; |
| logOutput.appendChild(fallbackSpan); |
| logOutput.scrollTop=logOutput.scrollHeight; |
| } |
| }; |
| |
| eventSource.onerror=function(err){ |
| if(eventSource){ |
| eventSource.close(); |
| eventSource=null; |
| } |
| btn.disabled=false; |
| btn.classList.remove('loading'); |
| btnText.textContent='Build Android App'; |
| |
| var checkStatus=function(){ |
| fetch('/status/'+buildId) |
| .then(function(r){return r.json()}) |
| .then(function(d){ |
| if(d.status==='success'){ |
| logStatusEl.textContent='SUCCESS'; |
| logStatusEl.className='log-status success'; |
| downloadSection.classList.add('active'); |
| downloadBtn.href='/download/'+buildId; |
| }else if(d.status==='failed'){ |
| logStatusEl.textContent='FAILED'; |
| logStatusEl.className='log-status failed'; |
| }else{ |
| logStatusEl.textContent='UNKNOWN'; |
| logStatusEl.className='log-status failed'; |
| } |
| }) |
| .catch(function(){ |
| logStatusEl.textContent='CONNECTION LOST'; |
| logStatusEl.className='log-status failed'; |
| }); |
| }; |
| checkStatus(); |
| }; |
| } |
| </script> |
| </body> |
| </html> |
|
|