File size: 9,503 Bytes
ad7ea0d
 
 
 
 
 
 
09becbc
ad7ea0d
 
 
a49ab03
ad7ea0d
09becbc
ad7ea0d
09becbc
 
 
ad7ea0d
09becbc
ad7ea0d
 
 
 
 
 
 
 
 
 
 
09becbc
ad7ea0d
 
09becbc
ad7ea0d
a49ab03
b27d2f2
 
 
ad7ea0d
 
 
 
 
a49ab03
ad7ea0d
 
 
 
a49ab03
ad7ea0d
 
09becbc
 
 
ad7ea0d
09becbc
 
 
b27d2f2
 
93db0b6
 
 
 
 
b27d2f2
 
09becbc
 
93db0b6
09becbc
 
93db0b6
09becbc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b27d2f2
09becbc
 
 
 
 
 
b27d2f2
09becbc
 
 
a49ab03
09becbc
b27d2f2
09becbc
 
 
 
 
 
b27d2f2
 
09becbc
 
 
 
 
 
 
b27d2f2
09becbc
b27d2f2
 
09becbc
 
 
 
 
 
 
 
 
 
 
 
 
 
ad7ea0d
09becbc
ad7ea0d
 
 
09becbc
 
ad7ea0d
09becbc
ad7ea0d
 
 
 
 
 
 
 
 
 
 
 
93db0b6
ad7ea0d
09becbc
ad7ea0d
b27d2f2
09becbc
 
ad7ea0d
09becbc
 
 
b27d2f2
09becbc
 
b27d2f2
 
93db0b6
b27d2f2
09becbc
 
b27d2f2
09becbc
93db0b6
09becbc
 
93db0b6
09becbc
 
 
 
 
 
 
93db0b6
09becbc
 
 
 
93db0b6
ad7ea0d
 
 
93db0b6
ad7ea0d
 
93db0b6
ad7ea0d
09becbc
93db0b6
 
ad7ea0d
93db0b6
09becbc
ad7ea0d
b27d2f2
09becbc
 
ad7ea0d
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
import streamlit as st
import os
import zipfile
import subprocess
import shutil
import requests
import re
import time

# --- CONFIGURATION ---
API_ENDPOINT = "https://gen.pollinations.ai/v1/chat/completions"
API_KEY = "sk_qDYtDvntvqzjmp23XKVzr9lBXYwYndaR"
AI_MODEL = "claude"
MAX_RETRIES = 10  # AI 10 baar koshish karega haar manne se pehle

st.set_page_config(page_title="AI Terminator Builder", layout="centered")
st.title("πŸ€– AI Terminator Builder")
st.markdown("This AI will **Hunt & Fix** errors recursively until the APK is built.")

# --- HELPER: AI API ---
def ask_claude(system_prompt, user_message):
    headers = {
        "Content-Type": "application/json",
        "Authorization": f"Bearer {API_KEY}"
    }
    payload = {
        "model": AI_MODEL,
        "messages": [
            {"role": "system", "content": system_prompt},
            {"role": "user", "content": user_message}
        ],
        "temperature": 0.1 # High precision
    }
    try:
        response = requests.post(API_ENDPOINT, json=payload, headers=headers, timeout=60)
        if response.status_code == 200:
            return True, response.json()['choices'][0]['message']['content']
        return False, f"API Error: {response.text}"
    except Exception as e:
        return False, f"Connection Error: {str(e)}"

