Spaces:
Sleeping
Sleeping
| import streamlit as st | |
| import os, zipfile, subprocess, shutil | |
| st.set_page_config(page_title="Android Build Server", layout="centered") | |
| st.title("π± Android APK Generator") | |
| st.markdown("Flutter ya Native Android β dono projects ka APK banayega aur **Auto-Sign** karega.") | |
| def setup_java_env(): | |
| # Automatically detect valid JAVA_HOME path from system java binary | |
| current_jh = os.environ.get("JAVA_HOME", "") | |
| if not current_jh or not os.path.exists(os.path.join(current_jh, "bin", "java")): | |
| java_bin = shutil.which("java") | |
| if java_bin: | |
| real_path = os.path.realpath(java_bin) | |
| detected_jh = os.path.dirname(os.path.dirname(real_path)) | |
| os.environ["JAVA_HOME"] = detected_jh | |
| def generate_keystore(): | |
| kp = "debug.keystore" | |
| if not os.path.exists(kp): | |
| subprocess.run([ | |
| "keytool","-genkey","-v","-keystore",kp,"-storepass","android", | |
| "-alias","androiddebugkey","-keypass","android","-keyalg","RSA", | |
| "-keysize","2048","-validity","10000", | |
| "-dname","CN=AndroidDebug,O=Android,C=US" | |
| ], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | |
| return kp | |
| def find_apk(d): | |
| for r,_,fs in os.walk(d): | |
| for f in fs: | |
| if f.endswith(".apk") and "release" in f.lower(): | |
| return os.path.join(r,f) | |
| return None | |
| def sign_apk(unsigned): | |
| al = "aligned.apk"; sg = "Installable_App.apk" | |
| try: | |
| subprocess.run(["zipalign","-v","-p","4",unsigned,al], | |
| stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,check=True) | |
| target = al | |
| except: | |
| target = unsigned | |
| generate_keystore() | |
| r = subprocess.run([ | |
| "apksigner","sign","--ks","debug.keystore","--ks-pass","pass:android", | |
| "--out",sg,target | |
| ], capture_output=True, text=True) | |
| return sg if r.returncode == 0 else None | |
| f = st.file_uploader("Project ZIP Upload Karein", type=["zip"]) | |
| if f is not None: | |
| if st.button("π¨ Create & Sign APK"): | |
| setup_java_env() # Auto detect and set JAVA_HOME | |
| sts = st.empty(); bar = st.progress(0) | |
| if os.path.exists("project_extract"): shutil.rmtree("project_extract") | |
| os.makedirs("project_extract") | |
| sts.info("π¦ ZIP extract ho raha hai...") | |
| with open("uploaded_project.zip","wb") as w: w.write(f.getbuffer()) | |
| with zipfile.ZipFile("uploaded_project.zip","r") as z: z.extractall("project_extract") | |
| bar.progress(15) | |
| flutter_root = None; native_root = None | |
| for r,_,fs in os.walk("project_extract"): | |
| if "pubspec.yaml" in fs and not flutter_root: flutter_root = r | |
| if "gradlew" in fs and not native_root: native_root = r | |
| unsigned = None | |
| if flutter_root: | |
| sts.info("π¦ Flutter project β flutter build apk chal raha hai...") | |
| bar.progress(20) | |
| android_dir = os.path.join(flutter_root, "android") | |
| if not os.path.exists(android_dir): | |
| os.makedirs(android_dir) | |
| sdk_dir = os.environ.get('ANDROID_HOME', os.environ.get('ANDROID_SDK_ROOT', '/sdk')) | |
| flutter_dir = os.environ.get('FLUTTER_HOME', '/opt/flutter') | |
| with open(os.path.join(android_dir, "local.properties"), "w") as lf: | |
| lf.write(f"sdk.dir={sdk_dir}\n") | |
| lf.write(f"flutter.sdk={flutter_dir}\n") | |
| lf.write("flutter.buildMode=release\n") | |
| r1 = subprocess.run(["flutter","pub","get"], cwd=flutter_root, capture_output=True, text=True) | |
| if r1.returncode != 0: | |
| st.error("β flutter pub get fail!"); st.text(r1.stderr[-2000:]) | |
| else: | |
| bar.progress(35) | |
| r2 = subprocess.run(["flutter","build","apk","--release"], cwd=flutter_root, capture_output=True, text=True) | |
| bar.progress(85) | |
| if r2.returncode == 0: | |
| apk_dir = os.path.join(flutter_root,"build","app","outputs","flutter-apk") | |
| unsigned = find_apk(apk_dir) | |
| else: | |
| st.error("β Flutter Build Failed!") | |
| st.text_area("Error Log", r2.stderr[-3000:], height=250) | |
| elif native_root: | |
| sts.info("π© Native Android project β gradlew assembleRelease chal raha hai...") | |
| bar.progress(20) | |
| sdk_dir = os.environ.get('ANDROID_HOME', os.environ.get('ANDROID_SDK_ROOT', '/sdk')) | |
| with open(os.path.join(native_root, "local.properties"), "w") as lf: | |
| lf.write(f"sdk.dir={sdk_dir}\n") | |
| subprocess.run(["chmod","+x",os.path.join(native_root,"gradlew")]) | |
| bar.progress(25) | |
| r = subprocess.run(["./gradlew","assembleRelease","--no-daemon","--stacktrace"], | |
| cwd=native_root, capture_output=True, text=True) | |
| bar.progress(85) | |
| if r.returncode == 0: | |
| unsigned = find_apk(native_root) | |
| else: | |
| st.error("β Gradle Build Failed!") | |
| st.text_area("Error Log", r.stderr[-3000:], height=250) | |
| else: | |
| st.error("β ZIP mein na pubspec.yaml mila, na gradlew.") | |
| if unsigned and os.path.exists(unsigned): | |
| sts.info("π APK sign ho raha hai...") | |
| sg = sign_apk(unsigned) | |
| if sg: | |
| bar.progress(100); sts.success("π APK Ready!") | |
| with open(sg,"rb") as dl: | |
| st.download_button("β¬οΈ Download APK", data=dl, file_name=sg, mime="application/vnd.android.package-archive") | |
| else: | |
| st.error("Signing fail ho gayi.") | |
| if os.path.exists("uploaded_project.zip"): os.remove("uploaded_project.zip") |