Poorpoor6976 commited on
Commit
89b2480
·
verified ·
1 Parent(s): f5258f1

Upload 3 files

Browse files
Files changed (3) hide show
  1. Dockerfile.txt +63 -0
  2. index.html +1189 -0
  3. requirements.txt +3 -0
Dockerfile.txt ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM ubuntu:22.04
2
+
3
+ ENV DEBIAN_FRONTEND=noninteractive
4
+ ENV ANDROID_SDK_ROOT=/opt/android-sdk
5
+ ENV ANDROID_HOME=/opt/android-sdk
6
+ ENV GRADLE_HOME=/opt/gradle/gradle-7.6
7
+ ENV PATH="${PATH}:${ANDROID_SDK_ROOT}/cmdline-tools/latest/bin:${ANDROID_SDK_ROOT}/platform-tools:${ANDROID_SDK_ROOT}/build-tools/34.0.0:${GRADLE_HOME}/bin"
8
+ ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
9
+
10
+ RUN apt-get update && apt-get install -y \
11
+ openjdk-17-jdk \
12
+ python3 \
13
+ python3-pip \
14
+ wget \
15
+ unzip \
16
+ curl \
17
+ git \
18
+ zip \
19
+ file \
20
+ && rm -rf /var/lib/apt/lists/*
21
+
22
+ RUN mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools && \
23
+ cd ${ANDROID_SDK_ROOT}/cmdline-tools && \
24
+ wget -q https://dl.google.com/android/repository/commandlinetools-linux-10406996_latest.zip -O cmdline-tools.zip && \
25
+ unzip -q cmdline-tools.zip && \
26
+ mv cmdline-tools latest && \
27
+ rm cmdline-tools.zip
28
+
29
+ RUN yes | sdkmanager --licenses > /dev/null 2>&1 || true
30
+ RUN sdkmanager "platforms;android-34" "build-tools;34.0.0" "build-tools;30.0.3" "platform-tools"
31
+
32
+ RUN mkdir -p /opt/gradle && \
33
+ cd /opt/gradle && \
34
+ wget -q https://services.gradle.org/distributions/gradle-7.6-bin.zip -O gradle.zip && \
35
+ unzip -q gradle.zip && \
36
+ rm gradle.zip
37
+
38
+ RUN mkdir -p /home/user/.gradle && \
39
+ echo "org.gradle.jvmargs=-Xmx3072m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8" > /home/user/.gradle/gradle.properties && \
40
+ echo "org.gradle.daemon=false" >> /home/user/.gradle/gradle.properties && \
41
+ echo "org.gradle.parallel=true" >> /home/user/.gradle/gradle.properties && \
42
+ echo "org.gradle.caching=true" >> /home/user/.gradle/gradle.properties && \
43
+ echo "android.useAndroidX=true" >> /home/user/.gradle/gradle.properties
44
+
45
+ RUN useradd -m -u 1000 user && \
46
+ chown -R user:user /opt/android-sdk && \
47
+ chown -R user:user /opt/gradle && \
48
+ chown -R user:user /home/user
49
+
50
+ WORKDIR /app
51
+
52
+ COPY requirements.txt .
53
+ RUN pip3 install --no-cache-dir -r requirements.txt
54
+
55
+ COPY . .
56
+
57
+ RUN chown -R user:user /app
58
+
59
+ USER user
60
+
61
+ EXPOSE 7860
62
+
63
+ CMD ["python3", "app.py"]
index.html ADDED
@@ -0,0 +1,1189 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
6
+ <title>Android APK/AAB Builder SaaS</title>
7
+ <style>
8
+ *,*::before,*::after{margin:0;padding:0;box-sizing:border-box}
9
+ :root{
10
+ --bg:#0a0a1a;
11
+ --surface:#111128;
12
+ --surface2:#1a1a3e;
13
+ --surface3:#222255;
14
+ --border:#2a2a5a;
15
+ --accent:#e94560;
16
+ --accent2:#ff6b81;
17
+ --text:#e0e0f0;
18
+ --text2:#a0a0c0;
19
+ --success:#00e676;
20
+ --warning:#ffc107;
21
+ --error:#ff5252;
22
+ --radius:12px;
23
+ --font:'Segoe UI',system-ui,-apple-system,sans-serif;
24
+ }
25
+ html{scroll-behavior:smooth}
26
+ body{
27
+ font-family:var(--font);
28
+ background:var(--bg);
29
+ color:var(--text);
30
+ min-height:100vh;
31
+ line-height:1.6;
32
+ overflow-x:hidden;
33
+ }
34
+ .container{
35
+ max-width:800px;
36
+ margin:0 auto;
37
+ padding:16px;
38
+ }
39
+ header{
40
+ text-align:center;
41
+ padding:30px 16px 20px;
42
+ background:linear-gradient(135deg,#0f0f2e 0%,#1a1a4e 50%,#0f0f2e 100%);
43
+ border-bottom:2px solid var(--accent);
44
+ margin-bottom:20px;
45
+ }
46
+ header h1{
47
+ font-size:1.6em;
48
+ background:linear-gradient(135deg,var(--accent),var(--accent2),#a855f7);
49
+ -webkit-background-clip:text;
50
+ -webkit-text-fill-color:transparent;
51
+ background-clip:text;
52
+ margin-bottom:6px;
53
+ font-weight:800;
54
+ letter-spacing:-0.5px;
55
+ }
56
+ header p{
57
+ color:var(--text2);
58
+ font-size:0.9em;
59
+ }
60
+ .badge{
61
+ display:inline-block;
62
+ background:var(--accent);
63
+ color:#fff;
64
+ padding:2px 10px;
65
+ border-radius:20px;
66
+ font-size:0.7em;
67
+ margin-top:8px;
68
+ font-weight:600;
69
+ }
70
+ .accordion{
71
+ margin-bottom:12px;
72
+ border-radius:var(--radius);
73
+ overflow:hidden;
74
+ border:1px solid var(--border);
75
+ background:var(--surface);
76
+ transition:all 0.3s ease;
77
+ }
78
+ .accordion:hover{
79
+ border-color:var(--accent);
80
+ }
81
+ .accordion-header{
82
+ display:flex;
83
+ align-items:center;
84
+ justify-content:space-between;
85
+ padding:14px 18px;
86
+ cursor:pointer;
87
+ user-select:none;
88
+ background:var(--surface);
89
+ transition:background 0.3s;
90
+ }
91
+ .accordion-header:hover{
92
+ background:var(--surface2);
93
+ }
94
+ .accordion-header .section-icon{
95
+ font-size:1.3em;
96
+ margin-right:12px;
97
+ flex-shrink:0;
98
+ }
99
+ .accordion-header .section-title{
100
+ flex:1;
101
+ font-weight:600;
102
+ font-size:0.95em;
103
+ }
104
+ .accordion-header .section-count{
105
+ background:var(--surface3);
106
+ color:var(--text2);
107
+ padding:2px 8px;
108
+ border-radius:10px;
109
+ font-size:0.75em;
110
+ margin-right:10px;
111
+ }
112
+ .accordion-arrow{
113
+ font-size:0.8em;
114
+ transition:transform 0.3s;
115
+ color:var(--text2);
116
+ }
117
+ .accordion.open .accordion-arrow{
118
+ transform:rotate(180deg);
119
+ }
120
+ .accordion-body{
121
+ max-height:0;
122
+ overflow:hidden;
123
+ transition:max-height 0.4s ease;
124
+ background:var(--surface2);
125
+ }
126
+ .accordion.open .accordion-body{
127
+ max-height:5000px;
128
+ }
129
+ .accordion-content{
130
+ padding:16px 18px;
131
+ }
132
+ .form-group{
133
+ margin-bottom:16px;
134
+ }
135
+ .form-group label{
136
+ display:block;
137
+ font-size:0.85em;
138
+ color:var(--text2);
139
+ margin-bottom:6px;
140
+ font-weight:500;
141
+ }
142
+ .form-group label .required{
143
+ color:var(--accent);
144
+ }
145
+ input[type="text"],
146
+ input[type="number"],
147
+ input[type="url"],
148
+ input[type="email"],
149
+ input[type="color"],
150
+ select,
151
+ textarea{
152
+ width:100%;
153
+ padding:10px 14px;
154
+ background:var(--bg);
155
+ border:1px solid var(--border);
156
+ border-radius:8px;
157
+ color:var(--text);
158
+ font-size:0.9em;
159
+ font-family:var(--font);
160
+ outline:none;
161
+ transition:border-color 0.3s,box-shadow 0.3s;
162
+ }
163
+ input:focus,select:focus,textarea:focus{
164
+ border-color:var(--accent);
165
+ box-shadow:0 0 0 3px rgba(233,69,96,0.15);
166
+ }
167
+ textarea{
168
+ resize:vertical;
169
+ min-height:80px;
170
+ font-family:'Courier New',monospace;
171
+ font-size:0.8em;
172
+ }
173
+ input[type="color"]{
174
+ padding:4px;
175
+ height:42px;
176
+ cursor:pointer;
177
+ }
178
+ select{
179
+ cursor:pointer;
180
+ appearance:none;
181
+ 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");
182
+ background-repeat:no-repeat;
183
+ background-position:right 12px center;
184
+ padding-right:32px;
185
+ }
186
+ .toggle-row{
187
+ display:flex;
188
+ align-items:center;
189
+ justify-content:space-between;
190
+ padding:10px 0;
191
+ border-bottom:1px solid rgba(255,255,255,0.05);
192
+ }
193
+ .toggle-row:last-child{
194
+ border-bottom:none;
195
+ }
196
+ .toggle-label{
197
+ font-size:0.88em;
198
+ color:var(--text);
199
+ flex:1;
200
+ padding-right:10px;
201
+ }
202
+ .toggle-label small{
203
+ display:block;
204
+ color:var(--text2);
205
+ font-size:0.82em;
206
+ margin-top:2px;
207
+ }
208
+ .switch{
209
+ position:relative;
210
+ width:48px;
211
+ height:26px;
212
+ flex-shrink:0;
213
+ }
214
+ .switch input{
215
+ opacity:0;
216
+ width:0;
217
+ height:0;
218
+ }
219
+ .slider{
220
+ position:absolute;
221
+ cursor:pointer;
222
+ top:0;left:0;right:0;bottom:0;
223
+ background:var(--surface3);
224
+ border-radius:26px;
225
+ transition:0.3s;
226
+ }
227
+ .slider:before{
228
+ content:"";
229
+ position:absolute;
230
+ height:20px;
231
+ width:20px;
232
+ left:3px;
233
+ bottom:3px;
234
+ background:#fff;
235
+ border-radius:50%;
236
+ transition:0.3s;
237
+ }
238
+ .switch input:checked + .slider{
239
+ background:var(--accent);
240
+ }
241
+ .switch input:checked + .slider:before{
242
+ transform:translateX(22px);
243
+ }
244
+ .file-upload-zone{
245
+ border:2px dashed var(--border);
246
+ border-radius:var(--radius);
247
+ padding:20px;
248
+ text-align:center;
249
+ cursor:pointer;
250
+ transition:all 0.3s;
251
+ background:var(--bg);
252
+ }
253
+ .file-upload-zone:hover{
254
+ border-color:var(--accent);
255
+ background:rgba(233,69,96,0.05);
256
+ }
257
+ .file-upload-zone p{
258
+ color:var(--text2);
259
+ font-size:0.85em;
260
+ margin-top:6px;
261
+ }
262
+ .file-upload-zone .upload-icon{
263
+ font-size:2em;
264
+ margin-bottom:4px;
265
+ }
266
+ .file-upload-zone img{
267
+ max-width:80px;
268
+ max-height:80px;
269
+ border-radius:8px;
270
+ margin-top:8px;
271
+ }
272
+ .grid-2{
273
+ display:grid;
274
+ grid-template-columns:1fr 1fr;
275
+ gap:12px;
276
+ }
277
+ @media(max-width:500px){
278
+ .grid-2{grid-template-columns:1fr}
279
+ }
280
+ .build-btn{
281
+ display:block;
282
+ width:100%;
283
+ padding:16px;
284
+ background:linear-gradient(135deg,var(--accent),#a855f7);
285
+ color:#fff;
286
+ border:none;
287
+ border-radius:var(--radius);
288
+ font-size:1.1em;
289
+ font-weight:700;
290
+ cursor:pointer;
291
+ transition:all 0.3s;
292
+ letter-spacing:0.5px;
293
+ margin-top:20px;
294
+ position:relative;
295
+ overflow:hidden;
296
+ }
297
+ .build-btn:hover{
298
+ transform:translateY(-2px);
299
+ box-shadow:0 8px 30px rgba(233,69,96,0.3);
300
+ }
301
+ .build-btn:active{
302
+ transform:translateY(0);
303
+ }
304
+ .build-btn:disabled{
305
+ opacity:0.6;
306
+ cursor:not-allowed;
307
+ transform:none;
308
+ }
309
+ .build-btn .spinner{
310
+ display:none;
311
+ width:20px;
312
+ height:20px;
313
+ border:3px solid rgba(255,255,255,0.3);
314
+ border-top-color:#fff;
315
+ border-radius:50%;
316
+ animation:spin 0.8s linear infinite;
317
+ margin-right:10px;
318
+ vertical-align:middle;
319
+ }
320
+ .build-btn.loading .spinner{
321
+ display:inline-block;
322
+ }
323
+ @keyframes spin{to{transform:rotate(360deg)}}
324
+ #log-panel{
325
+ display:none;
326
+ margin-top:20px;
327
+ border-radius:var(--radius);
328
+ overflow:hidden;
329
+ border:1px solid var(--border);
330
+ background:var(--surface);
331
+ }
332
+ #log-panel.active{
333
+ display:block;
334
+ }
335
+ .log-header{
336
+ display:flex;
337
+ align-items:center;
338
+ justify-content:space-between;
339
+ padding:12px 18px;
340
+ background:var(--surface2);
341
+ border-bottom:1px solid var(--border);
342
+ }
343
+ .log-header h3{
344
+ font-size:0.95em;
345
+ font-weight:600;
346
+ }
347
+ .log-status{
348
+ padding:3px 12px;
349
+ border-radius:20px;
350
+ font-size:0.75em;
351
+ font-weight:600;
352
+ }
353
+ .log-status.running{background:var(--warning);color:#000}
354
+ .log-status.success{background:var(--success);color:#000}
355
+ .log-status.failed{background:var(--error);color:#fff}
356
+ #log-output{
357
+ padding:14px;
358
+ max-height:500px;
359
+ overflow-y:auto;
360
+ font-family:'Courier New',monospace;
361
+ font-size:0.78em;
362
+ line-height:1.7;
363
+ background:#050510;
364
+ color:#88ff88;
365
+ white-space:pre-wrap;
366
+ word-break:break-all;
367
+ }
368
+ #log-output .error-line{color:var(--error)}
369
+ #log-output .success-line{color:var(--success)}
370
+ #log-output .ai-line{color:#ffd700;font-weight:bold}
371
+ #download-section{
372
+ display:none;
373
+ margin-top:16px;
374
+ text-align:center;
375
+ }
376
+ #download-section.active{
377
+ display:block;
378
+ }
379
+ .download-btn{
380
+ display:inline-block;
381
+ padding:14px 40px;
382
+ background:linear-gradient(135deg,var(--success),#00c853);
383
+ color:#000;
384
+ border:none;
385
+ border-radius:var(--radius);
386
+ font-size:1em;
387
+ font-weight:700;
388
+ cursor:pointer;
389
+ text-decoration:none;
390
+ transition:all 0.3s;
391
+ }
392
+ .download-btn:hover{
393
+ transform:translateY(-2px);
394
+ box-shadow:0 8px 30px rgba(0,230,118,0.3);
395
+ }
396
+ footer{
397
+ text-align:center;
398
+ padding:30px 16px;
399
+ color:var(--text2);
400
+ font-size:0.8em;
401
+ border-top:1px solid var(--border);
402
+ margin-top:30px;
403
+ }
404
+ ::-webkit-scrollbar{width:6px}
405
+ ::-webkit-scrollbar-track{background:var(--bg)}
406
+ ::-webkit-scrollbar-thumb{background:var(--surface3);border-radius:3px}
407
+ ::-webkit-scrollbar-thumb:hover{background:var(--accent)}
408
+ .perm-grid{
409
+ display:grid;
410
+ grid-template-columns:1fr;
411
+ gap:0;
412
+ }
413
+ </style>
414
+ </head>
415
+ <body>
416
+ <header>
417
+ <h1>Android APK/AAB Builder</h1>
418
+ <p>Build production-ready Android apps from any URL</p>
419
+ <span class="badge">45+ Features &bull; Cloud Build</span>
420
+ </header>
421
+
422
+ <div class="container">
423
+ <form id="buildForm" autocomplete="off">
424
+
425
+ <!-- SECTION 1: Core Basics -->
426
+ <div class="accordion open" id="acc-core">
427
+ <div class="accordion-header" onclick="toggleAccordion('acc-core')">
428
+ <span class="section-icon">&#9881;</span>
429
+ <span class="section-title">Core Basics</span>
430
+ <span class="section-count">7 options</span>
431
+ <span class="accordion-arrow">&#9660;</span>
432
+ </div>
433
+ <div class="accordion-body">
434
+ <div class="accordion-content">
435
+ <div class="form-group">
436
+ <label>App Name <span class="required">*</span></label>
437
+ <input type="text" name="app_name" value="MyApp" required>
438
+ </div>
439
+ <div class="form-group">
440
+ <label>Package Name <span class="required">*</span></label>
441
+ <input type="text" name="package_name" value="com.example.myapp" required pattern="^[a-z][a-z0-9]*(\.[a-z][a-z0-9]*)+$">
442
+ </div>
443
+ <div class="form-group">
444
+ <label>Target Web URL <span class="required">*</span></label>
445
+ <input type="url" name="target_url" value="https://www.google.com" required>
446
+ </div>
447
+ <div class="grid-2">
448
+ <div class="form-group">
449
+ <label>Build Format</label>
450
+ <select name="build_format">
451
+ <option value="apk" selected>APK</option>
452
+ <option value="aab">AAB (App Bundle)</option>
453
+ </select>
454
+ </div>
455
+ <div class="form-group">
456
+ <label>Screen Orientation</label>
457
+ <select name="screen_orientation">
458
+ <option value="unspecified" selected>Auto</option>
459
+ <option value="portrait">Portrait Only</option>
460
+ <option value="landscape">Landscape Only</option>
461
+ </select>
462
+ </div>
463
+ </div>
464
+ <div class="grid-2">
465
+ <div class="form-group">
466
+ <label>Version Name</label>
467
+ <input type="text" name="version_name" value="1.0.0">
468
+ </div>
469
+ <div class="form-group">
470
+ <label>Version Code</label>
471
+ <input type="number" name="version_code" value="1" min="1">
472
+ </div>
473
+ </div>
474
+ <div class="grid-2">
475
+ <div class="form-group">
476
+ <label>Developer Name</label>
477
+ <input type="text" name="dev_name" value="Developer">
478
+ </div>
479
+ <div class="form-group">
480
+ <label>Developer Email</label>
481
+ <input type="email" name="dev_email" value="dev@example.com">
482
+ </div>
483
+ </div>
484
+ </div>
485
+ </div>
486
+ </div>
487
+
488
+ <!-- SECTION 2: Branding & UI -->
489
+ <div class="accordion" id="acc-branding">
490
+ <div class="accordion-header" onclick="toggleAccordion('acc-branding')">
491
+ <span class="section-icon">&#127912;</span>
492
+ <span class="section-title">Branding &amp; UI</span>
493
+ <span class="section-count">7 options</span>
494
+ <span class="accordion-arrow">&#9660;</span>
495
+ </div>
496
+ <div class="accordion-body">
497
+ <div class="accordion-content">
498
+ <div class="form-group">
499
+ <label>App Icon (PNG, 512x512 recommended)</label>
500
+ <div class="file-upload-zone" id="iconUploadZone" onclick="document.getElementById('iconFileInput').click()">
501
+ <div class="upload-icon">&#128247;</div>
502
+ <p>Click to upload icon</p>
503
+ <img id="iconPreview" style="display:none" alt="icon preview">
504
+ </div>
505
+ <input type="file" id="iconFileInput" accept="image/png,image/jpeg,image/webp" style="display:none" onchange="handleIconUpload(event)">
506
+ <input type="hidden" name="app_icon_b64" id="app_icon_b64">
507
+ </div>
508
+ <div class="toggle-row">
509
+ <div class="toggle-label">
510
+ Splash Screen
511
+ <small>Show a splash screen before loading the WebView</small>
512
+ </div>
513
+ <label class="switch">
514
+ <input type="checkbox" name="splash_enabled" id="splashToggle" onchange="toggleSplashOptions()">
515
+ <span class="slider"></span>
516
+ </label>
517
+ </div>
518
+ <div id="splashOptions" style="display:none">
519
+ <div class="form-group">
520
+ <label>Splash Media (Image or GIF)</label>
521
+ <div class="file-upload-zone" id="splashUploadZone" onclick="document.getElementById('splashFileInput').click()">
522
+ <div class="upload-icon">&#127756;</div>
523
+ <p>Click to upload splash image/GIF</p>
524
+ <img id="splashPreview" style="display:none" alt="splash preview">
525
+ </div>
526
+ <input type="file" id="splashFileInput" accept="image/png,image/jpeg,image/gif,image/webp" style="display:none" onchange="handleSplashUpload(event)">
527
+ <input type="hidden" name="splash_media_b64" id="splash_media_b64">
528
+ </div>
529
+ <div class="form-group">
530
+ <label>Splash Text</label>
531
+ <input type="text" name="splash_text" value="Loading...">
532
+ </div>
533
+ <div class="grid-2">
534
+ <div class="form-group">
535
+ <label>Splash Text Color</label>
536
+ <input type="color" name="splash_text_color" value="#FFFFFF">
537
+ </div>
538
+ <div class="form-group">
539
+ <label>Splash Background Color</label>
540
+ <input type="color" name="splash_bg_color" value="#1a1a2e">
541
+ </div>
542
+ </div>
543
+ </div>
544
+ <div class="grid-2">
545
+ <div class="form-group">
546
+ <label>Status Bar Color</label>
547
+ <input type="color" name="status_bar_color" value="#1a1a2e">
548
+ </div>
549
+ </div>
550
+ <div class="toggle-row">
551
+ <div class="toggle-label">
552
+ Bottom Navigation Bar
553
+ <small>Home, Refresh, and Back buttons</small>
554
+ </div>
555
+ <label class="switch">
556
+ <input type="checkbox" name="bottom_nav_enabled">
557
+ <span class="slider"></span>
558
+ </label>
559
+ </div>
560
+ </div>
561
+ </div>
562
+ </div>
563
+
564
+ <!-- SECTION 3: Advanced Web Engine & UX -->
565
+ <div class="accordion" id="acc-webengine">
566
+ <div class="accordion-header" onclick="toggleAccordion('acc-webengine')">
567
+ <span class="section-icon">&#9889;</span>
568
+ <span class="section-title">Advanced Web Engine &amp; UX</span>
569
+ <span class="section-count">9 options</span>
570
+ <span class="accordion-arrow">&#9660;</span>
571
+ </div>
572
+ <div class="accordion-body">
573
+ <div class="accordion-content">
574
+ <div class="toggle-row">
575
+ <div class="toggle-label">
576
+ Enable JavaScript
577
+ <small>Required for most modern websites</small>
578
+ </div>
579
+ <label class="switch">
580
+ <input type="checkbox" name="enable_javascript" checked>
581
+ <span class="slider"></span>
582
+ </label>
583
+ </div>
584
+ <div class="toggle-row">
585
+ <div class="toggle-label">
586
+ Enable DOM Storage
587
+ <small>Local Storage for sessions and data persistence</small>
588
+ </div>
589
+ <label class="switch">
590
+ <input type="checkbox" name="enable_dom_storage" checked>
591
+ <span class="slider"></span>
592
+ </label>
593
+ </div>
594
+ <div class="toggle-row">
595
+ <div class="toggle-label">
596
+ Allow File Access
597
+ <small>Upload/Download files from WebView</small>
598
+ </div>
599
+ <label class="switch">
600
+ <input type="checkbox" name="allow_file_access" checked>
601
+ <span class="slider"></span>
602
+ </label>
603
+ </div>
604
+ <div class="toggle-row">
605
+ <div class="toggle-label">
606
+ Media Auto-play
607
+ <small>Auto-play audio/video without user gesture</small>
608
+ </div>
609
+ <label class="switch">
610
+ <input type="checkbox" name="media_autoplay">
611
+ <span class="slider"></span>
612
+ </label>
613
+ </div>
614
+ <div class="toggle-row">
615
+ <div class="toggle-label">
616
+ Enable Zoom Controls
617
+ <small>Pinch-to-zoom and built-in zoom buttons</small>
618
+ </div>
619
+ <label class="switch">
620
+ <input type="checkbox" name="enable_zoom">
621
+ <span class="slider"></span>
622
+ </label>
623
+ </div>
624
+ <div class="form-group" style="margin-top:12px">
625
+ <label>Custom User Agent <small>(leave empty for default)</small></label>
626
+ <input type="text" name="custom_user_agent" placeholder="Mozilla/5.0 ...">
627
+ </div>
628
+ <div class="toggle-row">
629
+ <div class="toggle-label">
630
+ Pull-to-Refresh
631
+ <small>SwipeRefreshLayout for page reload</small>
632
+ </div>
633
+ <label class="switch">
634
+ <input type="checkbox" name="pull_to_refresh" checked>
635
+ <span class="slider"></span>
636
+ </label>
637
+ </div>
638
+ <div class="toggle-row">
639
+ <div class="toggle-label">
640
+ Multiple Windows Support
641
+ <small>JavaScript can open windows automatically</small>
642
+ </div>
643
+ <label class="switch">
644
+ <input type="checkbox" name="multi_windows">
645
+ <span class="slider"></span>
646
+ </label>
647
+ </div>
648
+ <div class="toggle-row">
649
+ <div class="toggle-label">
650
+ Deep Link &amp; Intent Interception
651
+ <small>Open OAuth URLs (Google, Facebook, WhatsApp) externally</small>
652
+ </div>
653
+ <label class="switch">
654
+ <input type="checkbox" name="deep_link_intercept" checked>
655
+ <span class="slider"></span>
656
+ </label>
657
+ </div>
658
+ </div>
659
+ </div>
660
+ </div>
661
+
662
+ <!-- SECTION 4: Security & Protection -->
663
+ <div class="accordion" id="acc-security">
664
+ <div class="accordion-header" onclick="toggleAccordion('acc-security')">
665
+ <span class="section-icon">&#128274;</span>
666
+ <span class="section-title">Security &amp; Protection</span>
667
+ <span class="section-count">9 options</span>
668
+ <span class="accordion-arrow">&#9660;</span>
669
+ </div>
670
+ <div class="accordion-body">
671
+ <div class="accordion-content">
672
+ <div class="toggle-row">
673
+ <div class="toggle-label">
674
+ ProGuard / R8 Obfuscation
675
+ <small>Minify and obfuscate release code</small>
676
+ </div>
677
+ <label class="switch">
678
+ <input type="checkbox" name="proguard_enabled">
679
+ <span class="slider"></span>
680
+ </label>
681
+ </div>
682
+ <div class="toggle-row">
683
+ <div class="toggle-label">
684
+ Root &amp; Emulator Detection
685
+ <small>Auto-exit if device is rooted or emulated</small>
686
+ </div>
687
+ <label class="switch">
688
+ <input type="checkbox" name="root_detection">
689
+ <span class="slider"></span>
690
+ </label>
691
+ </div>
692
+ <div class="toggle-row">
693
+ <div class="toggle-label">
694
+ Prevent Screenshots
695
+ <small>FLAG_SECURE blocks screen capture</small>
696
+ </div>
697
+ <label class="switch">
698
+ <input type="checkbox" name="prevent_screenshots">
699
+ <span class="slider"></span>
700
+ </label>
701
+ </div>
702
+ <div class="toggle-row">
703
+ <div class="toggle-label">
704
+ Clear Cache on Exit
705
+ <small>Wipe WebView cache when app closes</small>
706
+ </div>
707
+ <label class="switch">
708
+ <input type="checkbox" name="clear_cache_exit">
709
+ <span class="slider"></span>
710
+ </label>
711
+ </div>
712
+ <div class="toggle-row">
713
+ <div class="toggle-label">
714
+ Clear Cookies on Exit
715
+ <small>Wipe all cookies &amp; sessions on close</small>
716
+ </div>
717
+ <label class="switch">
718
+ <input type="checkbox" name="clear_cookies_exit">
719
+ <span class="slider"></span>
720
+ </label>
721
+ </div>
722
+ <div class="toggle-row">
723
+ <div class="toggle-label">
724
+ Keep Screen On
725
+ <small>Prevent screen from sleeping</small>
726
+ </div>
727
+ <label class="switch">
728
+ <input type="checkbox" name="keep_screen_on">
729
+ <span class="slider"></span>
730
+ </label>
731
+ </div>
732
+ <div class="toggle-row">
733
+ <div class="toggle-label">
734
+ Fullscreen Immersive Mode
735
+ <small>Hide nav/status bars, fix notch cutout</small>
736
+ </div>
737
+ <label class="switch">
738
+ <input type="checkbox" name="fullscreen_mode">
739
+ <span class="slider"></span>
740
+ </label>
741
+ </div>
742
+ <div class="toggle-row">
743
+ <div class="toggle-label">
744
+ Enforce HTTPS Only
745
+ <small>Block mixed content (HTTP resources)</small>
746
+ </div>
747
+ <label class="switch">
748
+ <input type="checkbox" name="enforce_https" checked>
749
+ <span class="slider"></span>
750
+ </label>
751
+ </div>
752
+ <div class="toggle-row">
753
+ <div class="toggle-label">
754
+ Tamper Protection
755
+ <small>Basic APK signature verification check</small>
756
+ </div>
757
+ <label class="switch">
758
+ <input type="checkbox" name="tamper_protection">
759
+ <span class="slider"></span>
760
+ </label>
761
+ </div>
762
+ </div>
763
+ </div>
764
+ </div>
765
+
766
+ <!-- SECTION 5: Monetization & Services -->
767
+ <div class="accordion" id="acc-monetize">
768
+ <div class="accordion-header" onclick="toggleAccordion('acc-monetize')">
769
+ <span class="section-icon">&#128176;</span>
770
+ <span class="section-title">Monetization &amp; Services</span>
771
+ <span class="section-count">5 options</span>
772
+ <span class="accordion-arrow">&#9660;</span>
773
+ </div>
774
+ <div class="accordion-body">
775
+ <div class="accordion-content">
776
+ <div class="toggle-row">
777
+ <div class="toggle-label">
778
+ Firebase Integration
779
+ <small>Analytics &amp; Cloud Messaging</small>
780
+ </div>
781
+ <label class="switch">
782
+ <input type="checkbox" name="firebase_enabled" id="firebaseToggle" onchange="toggleFirebaseOptions()">
783
+ <span class="slider"></span>
784
+ </label>
785
+ </div>
786
+ <div id="firebaseOptions" style="display:none">
787
+ <div class="form-group">
788
+ <label>google-services.json content <small>(paste JSON)</small></label>
789
+ <textarea name="firebase_json" placeholder='{"project_info":{"project_number":"..."},...}'></textarea>
790
+ </div>
791
+ </div>
792
+ <div class="form-group" style="margin-top:12px">
793
+ <label>OneSignal App ID <small>(for Push Notifications)</small></label>
794
+ <input type="text" name="onesignal_app_id" placeholder="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx">
795
+ </div>
796
+ <div class="toggle-row">
797
+ <div class="toggle-label">
798
+ AdMob Ads
799
+ <small>Enable Google AdMob monetization</small>
800
+ </div>
801
+ <label class="switch">
802
+ <input type="checkbox" name="admob_enabled" id="admobToggle" onchange="toggleAdmobOptions()">
803
+ <span class="slider"></span>
804
+ </label>
805
+ </div>
806
+ <div id="admobOptions" style="display:none">
807
+ <div class="form-group">
808
+ <label>AdMob Banner ID</label>
809
+ <input type="text" name="admob_banner_id" placeholder="ca-app-pub-xxxxx/xxxxx">
810
+ </div>
811
+ <div class="form-group">
812
+ <label>AdMob Interstitial ID</label>
813
+ <input type="text" name="admob_interstitial_id" placeholder="ca-app-pub-xxxxx/xxxxx">
814
+ </div>
815
+ </div>
816
+ </div>
817
+ </div>
818
+ </div>
819
+
820
+ <!-- SECTION 6: Permissions -->
821
+ <div class="accordion" id="acc-perms">
822
+ <div class="accordion-header" onclick="toggleAccordion('acc-perms')">
823
+ <span class="section-icon">&#128272;</span>
824
+ <span class="section-title">Dynamic Permissions</span>
825
+ <span class="section-count">8 options</span>
826
+ <span class="accordion-arrow">&#9660;</span>
827
+ </div>
828
+ <div class="accordion-body">
829
+ <div class="accordion-content">
830
+ <div class="perm-grid">
831
+ <div class="toggle-row">
832
+ <div class="toggle-label">
833
+ INTERNET
834
+ <small>Network access (always enabled)</small>
835
+ </div>
836
+ <label class="switch">
837
+ <input type="checkbox" checked disabled>
838
+ <span class="slider"></span>
839
+ </label>
840
+ </div>
841
+ <div class="toggle-row">
842
+ <div class="toggle-label">
843
+ CAMERA
844
+ <small>Camera access for photo/video capture</small>
845
+ </div>
846
+ <label class="switch">
847
+ <input type="checkbox" name="perm_camera">
848
+ <span class="slider"></span>
849
+ </label>
850
+ </div>
851
+ <div class="toggle-row">
852
+ <div class="toggle-label">
853
+ RECORD_AUDIO (Microphone)
854
+ <small>Microphone access for audio recording</small>
855
+ </div>
856
+ <label class="switch">
857
+ <input type="checkbox" name="perm_microphone">
858
+ <span class="slider"></span>
859
+ </label>
860
+ </div>
861
+ <div class="toggle-row">
862
+ <div class="toggle-label">
863
+ LOCATION (Fine &amp; Coarse)
864
+ <small>GPS and network location access</small>
865
+ </div>
866
+ <label class="switch">
867
+ <input type="checkbox" name="perm_location">
868
+ <span class="slider"></span>
869
+ </label>
870
+ </div>
871
+ <div class="toggle-row">
872
+ <div class="toggle-label">
873
+ STORAGE (Read/Write)
874
+ <small>External storage file access</small>
875
+ </div>
876
+ <label class="switch">
877
+ <input type="checkbox" name="perm_storage">
878
+ <span class="slider"></span>
879
+ </label>
880
+ </div>
881
+ <div class="toggle-row">
882
+ <div class="toggle-label">
883
+ VIBRATE
884
+ <small>Device vibration feedback</small>
885
+ </div>
886
+ <label class="switch">
887
+ <input type="checkbox" name="perm_vibrate">
888
+ <span class="slider"></span>
889
+ </label>
890
+ </div>
891
+ <div class="toggle-row">
892
+ <div class="toggle-label">
893
+ READ_CONTACTS
894
+ <small>Access device contacts</small>
895
+ </div>
896
+ <label class="switch">
897
+ <input type="checkbox" name="perm_contacts">
898
+ <span class="slider"></span>
899
+ </label>
900
+ </div>
901
+ <div class="toggle-row">
902
+ <div class="toggle-label">
903
+ POST_NOTIFICATIONS (Android 13+)
904
+ <small>Auto-prompt for notification permission</small>
905
+ </div>
906
+ <label class="switch">
907
+ <input type="checkbox" name="perm_notifications" checked>
908
+ <span class="slider"></span>
909
+ </label>
910
+ </div>
911
+ </div>
912
+ </div>
913
+ </div>
914
+ </div>
915
+
916
+ <!-- SECTION 7: Resilience & AI -->
917
+ <div class="accordion" id="acc-resilience">
918
+ <div class="accordion-header" onclick="toggleAccordion('acc-resilience')">
919
+ <span class="section-icon">&#129302;</span>
920
+ <span class="section-title">Resilience &amp; AI</span>
921
+ <span class="section-count">2 options</span>
922
+ <span class="accordion-arrow">&#9660;</span>
923
+ </div>
924
+ <div class="accordion-body">
925
+ <div class="accordion-content">
926
+ <div class="toggle-row">
927
+ <div class="toggle-label">
928
+ Offline Mode
929
+ <small>Show local offline.html when no internet detected</small>
930
+ </div>
931
+ <label class="switch">
932
+ <input type="checkbox" name="offline_mode" checked>
933
+ <span class="slider"></span>
934
+ </label>
935
+ </div>
936
+ <div class="toggle-row">
937
+ <div class="toggle-label">
938
+ AI Log Analyzer
939
+ <small>Automatic Arabic diagnosis of build errors</small>
940
+ </div>
941
+ <label class="switch">
942
+ <input type="checkbox" checked disabled>
943
+ <span class="slider"></span>
944
+ </label>
945
+ </div>
946
+ </div>
947
+ </div>
948
+ </div>
949
+
950
+ <button type="submit" class="build-btn" id="buildBtn">
951
+ <span class="spinner" id="buildSpinner"></span>
952
+ <span id="buildBtnText">Build Android App</span>
953
+ </button>
954
+ </form>
955
+
956
+ <div id="log-panel">
957
+ <div class="log-header">
958
+ <h3>Build Log</h3>
959
+ <span class="log-status" id="logStatus">RUNNING</span>
960
+ </div>
961
+ <div id="log-output"></div>
962
+ </div>
963
+
964
+ <div id="download-section">
965
+ <a class="download-btn" id="downloadBtn" href="#">Download Build Artifact</a>
966
+ </div>
967
+
968
+ </div>
969
+
970
+ <footer>
971
+ <p>Android APK/AAB Builder SaaS &bull; Powered by Gradle &amp; Flask</p>
972
+ </footer>
973
+
974
+ <script>
975
+ function toggleAccordion(id){
976
+ var el=document.getElementById(id);
977
+ if(el.classList.contains('open')){
978
+ el.classList.remove('open');
979
+ }else{
980
+ el.classList.add('open');
981
+ }
982
+ }
983
+
984
+ function toggleSplashOptions(){
985
+ var cb=document.getElementById('splashToggle');
986
+ var opts=document.getElementById('splashOptions');
987
+ if(cb.checked){
988
+ opts.style.display='block';
989
+ }else{
990
+ opts.style.display='none';
991
+ }
992
+ }
993
+
994
+ function toggleFirebaseOptions(){
995
+ var cb=document.getElementById('firebaseToggle');
996
+ var opts=document.getElementById('firebaseOptions');
997
+ if(cb.checked){
998
+ opts.style.display='block';
999
+ }else{
1000
+ opts.style.display='none';
1001
+ }
1002
+ }
1003
+
1004
+ function toggleAdmobOptions(){
1005
+ var cb=document.getElementById('admobToggle');
1006
+ var opts=document.getElementById('admobOptions');
1007
+ if(cb.checked){
1008
+ opts.style.display='block';
1009
+ }else{
1010
+ opts.style.display='none';
1011
+ }
1012
+ }
1013
+
1014
+ function handleIconUpload(event){
1015
+ var file=event.target.files[0];
1016
+ if(!file)return;
1017
+ var reader=new FileReader();
1018
+ reader.onload=function(e){
1019
+ var b64=e.target.result;
1020
+ document.getElementById('app_icon_b64').value=b64;
1021
+ var preview=document.getElementById('iconPreview');
1022
+ preview.src=b64;
1023
+ preview.style.display='block';
1024
+ document.querySelector('#iconUploadZone p').textContent=file.name;
1025
+ };
1026
+ reader.readAsDataURL(file);
1027
+ }
1028
+
1029
+ function handleSplashUpload(event){
1030
+ var file=event.target.files[0];
1031
+ if(!file)return;
1032
+ var reader=new FileReader();
1033
+ reader.onload=function(e){
1034
+ var b64=e.target.result;
1035
+ document.getElementById('splash_media_b64').value=b64;
1036
+ var preview=document.getElementById('splashPreview');
1037
+ preview.src=b64;
1038
+ preview.style.display='block';
1039
+ document.querySelector('#splashUploadZone p').textContent=file.name;
1040
+ };
1041
+ reader.readAsDataURL(file);
1042
+ }
1043
+
1044
+ var currentBuildId=null;
1045
+ var eventSource=null;
1046
+
1047
+ document.getElementById('buildForm').addEventListener('submit',function(e){
1048
+ e.preventDefault();
1049
+
1050
+ var btn=document.getElementById('buildBtn');
1051
+ var btnText=document.getElementById('buildBtnText');
1052
+ btn.disabled=true;
1053
+ btn.classList.add('loading');
1054
+ btnText.textContent='Building...';
1055
+
1056
+ var logPanel=document.getElementById('log-panel');
1057
+ var logOutput=document.getElementById('log-output');
1058
+ var logStatusEl=document.getElementById('logStatus');
1059
+ var downloadSection=document.getElementById('download-section');
1060
+
1061
+ logPanel.classList.add('active');
1062
+ logOutput.innerHTML='';
1063
+ logStatusEl.textContent='RUNNING';
1064
+ logStatusEl.className='log-status running';
1065
+ downloadSection.classList.remove('active');
1066
+
1067
+ if(eventSource){
1068
+ eventSource.close();
1069
+ eventSource=null;
1070
+ }
1071
+
1072
+ var formData=new FormData(this);
1073
+
1074
+ fetch('/build',{
1075
+ method:'POST',
1076
+ body:formData
1077
+ })
1078
+ .then(function(resp){return resp.json()})
1079
+ .then(function(data){
1080
+ if(data.status==='started'){
1081
+ currentBuildId=data.build_id;
1082
+ startLogStream(data.build_id);
1083
+ }else{
1084
+ btn.disabled=false;
1085
+ btn.classList.remove('loading');
1086
+ btnText.textContent='Build Android App';
1087
+ logOutput.innerHTML='<span class="error-line">Error: '+((data.message)||'Unknown error')+'</span>\n';
1088
+ logStatusEl.textContent='FAILED';
1089
+ logStatusEl.className='log-status failed';
1090
+ }
1091
+ })
1092
+ .catch(function(err){
1093
+ btn.disabled=false;
1094
+ btn.classList.remove('loading');
1095
+ btnText.textContent='Build Android App';
1096
+ logOutput.innerHTML='<span class="error-line">Network Error: '+err.message+'</span>\n';
1097
+ logStatusEl.textContent='FAILED';
1098
+ logStatusEl.className='log-status failed';
1099
+ });
1100
+ });
1101
+
1102
+ function startLogStream(buildId){
1103
+ var logOutput=document.getElementById('log-output');
1104
+ var logStatusEl=document.getElementById('logStatus');
1105
+ var btn=document.getElementById('buildBtn');
1106
+ var btnText=document.getElementById('buildBtnText');
1107
+ var downloadSection=document.getElementById('download-section');
1108
+ var downloadBtn=document.getElementById('downloadBtn');
1109
+
1110
+ eventSource=new EventSource('/logs/'+buildId);
1111
+
1112
+ eventSource.onmessage=function(event){
1113
+ try{
1114
+ var data=JSON.parse(event.data);
1115
+ if(data.type==='log'){
1116
+ var msg=data.message;
1117
+ var span=document.createElement('span');
1118
+ if(msg.indexOf('ERROR')!==-1||msg.indexOf('FAILED')!==-1||msg.indexOf('error')!==-1||msg.indexOf('Exception')!==-1){
1119
+ span.className='error-line';
1120
+ }else if(msg.indexOf('BUILD SUCCESSFUL')!==-1||msg.indexOf('success')!==-1){
1121
+ span.className='success-line';
1122
+ }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){
1123
+ span.className='ai-line';
1124
+ }
1125
+ span.textContent=msg+'\n';
1126
+ logOutput.appendChild(span);
1127
+ logOutput.scrollTop=logOutput.scrollHeight;
1128
+ }else if(data.type==='status'){
1129
+ eventSource.close();
1130
+ eventSource=null;
1131
+ btn.disabled=false;
1132
+ btn.classList.remove('loading');
1133
+ btnText.textContent='Build Android App';
1134
+ if(data.status==='success'){
1135
+ logStatusEl.textContent='SUCCESS';
1136
+ logStatusEl.className='log-status success';
1137
+ downloadSection.classList.add('active');
1138
+ downloadBtn.href='/download/'+buildId;
1139
+ downloadBtn.download='';
1140
+ }else{
1141
+ logStatusEl.textContent='FAILED';
1142
+ logStatusEl.className='log-status failed';
1143
+ }
1144
+ }
1145
+ }catch(parseErr){
1146
+ var fallbackSpan=document.createElement('span');
1147
+ fallbackSpan.textContent=event.data+'\n';
1148
+ logOutput.appendChild(fallbackSpan);
1149
+ logOutput.scrollTop=logOutput.scrollHeight;
1150
+ }
1151
+ };
1152
+
1153
+ eventSource.onerror=function(err){
1154
+ if(eventSource){
1155
+ eventSource.close();
1156
+ eventSource=null;
1157
+ }
1158
+ btn.disabled=false;
1159
+ btn.classList.remove('loading');
1160
+ btnText.textContent='Build Android App';
1161
+
1162
+ var checkStatus=function(){
1163
+ fetch('/status/'+buildId)
1164
+ .then(function(r){return r.json()})
1165
+ .then(function(d){
1166
+ if(d.status==='success'){
1167
+ logStatusEl.textContent='SUCCESS';
1168
+ logStatusEl.className='log-status success';
1169
+ downloadSection.classList.add('active');
1170
+ downloadBtn.href='/download/'+buildId;
1171
+ }else if(d.status==='failed'){
1172
+ logStatusEl.textContent='FAILED';
1173
+ logStatusEl.className='log-status failed';
1174
+ }else{
1175
+ logStatusEl.textContent='UNKNOWN';
1176
+ logStatusEl.className='log-status failed';
1177
+ }
1178
+ })
1179
+ .catch(function(){
1180
+ logStatusEl.textContent='CONNECTION LOST';
1181
+ logStatusEl.className='log-status failed';
1182
+ });
1183
+ };
1184
+ checkStatus();
1185
+ };
1186
+ }
1187
+ </script>
1188
+ </body>
1189
+ </html>
requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ Flask==3.0.0
2
+ Werkzeug==3.0.1
3
+ Jinja2==3.1.2