apk / templates /index.html
Poorpoor6976's picture
Update templates/index.html
478834c verified
Raw
History Blame Contribute Delete
29.9 kB
<!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 &bull; Cloud Build</span>
</header>
<div class="container">
<form id="buildForm" autocomplete="off">
<!-- SECTION 1: Core Basics -->
<div class="accordion open" id="acc-core">
<div class="accordion-header" onclick="toggleAccordion('acc-core')">
<span class="section-icon">&#9881;</span>
<span class="section-title">Core Basics</span>
<span class="section-count">7 options</span>
<span class="accordion-arrow">&#9660;</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>
<!-- SECTION 2: Branding & UI -->
<div class="accordion" id="acc-branding">
<div class="accordion-header" onclick="toggleAccordion('acc-branding')">
<span class="section-icon">&#127912;</span>
<span class="section-title">Branding &amp; UI</span>
<span class="section-count">7 options</span>
<span class="accordion-arrow">&#9660;</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">&#128247;</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">&#127756;</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>
<!-- SECTION 3: Advanced Web Engine & UX -->
<div class="accordion" id="acc-webengine">
<div class="accordion-header" onclick="toggleAccordion('acc-webengine')">
<span class="section-icon">&#9889;</span>
<span class="section-title">Advanced Web Engine &amp; UX</span>
<span class="section-count">9 options</span>
<span class="accordion-arrow">&#9660;</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 &amp; 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>
<!-- SECTION 4: Security & Protection -->
<div class="accordion" id="acc-security">
<div class="accordion-header" onclick="toggleAccordion('acc-security')">
<span class="section-icon">&#128274;</span>
<span class="section-title">Security &amp; Protection</span>
<span class="section-count">9 options</span>
<span class="accordion-arrow">&#9660;</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 &amp; 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 &amp; 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>
<!-- SECTION 5: Monetization & Services -->
<div class="accordion" id="acc-monetize">
<div class="accordion-header" onclick="toggleAccordion('acc-monetize')">
<span class="section-icon">&#128176;</span>
<span class="section-title">Monetization &amp; Services</span>
<span class="section-count">5 options</span>
<span class="accordion-arrow">&#9660;</span>
</div>
<div class="accordion-body">
<div class="accordion-content">
<div class="toggle-row">
<div class="toggle-label">
Firebase Integration
<small>Analytics &amp; 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>
<!-- SECTION 6: Permissions -->
<div class="accordion" id="acc-perms">
<div class="accordion-header" onclick="toggleAccordion('acc-perms')">
<span class="section-icon">&#128272;</span>
<span class="section-title">Dynamic Permissions</span>
<span class="section-count">8 options</span>
<span class="accordion-arrow">&#9660;</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 &amp; 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>
<!-- SECTION 7: Resilience & AI -->
<div class="accordion" id="acc-resilience">
<div class="accordion-header" onclick="toggleAccordion('acc-resilience')">
<span class="section-icon">&#129302;</span>
<span class="section-title">Resilience &amp; AI</span>
<span class="section-count">2 options</span>
<span class="accordion-arrow">&#9660;</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 &bull; Powered by Gradle &amp; 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>