# --- HELPER: KEYSTORE ---
def generate_keystore():
    keystore_path = "debug.keystore"
    if not os.path.exists(keystore_path):
        subprocess.run([
            "keytool", "-genkey", "-v", "-keystore", keystore_path, 
            "-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 keystore_path

# --- STEP 1: FORCE UPGRADE (Base Setup) ---
def initial_modernization(project_root, status_box):
    status_box.write("πŸ› οΈ AI is modernizing project engine first...")
    
    # 1. Gradle Wrapper Upgrade to 8.0
    wrapper_props = os.path.join(project_root, "gradle", "wrapper", "gradle-wrapper.properties")
    os.makedirs(os.path.dirname(wrapper_props), exist_ok=True)
    
    new_props = """distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
"""
    with open(wrapper_props, "w") as f: f.write(new_props)
    
    # 2. Root Build.gradle Fix
    root_gradle = None
    for root, dirs, files in os.walk(project_root):
        if "build.gradle" in files and "app" not in root:
            root_gradle = os.path.join(root, "build.gradle")
            break
            
    if root_gradle:
        with open(root_gradle, 'r') as f: content = f.read()
        # AI se kaho dependencies update kare
        p = "Update 'classpath' dependencies to be compatible with Gradle 8. Use 'com.android.tools.build:gradle:8.0.0'. Output full code."
        ok, fix = ask_claude(p, content)
        if ok:
            clean = fix.replace("```gradle","").replace("```","").strip()
            with open(root_gradle, 'w') as f: f.write(clean)
            status_box.write("βœ… AI updated root build.gradle configuration.")

# --- STEP 2: THE FIXER LOOP ---
def analyze_and_fix_errors(project_root, error_log, status_box):
    # Regex to find file paths in error logs (Java, Kotlin, XML, Gradle)
    # Pattern looks for: /path/to/file.ext:line_number: error
    match = re.search(r'(/[\w/-]+\.(java|kt|xml|gradle))', error_log)
    
    target_file = None
    
    if match:
        extracted_path = match.group(1)
        # Verify path exists (Handle relative/absolute mismatch)
        if os.path.exists(extracted_path):
            target_file = extracted_path
        else:
            # Try to find by filename
            filename = os.path.basename(extracted_path)
            for root, dirs, files in os.walk(project_root):
                if filename in files:
                    target_file = os.path.join(root, filename)
                    break
    
    # Agar Logs se file nahi mili, to fallback 'build.gradle' (App Level) par jao
    if not target_file:
        for root, dirs, files in os.walk(project_root):
            if "build.gradle" in files and "app" in root:
                target_file = os.path.join(root, "build.gradle")
                break
    
    if target_file:
        file_name = os.path.basename(target_file)
        status_box.write(f"🧐 AI identified issue in: **{file_name}**")
        
        with open(target_file, 'r', errors='ignore') as f: current_code = f.read()
        
        system_prompt = """
        You are an elite Android debugger. 
        Analyze the Error Log and the File Content.
        Fix the code to resolve the build error.
        Ensure Java 17 / Gradle 8 compatibility.
        RETURN ONLY THE CORRECTED CODE. NO EXPLANATION.
        """
        
        user_message = f"""
        ERROR LOG:
        {error_log[-2000:]}
        
        FILE CONTENT ({file_name}):
        {current_code}
        """
        
        success, ai_fix = ask_claude(system_prompt, user_message)
        
        if success:
            # Clean response
            cleaned_code = ai_fix.replace("```java", "").replace("```kotlin", "").replace("```xml", "").replace("```gradle", "").replace("```", "").strip()
            
            with open(target_file, 'w', encoding='utf-8') as f:
                f.write(cleaned_code)
            
            status_box.write(f"πŸ€– **AI Message:** I have corrected `{file_name}` to fix the reported error.")
            return True
        else:
            status_box.error("AI API Failed to respond.")
            return False
    else:
        status_box.warning("Could not identify specific broken file from logs. Trying generic fix...")
        return False

# --- MAIN UI ---
uploaded_file = st.file_uploader("Project ZIP Upload", type=["zip"])

if uploaded_file:
    if st.button("πŸš€ Start Terminator Build"):
        status_box = st.status("Initializing System...", expanded=True)
        
        # 1. EXTRACT
        if os.path.exists("project_extract"): shutil.rmtree("project_extract")
        os.makedirs("project_extract", exist_ok=True)
        with open("uploaded.zip", "wb") as f: f.write(uploaded_file.getbuffer())
        with zipfile.ZipFile("uploaded.zip", "r") as z: z.extractall("project_extract")
        
        project_root = None
        for root, dirs, files in os.walk("project_extract"):
            if "gradlew" in files:
                project_root = root
                break
        
        if not project_root:
            st.error("Gradlew not found.")
            st.stop()
            
        subprocess.run(["chmod", "+x", os.path.join(project_root, "gradlew")])
        
        # 2. PRE-BUILD MODERNIZATION
        initial_modernization(project_root, status_box)
        
        # 3. THE LOOP (MAX RETRIES)
        build_success = False
        cmd = ["./gradlew", "assembleRelease", "--no-daemon", "--stacktrace"]
        
        for attempt in range(1, MAX_RETRIES + 1):
            status_box.write(f"πŸ”„ **Build Attempt {attempt}/{MAX_RETRIES}** is running...")
            
            result = subprocess.run(cmd, cwd=project_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            
            if result.returncode == 0:
                build_success = True
                break # Loop khatam, kaam ho gaya
            else:
                status_box.write(f"❌ Build Failed (Attempt {attempt}). AI is analyzing logs...")
                
                # AI FIXING LOGIC
                fixed = analyze_and_fix_errors(project_root, result.stderr, status_box)
                
                if not fixed:
                    # Agar AI file nahi dhoond paya, to shayad "clean" ki zaroorat hai
                    subprocess.run(["./gradlew", "clean"], cwd=project_root)
                    status_box.write("🧹 AI cleaned the project cache.")
                
                # Thora saans lene do system ko
                time.sleep(1)

        # 4. FINAL OUTCOME
        if build_success:
            status_box.update(label="βœ… Build Successful! Signing APK...", state="complete")
            
            target_apk = None
            for root, dirs, files in os.walk(project_root):
                for file in files:
                    if file.endswith(".apk") and "release" in file:
                        target_apk = os.path.join(root, file)
                        break
            
            if target_apk:
                keystore = generate_keystore()
                signed = "AI_Terminator_App.apk"
                subprocess.run(["zipalign", "-v", "-p", "4", target_apk, "aligned.apk"], stdout=subprocess.DEVNULL)
                subprocess.run(["apksigner", "sign", "--ks", keystore, "--ks-pass", "pass:android", "--out", signed, "aligned.apk"])
                
                with open(signed, "rb") as f:
                    st.download_button("⬇️ Download Final APK", f, file_name=signed)
        else:
            status_box.update(label="❌ Mission Failed", state="error")
            st.error(f"AI tried {MAX_RETRIES} times but could not fix all errors.")
            st.text_area("Last Error Log", result.stderr[-2000:], height=300)

        if os.path.exists("uploaded.zip"): os.remove("uploaded.zip")