import os import sys import json import uuid import time import shutil import base64 import threading import subprocess import re from datetime import datetime from flask import Flask, request, jsonify, send_file, Response app = Flask(__name__) BUILD_DIR_BASE = "/tmp/builds" ROOT_DIR = os.getcwd() KEYSTORE_PATH = os.path.join(ROOT_DIR, "release-key.jks") LOG_STORE = {} BUILD_STATUS = {} BUILD_ARTIFACTS = {} os.makedirs(BUILD_DIR_BASE, exist_ok=True) def init_keystore(): if not os.path.exists(KEYSTORE_PATH): keytool_cmd = [ "keytool", "-genkeypair", "-v", "-keystore", KEYSTORE_PATH, "-keyalg", "RSA", "-keysize", "2048", "-validity", "10000", "-alias", "releasekey", "-storepass", "android123", "-keypass", "android123", "-dname", "CN=SkyData, OU=Mobile, O=Company, L=City, S=State, C=US" ] subprocess.run(keytool_cmd, capture_output=True, timeout=60) init_keystore() def get_sha1(): if not os.path.exists(KEYSTORE_PATH): return None cmd = [ "keytool", "-list", "-v", "-keystore", KEYSTORE_PATH, "-storepass", "android123", "-alias", "releasekey" ] result = subprocess.run(cmd, capture_output=True, text=True) match = re.search(r"SHA1:\s+([A-F0-9:]+)", result.stdout) if match: return match.group(1) return None def analyze_logs_ai(log_text): diagnosis_lines = [] if "Could not resolve" in log_text or "Could not find" in log_text: diagnosis_lines.append("خطأ في التبعيات: لم يتم العثور على مكتبة مطلوبة. تأكد من أن جميع المكتبات المحددة متوفرة في مستودعات Maven/Google.") if "FirebaseApp" in log_text or "google-services" in log_text or "com.google.gms" in log_text: diagnosis_lines.append("خطأ في Firebase: ملف google-services.json غير صالح أو غير متوافق مع اسم الحزمة. تحقق من إعدادات Firebase الخاصة بك.") if "AAPT" in log_text or "resource" in log_text.lower() and "not found" in log_text.lower(): diagnosis_lines.append("خطأ في الموارد: مورد XML أو صورة مفقودة. تأكد من صحة جميع ملفات الموارد.") if "Syntax error" in log_text or "Unexpected token" in log_text or "illegal start" in log_text.lower(): diagnosis_lines.append("خطأ في بناء الجملة: يوجد خطأ نحوي في كود Java. راجع ملفات المصدر بعناية.") if "OutOfMemoryError" in log_text or "heap" in log_text.lower(): diagnosis_lines.append("خطأ في الذاكرة: نفدت ذاكرة JVM أثناء البناء. حاول زيادة حجم الذاكرة المخصصة.") if "SDK location not found" in log_text or "ANDROID_HOME" in log_text: diagnosis_lines.append("خطأ في SDK: لم يتم العثور على Android SDK. تحقق من متغيرات البيئة.") if "Manifest merger failed" in log_text: diagnosis_lines.append("خطأ في دمج الـ Manifest: تعارض بين إعدادات AndroidManifest.xml. راجع الأذونات والإعدادات.") if "Execution failed for task" in log_text: task_match = re.search(r"Execution failed for task '([^']+)'", log_text) if task_match: diagnosis_lines.append("فشل تنفيذ المهمة: {} - تحقق من إعدادات هذه المهمة والتبعيات المرتبطة بها.".format(task_match.group(1))) if "minSdkVersion" in log_text: diagnosis_lines.append("خطأ في إصدار SDK الأدنى: إصدار SDK الأدنى المحدد غير متوافق مع إحدى المكتبات.") if "Duplicate class" in log_text: diagnosis_lines.append("خطأ تكرار: يوجد تكرار في الفئات بين المكتبات. استخدم exclude لحل التعارضات.") if "R8" in log_text and ("error" in log_text.lower() or "failed" in log_text.lower()): diagnosis_lines.append("خطأ في R8/ProGuard: فشل تقليص الكود. تحقق من قواعد ProGuard وتأكد من عدم حذف فئات مطلوبة.") if not diagnosis_lines: if "BUILD SUCCESSFUL" in log_text: diagnosis_lines.append("تم البناء بنجاح! لا توجد أخطاء.") else: diagnosis_lines.append("لم يتم التعرف على خطأ محدد. راجع سجل البناء الكامل يدوياً للبحث عن المشكلة.") return "\n".join(diagnosis_lines) def generate_offline_html(): return """ Offline
🔌

لا يوجد اتصال بالإنترنت

التطبيق بانتظار عودة الشبكة لإعادة الاتصال تلقائياً...

""" def create_build_gradle_root(project_dir, use_firebase, use_onesignal): firebase_classpath = "" onesignal_classpath = "" if use_firebase: firebase_classpath = " classpath 'com.google.gms:google-services:4.4.0'" if use_onesignal: onesignal_classpath = " classpath 'gradle.plugin.com.onesignal:onesignal-gradle-plugin:0.14.0'" content = """buildscript {{ repositories {{ google() mavenCentral() gradlePluginPortal() }} dependencies {{ classpath 'com.android.tools.build:gradle:7.4.2' {firebase_cp} {onesignal_cp} }} }} allprojects {{ repositories {{ google() mavenCentral() gradlePluginPortal() }} }} task clean(type: Delete) {{ delete rootProject.buildDir }} """.format(firebase_cp=firebase_classpath, onesignal_cp=onesignal_classpath) with open(os.path.join(project_dir, "build.gradle"), "w") as f: f.write(content) def create_settings_gradle(project_dir, app_name): content = """rootProject.name = "{app_name}" include ':app' """.format(app_name=app_name.replace('"', '\\"')) with open(os.path.join(project_dir, "settings.gradle"), "w") as f: f.write(content) def create_gradle_properties(project_dir): content = """org.gradle.jvmargs=-Xmx3072m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 org.gradle.daemon=false org.gradle.parallel=true org.gradle.caching=true android.useAndroidX=true android.enableJetifier=true """ with open(os.path.join(project_dir, "gradle.properties"), "w") as f: f.write(content) def create_gradle_wrapper(project_dir): wrapper_dir = os.path.join(project_dir, "gradle", "wrapper") os.makedirs(wrapper_dir, exist_ok=True) props_content = """distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists distributionUrl=https\\://services.gradle.org/distributions/gradle-7.6-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists """ with open(os.path.join(wrapper_dir, "gradle-wrapper.properties"), "w") as f: f.write(props_content) def create_app_build_gradle(app_dir, config): package_name = config.get("package_name", "com.example.webapp") version_name = config.get("version_name", "1.0.0") version_code = config.get("version_code", "1") minify_enabled = "true" if config.get("proguard_enabled") else "false" split_apk = config.get("split_apk", False) use_firebase = config.get("actual_firebase_enabled", False) use_onesignal = bool(config.get("onesignal_app_id", "").strip()) use_admob = config.get("admob_enabled", False) dependencies = [] dependencies.append(" implementation 'androidx.appcompat:appcompat:1.6.1'") dependencies.append(" implementation 'androidx.webkit:webkit:1.8.0'") dependencies.append(" implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'") dependencies.append(" implementation 'com.google.android.material:material:1.10.0'") dependencies.append(" implementation 'com.google.android.gms:play-services-auth:20.7.0'") if use_firebase: dependencies.append(" implementation platform('com.google.firebase:firebase-bom:32.7.0')") dependencies.append(" implementation 'com.google.firebase:firebase-analytics'") dependencies.append(" implementation 'com.google.firebase:firebase-messaging'") if use_onesignal: dependencies.append(" implementation 'com.onesignal:OneSignal:4.114.0'") if use_admob: dependencies.append(" implementation 'com.google.android.gms:play-services-ads:22.6.0'") deps_str = "\n".join(dependencies) plugins_str = " id 'com.android.application'\n" if use_firebase: plugins_str += " id 'com.google.gms.google-services'\n" if use_onesignal: plugins_str += " id 'com.onesignal.androidsdk.onesignal-gradle-plugin'\n" proguard_rules = "" if config.get("proguard_enabled"): proguard_rules = """ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'""" splits_block = "" if split_apk: splits_block = """ splits { abi { enable true reset() include 'armeabi-v7a', 'arm64-v8a' universalApk true } }""" content = """plugins {{ {plugins} }} android {{ namespace '{package_name}' compileSdk 34 defaultConfig {{ applicationId "{package_name}" minSdk 24 targetSdk 34 versionCode {version_code} versionName "{version_name}" }} signingConfigs {{ release {{ storeFile file("../release-key.jks") storePassword "android123" keyAlias "releasekey" keyPassword "android123" }} }} buildTypes {{ release {{ minifyEnabled {minify_enabled} shrinkResources {minify_enabled} {proguard_rules} signingConfig signingConfigs.release }} }} {splits} compileOptions {{ sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 }} lint {{ abortOnError false checkReleaseBuilds false }} }} dependencies {{ {deps_str} }} """.format( plugins=plugins_str, package_name=package_name, version_code=version_code, version_name=version_name, minify_enabled=minify_enabled, proguard_rules=proguard_rules, splits=splits_block, deps_str=deps_str ) with open(os.path.join(app_dir, "build.gradle"), "w") as f: f.write(content) def create_proguard_rules(app_dir, config): rules = """-keepattributes Signature -keepattributes *Annotation* -keep class * extends android.app.Activity -keep class * extends android.app.Application -keep class * extends android.app.Service -keep class * extends android.content.BroadcastReceiver -keep class * extends android.content.ContentProvider -dontwarn okhttp3.** -dontwarn okio.** -dontwarn javax.annotation.** -keep class com.google.android.gms.** { *; } -keep class com.google.firebase.** { *; } -keep class androidx.** { *; } -keep class com.onesignal.** { *; } -dontwarn com.onesignal.** -keepclassmembers class * { @android.webkit.JavascriptInterface ; } """ with open(os.path.join(app_dir, "proguard-rules.pro"), "w") as f: f.write(rules) def create_android_manifest(manifest_dir, config): package_name = config.get("package_name", "com.example.webapp") orientation = config.get("screen_orientation", "unspecified") if orientation == "portrait": orientation_str = "portrait" elif orientation == "landscape": orientation_str = "landscape" else: orientation_str = "unspecified" permissions = [] permissions.append(' ') permissions.append(' ') permissions.append(' ') if config.get("perm_camera"): permissions.append(' ') permissions.append(' ') if config.get("perm_microphone"): permissions.append(' ') if config.get("perm_location"): permissions.append(' ') permissions.append(' ') if config.get("perm_storage"): permissions.append(' ') permissions.append(' ') if config.get("perm_vibrate"): permissions.append(' ') if config.get("perm_contacts"): permissions.append(' ') if config.get("perm_notifications"): permissions.append(' ') permissions_str = "\n".join(permissions) fullscreen_theme = "" if config.get("fullscreen_mode"): fullscreen_theme = 'android:theme="@style/Theme.AppCompat.NoActionBar"' else: fullscreen_theme = 'android:theme="@style/Theme.AppCompat.DayNight.NoActionBar"' splash_activity = "" main_activity_intent = "" if config.get("splash_enabled"): splash_activity = """ """.format(orientation=orientation_str, fullscreen_theme_attr=fullscreen_theme) main_activity_intent = "" else: main_activity_intent = """ """ meta_data = "" if config.get("admob_enabled"): meta_data += """ """ content = """ {permissions} {splash_activity} {main_activity_intent} {meta_data} """.format( permissions=permissions_str, app_name=config.get("app_name", "WebApp").replace('"', '\\"'), fullscreen_theme=fullscreen_theme, splash_activity=splash_activity, orientation=orientation_str, main_activity_intent=main_activity_intent, meta_data=meta_data ) with open(os.path.join(manifest_dir, "AndroidManifest.xml"), "w") as f: f.write(content) def create_main_activity(java_dir, config): package_name = config.get("package_name", "com.example.webapp") target_url = config.get("target_url", "https://www.google.com") enable_js = config.get("enable_javascript", True) enable_dom = config.get("enable_dom_storage", True) allow_file_access = config.get("allow_file_access", False) media_autoplay = config.get("media_autoplay", False) enable_zoom = config.get("enable_zoom", False) custom_ua = config.get("custom_user_agent", "") pull_to_refresh = config.get("pull_to_refresh", False) multi_windows = config.get("multi_windows", False) deep_link_intercept = config.get("deep_link_intercept", True) prevent_screenshots = config.get("prevent_screenshots", False) clear_cache_exit = config.get("clear_cache_exit", False) clear_cookies_exit = config.get("clear_cookies_exit", False) keep_screen_on = config.get("keep_screen_on", False) fullscreen_mode = config.get("fullscreen_mode", False) enforce_https = config.get("enforce_https", True) dev_tools_f12 = config.get("dev_tools_f12", True) root_detection = config.get("root_detection", True) anti_frida = config.get("anti_frida", True) tamper_protection = config.get("tamper_protection", True) prevent_memory_edit = config.get("prevent_memory_edit", True) smart_cache = config.get("smart_cache", True) status_bar_color = config.get("status_bar_color", "#1a1a2e") bottom_nav = config.get("bottom_nav_enabled", False) offline_mode = config.get("offline_mode", True) mixed_content = "WebSettings.MIXED_CONTENT_NEVER_ALLOW" if enforce_https else "WebSettings.MIXED_CONTENT_ALWAYS_ALLOW" use_admob = config.get("admob_enabled", False) onesignal_id = config.get("onesignal_app_id", "").strip() use_onesignal = bool(onesignal_id) google_web_client_id = config.get("google_web_client_id", "") supabase_url = config.get("supabase_url", "") supabase_anon_key = config.get("supabase_anon_key", "") imports = [] imports.append("package {};".format(package_name)) imports.append("") imports.append("import android.app.Activity;") imports.append("import android.content.Context;") imports.append("import android.content.Intent;") imports.append("import android.content.pm.PackageManager;") imports.append("import android.graphics.Bitmap;") imports.append("import android.graphics.Color;") imports.append("import android.net.ConnectivityManager;") imports.append("import android.net.Network;") imports.append("import android.net.NetworkCapabilities;") imports.append("import android.net.NetworkInfo;") imports.append("import android.net.NetworkRequest;") imports.append("import android.net.Uri;") imports.append("import android.os.Build;") imports.append("import android.os.Bundle;") imports.append("import android.util.Log;") imports.append("import android.view.View;") imports.append("import android.view.Window;") imports.append("import android.view.WindowManager;") imports.append("import android.webkit.ConsoleMessage;") imports.append("import android.webkit.CookieManager;") imports.append("import android.webkit.GeolocationPermissions;") imports.append("import android.webkit.JavascriptInterface;") imports.append("import android.webkit.PermissionRequest;") imports.append("import android.webkit.ValueCallback;") imports.append("import android.webkit.WebChromeClient;") imports.append("import android.webkit.WebResourceRequest;") imports.append("import android.webkit.WebSettings;") imports.append("import android.webkit.WebView;") imports.append("import android.webkit.WebViewClient;") imports.append("import android.widget.FrameLayout;") imports.append("import android.widget.LinearLayout;") imports.append("import android.widget.ImageButton;") imports.append("import android.widget.ProgressBar;") imports.append("import android.widget.Toast;") imports.append("import androidx.appcompat.app.AppCompatActivity;") imports.append("import androidx.core.app.ActivityCompat;") imports.append("import androidx.core.content.ContextCompat;") imports.append("import com.google.android.gms.auth.api.signin.GoogleSignIn;") imports.append("import com.google.android.gms.auth.api.signin.GoogleSignInAccount;") imports.append("import com.google.android.gms.auth.api.signin.GoogleSignInClient;") imports.append("import com.google.android.gms.auth.api.signin.GoogleSignInOptions;") imports.append("import com.google.android.gms.common.api.ApiException;") imports.append("import com.google.android.gms.tasks.Task;") if use_onesignal: imports.append("import com.onesignal.OneSignal;") if pull_to_refresh: imports.append("import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;") imports.append("import java.io.File;") imports.append("import java.io.BufferedReader;") imports.append("import java.io.FileReader;") imports.append("import java.util.ArrayList;") imports.append("import java.util.List;") imports.append("") root_detection_code = "" root_detection_method = "" if root_detection: root_detection_method = """ private boolean isRootedOrEmulator() { String[] rootPaths = {"/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su", "/su/bin/su"}; for (String path : rootPaths) { if (new File(path).exists()) return true; } String buildTags = android.os.Build.TAGS; if (buildTags != null && buildTags.contains("test-keys")) return true; if (Build.FINGERPRINT.startsWith("generic") || Build.FINGERPRINT.startsWith("unknown") || Build.MODEL.contains("google_sdk") || Build.MODEL.contains("Emulator") || Build.MODEL.contains("Android SDK built for x86") || Build.MANUFACTURER.contains("Genymotion")) return true; return false; }""" root_detection_code = """ if (isRootedOrEmulator()) { Toast.makeText(this, "Security Alert: Device not secure.", Toast.LENGTH_LONG).show(); finish(); return; }""" frida_method = "" if anti_frida: frida_method = """ private boolean detectFridaAndXposed() { try { java.net.Socket socket = new java.net.Socket("127.0.0.1", 27042); socket.close(); return true; } catch (Exception e) { } try { throw new Exception("Stack trace check"); } catch (Exception e) { for (StackTraceElement element : e.getStackTrace()) { if (element.getClassName().contains("de.robv.android.xposed.") || element.getClassName().contains("com.saurik.substrate.")) { return true; } } } return false; }""" root_detection_code += """ if (detectFridaAndXposed()) { Toast.makeText(this, "Security Alert: Hooking framework detected.", Toast.LENGTH_LONG).show(); finish(); return; }""" tamper_method = "" if tamper_protection: tamper_method = """ private boolean verifyAppSignature() { try { android.content.pm.PackageInfo pInfo = getPackageManager().getPackageInfo(getPackageName(), android.content.pm.PackageManager.GET_SIGNATURES); for (android.content.pm.Signature signature : pInfo.signatures) { java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA"); md.update(signature.toByteArray()); String currentSignature = android.util.Base64.encodeToString(md.digest(), android.util.Base64.DEFAULT).trim(); if (currentSignature != null && currentSignature.length() > 0) return true; } } catch (Exception e) { e.printStackTrace(); } return false; }""" root_detection_code += """ if (!verifyAppSignature()) { Toast.makeText(this, "Security Alert: App integrity compromised.", Toast.LENGTH_LONG).show(); finish(); return; }""" memory_method = "" if prevent_memory_edit: memory_method = """ private boolean detectMemoryEditors() { String[] suspiciousPackages = {"com.catchdroid.gameguardian", "org.sbtools.gamehack", "com.cih.gamecih", "com.silead.guardian"}; PackageManager pm = getPackageManager(); for (String pkg : suspiciousPackages) { try { pm.getPackageInfo(pkg, PackageManager.GET_ACTIVITIES); return true; } catch (PackageManager.NameNotFoundException e) { } } return false; }""" root_detection_code += """ if (detectMemoryEditors()) { Toast.makeText(this, "Security Alert: Memory Editor detected.", Toast.LENGTH_LONG).show(); finish(); return; }""" eruda_injection = "" if dev_tools_f12: eruda_injection = """ view.evaluateJavascript("javascript:(function() { " + "if(document.getElementById('eruda-plugin')) return; " + "var script = document.createElement('script'); " + "script.id = 'eruda-plugin'; " + "script.src = 'https://cdn.jsdelivr.net/npm/eruda'; " + "document.body.appendChild(script); " + "script.onload = function() { eruda.init(); }; " + "})();", null);""" cache_mode = "WebSettings.LOAD_CACHE_ELSE_NETWORK" if smart_cache else "WebSettings.LOAD_DEFAULT" network_reconnect = """ private void setupNetworkListener() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { cm.registerDefaultNetworkCallback(new ConnectivityManager.NetworkCallback() { @Override public void onAvailable(Network network) { runOnUiThread(() -> { if (webView.getUrl() != null && webView.getUrl().contains("offline.html")) { webView.loadUrl(TARGET_URL); } }); } }); } }""" onesignal_setup = "" if use_onesignal: onesignal_setup = """ OneSignal.setLogLevel(OneSignal.LOG_LEVEL.VERBOSE, OneSignal.LOG_LEVEL.NONE); OneSignal.initWithContext(this); OneSignal.setAppId("{os_id}"); OneSignal.promptForPushNotifications(); """.format(os_id=onesignal_id) content = """{imports_str} public class MainActivity extends AppCompatActivity {{ private WebView webView; private static final String TARGET_URL = "{target_url}"; private ValueCallback fileUploadCallback; private static final int FILE_CHOOSER_RESULT = 1002; private GoogleSignInClient mGoogleSignInClient; private static final int RC_SIGN_IN = 9001; @Override protected void onCreate(Bundle savedInstanceState) {{ super.onCreate(savedInstanceState); {root_det_c} {screenshot_c} {os_setup} webView = new WebView(this); webView.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT)); webView.setBackgroundColor(Color.parseColor("#1a1a2e")); WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); settings.setAllowFileAccess(true); settings.setCacheMode({cache_mode}); settings.setDatabaseEnabled(true); settings.setMixedContentMode({mixed_content}); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); cookieManager.setAcceptThirdPartyCookies(webView, true); GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) .requestIdToken("{client_id}") .requestEmail() .build(); mGoogleSignInClient = GoogleSignIn.getClient(this, gso); webView.addJavascriptInterface(new WebAppInterface(this), "AndroidBridge"); setupNetworkListener(); webView.setWebViewClient(new WebViewClient() {{ @Override public void onPageFinished(WebView view, String url) {{ super.onPageFinished(view, url); {eruda_inj} }} @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {{ super.onReceivedError(view, errorCode, description, failingUrl); if (!isNetworkAvailable()) {{ view.loadUrl("file:///android_asset/offline.html"); }} }} }}); webView.setWebChromeClient(new WebChromeClient() {{ @Override public boolean onConsoleMessage(ConsoleMessage consoleMessage) {{ Log.d("SkyData-WebView", consoleMessage.message() + " -- From line " + consoleMessage.lineNumber() + " of " + consoleMessage.sourceId()); return super.onConsoleMessage(consoleMessage); }} @Override public boolean onShowFileChooser(WebView wv, ValueCallback filePathCallback, FileChooserParams fileChooserParams) {{ fileUploadCallback = filePathCallback; Intent intent = fileChooserParams.createIntent(); try {{ startActivityForResult(intent, FILE_CHOOSER_RESULT); }} catch (Exception e) {{ return false; }} return true; }} }}); setContentView(webView); if (!isNetworkAvailable()) {{ webView.loadUrl("file:///android_asset/offline.html"); }} else {{ webView.loadUrl(TARGET_URL); }} }} public class WebAppInterface {{ Context mContext; WebAppInterface(Context c) {{ mContext = c; }} @JavascriptInterface public void startGoogleSignIn() {{ Intent signInIntent = mGoogleSignInClient.getSignInIntent(); startActivityForResult(signInIntent, RC_SIGN_IN); }} @JavascriptInterface public String getSupabaseUrl() {{ return "{supa_url}"; }} @JavascriptInterface public String getSupabaseKey() {{ return "{supa_key}"; }} }} @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) {{ super.onActivityResult(requestCode, resultCode, data); if (requestCode == RC_SIGN_IN) {{ Task task = GoogleSignIn.getSignedInAccountFromIntent(data); try {{ GoogleSignInAccount account = task.getResult(ApiException.class); String idToken = account.getIdToken(); webView.post(() -> {{ webView.evaluateJavascript("javascript:handleGoogleLoginSuccess('" + idToken + "')", null); }}); }} catch (ApiException e) {{ Log.e("SkyData-Auth", "Login Failed Code: " + e.getStatusCode()); webView.post(() -> {{ webView.evaluateJavascript("javascript:handleGoogleLoginError('" + e.getStatusCode() + "')", null); }}); }} return; }} }} @Override public void onBackPressed() {{ if (webView.canGoBack()) {{ webView.goBack(); }} else {{ super.onBackPressed(); }} }} private boolean isNetworkAvailable() {{ ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); if (cm != null) {{ NetworkInfo activeNetwork = cm.getActiveNetworkInfo(); return activeNetwork != null && activeNetwork.isConnectedOrConnecting(); }} return false; }} {root_method} {frida_method} {tamper_method} {memory_method} {network_reconnect} }} """.format( imports_str="\n".join(imports), target_url=target_url.replace('"', '\\"'), screenshot_c= "getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);" if prevent_screenshots else "", cache_mode=cache_mode, mixed_content=mixed_content, client_id=google_web_client_id, eruda_inj=eruda_injection, supa_url=supabase_url, supa_key=supabase_anon_key, root_det_c=root_detection_code, root_method=root_detection_method, frida_method=frida_method, tamper_method=tamper_method, memory_method=memory_method, network_reconnect=network_reconnect, os_setup=onesignal_setup ) with open(os.path.join(java_dir, "MainActivity.java"), "w") as f: f.write(content) def create_splash_activity(java_dir, config): package_name = config.get("package_name", "com.example.webapp") splash_text = config.get("splash_text", "Loading...") splash_text_color = config.get("splash_text_color", "#FFFFFF") splash_bg_color = config.get("splash_bg_color", "#1a1a2e") has_splash_image = config.get("has_splash_image", False) image_code = "" if has_splash_image: image_code = """ ImageView splashImage = new ImageView(this); try { java.io.InputStream is = getAssets().open("splash_media"); android.graphics.drawable.Drawable d = android.graphics.drawable.Drawable.createFromStream(is, null); splashImage.setImageDrawable(d); splashImage.setScaleType(ImageView.ScaleType.FIT_CENTER); LinearLayout.LayoutParams imgParams = new LinearLayout.LayoutParams(600, 600); imgParams.gravity = android.view.Gravity.CENTER; splashImage.setLayoutParams(imgParams); layout.addView(splashImage); is.close(); } catch (Exception e) { e.printStackTrace(); }""" content = """package {package_name}; import android.content.Intent; import android.graphics.Color; import android.os.Bundle; import android.os.Handler; import android.view.Gravity; import android.view.WindowManager; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; public class SplashActivity extends AppCompatActivity {{ @Override protected void onCreate(Bundle savedInstanceState) {{ super.onCreate(savedInstanceState); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); LinearLayout layout = new LinearLayout(this); layout.setOrientation(LinearLayout.VERTICAL); layout.setGravity(Gravity.CENTER); layout.setBackgroundColor(Color.parseColor("{bg_color}")); layout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)); {image_code} TextView textView = new TextView(this); textView.setText("{splash_text}"); textView.setTextColor(Color.parseColor("{text_color}")); textView.setTextSize(24); textView.setGravity(Gravity.CENTER); LinearLayout.LayoutParams textParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT); textParams.topMargin = 40; textView.setLayoutParams(textParams); layout.addView(textView); setContentView(layout); new Handler().postDelayed(new Runnable() {{ @Override public void run() {{ Intent intent = new Intent(SplashActivity.this, MainActivity.class); startActivity(intent); finish(); }} }}, 3000); }} }} """.format( package_name=package_name, bg_color=splash_bg_color if splash_bg_color else "#1a1a2e", image_code=image_code, splash_text=splash_text.replace('"', '\\"') if splash_text else "Loading...", text_color=splash_text_color if splash_text_color else "#FFFFFF" ) with open(os.path.join(java_dir, "SplashActivity.java"), "w") as f: f.write(content) def save_app_icon(res_dir, icon_b64): sizes = {"mipmap-mdpi": 48, "mipmap-hdpi": 72, "mipmap-xhdpi": 96, "mipmap-xxhdpi": 144, "mipmap-xxxhdpi": 192} try: if "," in icon_b64: icon_b64 = icon_b64.split(",")[1] icon_bytes = base64.b64decode(icon_b64) for folder, size in sizes.items(): mipmap_dir = os.path.join(res_dir, folder) os.makedirs(mipmap_dir, exist_ok=True) icon_path = os.path.join(mipmap_dir, "ic_launcher.png") with open(icon_path, "wb") as f: f.write(icon_bytes) except Exception as e: for folder, size in sizes.items(): mipmap_dir = os.path.join(res_dir, folder) os.makedirs(mipmap_dir, exist_ok=True) create_default_icon(os.path.join(mipmap_dir, "ic_launcher.png")) def create_default_icon(path): import struct, zlib width = 48 height = 48 def create_png(w, h, r, g, b): def chunk(chunk_type, data): c = chunk_type + data crc = zlib.crc32(c) & 0xFFFFFFFF return struct.pack(">I", len(data)) + c + struct.pack(">I", crc) header = b'\x89PNG\r\n\x1a\n' ihdr = chunk(b'IHDR', struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0)) raw_data = b'' for y in range(h): raw_data += b'\x00' for x in range(w): raw_data += bytes([r, g, b]) compressed = zlib.compress(raw_data) idat = chunk(b'IDAT', compressed) iend = chunk(b'IEND', b'') return header + ihdr + idat + iend png_data = create_png(width, height, 0x4e, 0x54, 0xc8) with open(path, "wb") as f: f.write(png_data) def setup_project(build_id, config): project_dir = os.path.join(BUILD_DIR_BASE, build_id, "project") os.makedirs(project_dir, exist_ok=True) package_name = config.get("package_name", "com.example.webapp") package_path = package_name.replace(".", "/") app_dir = os.path.join(project_dir, "app") src_main = os.path.join(app_dir, "src", "main") java_dir = os.path.join(src_main, "java", package_path) res_dir = os.path.join(src_main, "res") assets_dir = os.path.join(src_main, "assets") os.makedirs(java_dir, exist_ok=True) os.makedirs(res_dir, exist_ok=True) os.makedirs(assets_dir, exist_ok=True) os.makedirs(os.path.join(res_dir, "values"), exist_ok=True) use_firebase = config.get("firebase_enabled", False) use_onesignal = bool(config.get("onesignal_app_id", "").strip()) firebase_json = config.get("firebase_json", "") actual_firebase_enabled = False if use_firebase and firebase_json.strip(): try: parsed = json.loads(firebase_json) if "project_info" in parsed: os.makedirs(app_dir, exist_ok=True) with open(os.path.join(app_dir, "google-services.json"), "w") as f: json.dump(parsed, f, indent=2) actual_firebase_enabled = True else: LOG_STORE[build_id].append("⚠️ WARNING: Firebase JSON is missing 'project_info'. Firebase integration skipped.") except json.JSONDecodeError: LOG_STORE[build_id].append("⚠️ WARNING: Firebase JSON is invalid. Firebase integration skipped.") config["actual_firebase_enabled"] = actual_firebase_enabled create_build_gradle_root(project_dir, actual_firebase_enabled, use_onesignal) create_settings_gradle(project_dir, config.get("app_name", "WebApp")) create_gradle_properties(project_dir) create_gradle_wrapper(project_dir) create_app_build_gradle(app_dir, config) create_proguard_rules(app_dir, config) create_android_manifest(src_main, config) create_main_activity(java_dir, config) if config.get("splash_enabled"): create_splash_activity(java_dir, config) icon_b64 = config.get("app_icon_b64", "") if icon_b64: save_app_icon(res_dir, icon_b64) else: for folder in ["mipmap-mdpi", "mipmap-hdpi", "mipmap-xhdpi", "mipmap-xxhdpi", "mipmap-xxxhdpi"]: mipmap_dir = os.path.join(res_dir, folder) os.makedirs(mipmap_dir, exist_ok=True) create_default_icon(os.path.join(mipmap_dir, "ic_launcher.png")) splash_media_b64 = config.get("splash_media_b64", "") if splash_media_b64 and config.get("splash_enabled"): try: if "," in splash_media_b64: splash_media_b64 = splash_media_b64.split(",")[1] media_bytes = base64.b64decode(splash_media_b64) with open(os.path.join(assets_dir, "splash_media"), "wb") as f: f.write(media_bytes) config["has_splash_image"] = True create_splash_activity(java_dir, config) except Exception as e: config["has_splash_image"] = False if config.get("offline_mode", True): with open(os.path.join(assets_dir, "offline.html"), "w") as f: f.write(generate_offline_html()) strings_xml = """ {app_name} """.format(app_name=config.get("app_name", "WebApp").replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """)) with open(os.path.join(res_dir, "values", "strings.xml"), "w") as f: f.write(strings_xml) colors_xml = """ {status_color} {status_color} #e94560 """.format(status_color=config.get("status_bar_color", "#1a1a2e")) with open(os.path.join(res_dir, "values", "colors.xml"), "w") as f: f.write(colors_xml) styles_xml = """ """ with open(os.path.join(res_dir, "values", "styles.xml"), "w") as f: f.write(styles_xml) if os.path.exists(KEYSTORE_PATH): shutil.copy2(KEYSTORE_PATH, os.path.join(project_dir, "release-key.jks")) local_props = "sdk.dir={}\n".format(os.environ.get("ANDROID_SDK_ROOT", "/opt/android-sdk")) with open(os.path.join(project_dir, "local.properties"), "w") as f: f.write(local_props) return project_dir def run_build(build_id, config): LOG_STORE[build_id] = [] BUILD_STATUS[build_id] = "running" def log(msg): timestamp = datetime.now().strftime("%H:%M:%S") LOG_STORE[build_id].append("[{}] {}".format(timestamp, msg)) try: log("=== Build Started ===") log("App Name: {}".format(config.get("app_name", "WebApp"))) log("Package: {}".format(config.get("package_name", "com.example.webapp"))) log("Setting up project structure...") project_dir = setup_project(build_id, config) log("Project structure created successfully.") log("Starting Gradle build...") build_format = config.get("build_format", "apk").lower() if build_format == "aab": gradle_task = "bundleRelease" else: gradle_task = "assembleRelease" gradle_bin = os.path.join(os.environ.get("GRADLE_HOME", "/opt/gradle/gradle-7.6"), "bin", "gradle") cmd = [ gradle_bin, gradle_task, "--parallel", "--build-cache", "--no-daemon", "--stacktrace" ] log("Running: {}".format(" ".join(cmd))) env = os.environ.copy() env["ANDROID_SDK_ROOT"] = os.environ.get("ANDROID_SDK_ROOT", "/opt/android-sdk") env["ANDROID_HOME"] = os.environ.get("ANDROID_HOME", "/opt/android-sdk") env["JAVA_HOME"] = os.environ.get("JAVA_HOME", "/usr/lib/jvm/java-17-openjdk-amd64") process = subprocess.Popen( cmd, cwd=project_dir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, env=env, bufsize=1 ) for line in iter(process.stdout.readline, ""): stripped = line.rstrip() if stripped: log(stripped) process.wait() if process.returncode == 0: log("=== BUILD SUCCESSFUL ===") if build_format == "aab": output_path = os.path.join(project_dir, "app", "build", "outputs", "bundle", "release", "app-release.aab") else: base_apk_dir = os.path.join(project_dir, "app", "build", "outputs", "apk", "release") output_path = os.path.join(base_apk_dir, "app-universal-release.apk") if not os.path.exists(output_path): output_path = os.path.join(base_apk_dir, "app-release.apk") if not os.path.exists(output_path): output_path = os.path.join(base_apk_dir, "app-release-unsigned.apk") if os.path.exists(output_path): artifact_name = "{}.{}".format(config.get("app_name", "app").replace(" ", "_"), "aab" if build_format == "aab" else "apk") final_path = os.path.join(BUILD_DIR_BASE, build_id, artifact_name) shutil.copy2(output_path, final_path) BUILD_ARTIFACTS[build_id] = final_path log("Artifact saved: {}".format(artifact_name)) BUILD_STATUS[build_id] = "success" else: log("ERROR: Build output file not found at expected path.") BUILD_STATUS[build_id] = "failed" else: log("=== BUILD FAILED (exit code: {}) ===".format(process.returncode)) BUILD_STATUS[build_id] = "failed" full_log = "\n".join(LOG_STORE[build_id]) ai_analysis = analyze_logs_ai(full_log) log("") log("=== AI Log Analysis (Arabic) ===") for analysis_line in ai_analysis.split("\n"): log(analysis_line) except Exception as e: log("CRITICAL ERROR: {}".format(str(e))) BUILD_STATUS[build_id] = "failed" # ========================================== # دمج الواجهة (HTML) لكي لا تحتاج لملف index.html أبداً! # ========================================== HTML_CONTENT = """ Android APK/AAB Builder SaaS

Android APK/AAB Builder

Build production-ready Android apps from any URL

AAA Studio Features • Cloud Build
🔐Security Keys (OAuth & Supabase)
Core Basics
Split APK (Performance)Build separate lightweight APKs for ARM64 & ARMv7
🎨Branding & UI
📷

Click to upload icon

Splash Screen
Bottom Navigation Bar
Advanced Web Engine & UX
DevTools F12 (Eruda)
Smart Cache
Enable JavaScript
Enable DOM Storage
Allow File Access
Media Auto-play
Enable Zoom Controls
Pull-to-Refresh
Multiple Windows Support
Deep Link & Intent Interception
🔒Security & Protection
Anti-Frida & Xposed
Prevent Memory Editing
ProGuard / R8 Obfuscation
Root & Emulator Detection
Prevent Screenshots
Clear Cache on Exit
Clear Cookies on Exit
Keep Screen On
Fullscreen Immersive Mode
Enforce HTTPS Only
Tamper Protection
💰Monetization & Services
Firebase Integration
AdMob Ads
🔐Dynamic Permissions
INTERNET
CAMERA
RECORD_AUDIO
LOCATION
STORAGE
VIBRATE
READ_CONTACTS
POST_NOTIFICATIONS
🤖Resilience & AI
Offline Mode
AI Log Analyzer

Build Log

RUNNING

Android APK/AAB Builder SaaS • Powered by Gradle & Flask

""" @app.route("/") def index(): return Response(HTML_CONTENT, mimetype="text/html") @app.route("/sha1") def get_sha1_route(): sha1 = get_sha1() if sha1: return jsonify({"sha1": sha1, "formatted": sha1}) return jsonify({"error": "Keystore not found"}), 404 @app.route("/download_keystore") def download_keystore(): if os.path.exists(KEYSTORE_PATH): return send_file(KEYSTORE_PATH, as_attachment=True, download_name="release-key.jks") return jsonify({"error": "Keystore not found"}), 404 @app.route("/build", methods=["POST"]) def start_build(): try: data = request.form.to_dict() config = {} config["supabase_url"] = data.get("supabase_url", "") config["supabase_anon_key"] = data.get("supabase_anon_key", "") raw_google_id = data.get("google_web_client_id", "") google_id_match = re.search(r'([0-9]+-[a-zA-Z0-9_]+\.apps\.googleusercontent\.com)', raw_google_id) if google_id_match: config["google_web_client_id"] = google_id_match.group(1) else: config["google_web_client_id"] = raw_google_id.strip() config["app_name"] = data.get("app_name", "MyApp") config["package_name"] = data.get("package_name", "com.example.myapp") config["target_url"] = data.get("target_url", "https://www.google.com") config["build_format"] = data.get("build_format", "apk") config["version_name"] = data.get("version_name", "1.0.0") config["version_code"] = data.get("version_code", "1") config["screen_orientation"] = data.get("screen_orientation", "unspecified") config["dev_tools_f12"] = data.get("dev_tools_f12", "on") == "on" config["anti_frida"] = data.get("anti_frida") == "on" config["smart_cache"] = data.get("smart_cache") == "on" config["prevent_memory_edit"] = data.get("prevent_memory_edit") == "on" config["split_apk"] = data.get("split_apk") == "on" config["splash_enabled"] = data.get("splash_enabled") == "on" config["splash_text"] = data.get("splash_text", "Loading...") config["splash_text_color"] = data.get("splash_text_color", "#FFFFFF") config["splash_bg_color"] = data.get("splash_bg_color", "#1a1a2e") config["status_bar_color"] = data.get("status_bar_color", "#1a1a2e") config["bottom_nav_enabled"] = data.get("bottom_nav_enabled") == "on" config["enable_javascript"] = data.get("enable_javascript") == "on" config["enable_dom_storage"] = data.get("enable_dom_storage") == "on" config["allow_file_access"] = data.get("allow_file_access") == "on" config["media_autoplay"] = data.get("media_autoplay") == "on" config["enable_zoom"] = data.get("enable_zoom") == "on" config["custom_user_agent"] = data.get("custom_user_agent", "") config["pull_to_refresh"] = data.get("pull_to_refresh") == "on" config["multi_windows"] = data.get("multi_windows") == "on" config["deep_link_intercept"] = data.get("deep_link_intercept") == "on" config["proguard_enabled"] = data.get("proguard_enabled") == "on" config["root_detection"] = data.get("root_detection") == "on" config["prevent_screenshots"] = data.get("prevent_screenshots") == "on" config["clear_cache_exit"] = data.get("clear_cache_exit") == "on" config["clear_cookies_exit"] = data.get("clear_cookies_exit") == "on" config["keep_screen_on"] = data.get("keep_screen_on") == "on" config["fullscreen_mode"] = data.get("fullscreen_mode") == "on" config["enforce_https"] = data.get("enforce_https") == "on" config["tamper_protection"] = data.get("tamper_protection") == "on" config["firebase_enabled"] = data.get("firebase_enabled") == "on" config["firebase_json"] = data.get("firebase_json", "") config["onesignal_enabled"] = bool(data.get("onesignal_app_id", "").strip()) config["onesignal_app_id"] = data.get("onesignal_app_id", "") config["admob_enabled"] = data.get("admob_enabled") == "on" config["admob_banner_id"] = data.get("admob_banner_id", "") config["admob_interstitial_id"] = data.get("admob_interstitial_id", "") config["perm_camera"] = data.get("perm_camera") == "on" config["perm_microphone"] = data.get("perm_microphone") == "on" config["perm_location"] = data.get("perm_location") == "on" config["perm_storage"] = data.get("perm_storage") == "on" config["perm_vibrate"] = data.get("perm_vibrate") == "on" config["perm_contacts"] = data.get("perm_contacts") == "on" config["perm_notifications"] = data.get("perm_notifications") == "on" config["offline_mode"] = data.get("offline_mode") == "on" config["app_icon_b64"] = data.get("app_icon_b64", "") config["splash_media_b64"] = data.get("splash_media_b64", "") config["has_splash_image"] = bool(data.get("splash_media_b64", "").strip()) build_id = str(uuid.uuid4())[:12] os.makedirs(os.path.join(BUILD_DIR_BASE, build_id), exist_ok=True) thread = threading.Thread(target=run_build, args=(build_id, config), daemon=True) thread.start() return jsonify({"status": "started", "build_id": build_id}) except Exception as e: return jsonify({"status": "error", "message": str(e)}), 500 @app.route("/logs/") def get_logs(build_id): def generate(): last_index = 0 while True: logs = LOG_STORE.get(build_id, []) status = BUILD_STATUS.get(build_id, "unknown") if last_index < len(logs): new_logs = logs[last_index:] last_index = len(logs) for log_line in new_logs: yield "data: {}\n\n".format(json.dumps({"type": "log", "message": log_line})) if status in ("success", "failed"): yield "data: {}\n\n".format(json.dumps({"type": "status", "status": status, "build_id": build_id})) break time.sleep(0.5) return Response(generate(), mimetype="text/event-stream", headers={ "Cache-Control": "no-cache", "X-Accel-Buffering": "no", "Connection": "keep-alive" }) @app.route("/download/") def download_artifact(build_id): artifact_path = BUILD_ARTIFACTS.get(build_id) if artifact_path and os.path.exists(artifact_path): return send_file(artifact_path, as_attachment=True, download_name=os.path.basename(artifact_path)) else: return jsonify({"error": "Artifact not found"}), 404 @app.route("/status/") def get_status(build_id): status = BUILD_STATUS.get(build_id, "unknown") return jsonify({"status": status, "build_id": build_id}) if __name__ == "__main__": app.run(host="0.0.0.0", port=7860, debug=False, threaded=True)