FerrellSyntheticIntelligence commited on
Commit
f67cb0e
·
verified ·
1 Parent(s): 131b582

Upload android_compiler.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. android_compiler.py +410 -0
android_compiler.py ADDED
@@ -0,0 +1,410 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ FSI_FELON ANDROID TERMINAL COMPILER v2
4
+ No Android Studio. No wrapper download. Uses system Gradle + Android SDK.
5
+ """
6
+ import os, sys, subprocess, shutil, zipfile, urllib.request, glob
7
+ from pathlib import Path
8
+
9
+ ANDROID_SDK_DIR = os.path.expanduser("~/android-sdk")
10
+ GRADLE_VERSION = "8.5"
11
+
12
+ class AndroidCompiler:
13
+ def __init__(self):
14
+ self.sdk_dir = ANDROID_SDK_DIR
15
+ self.project_dir = None
16
+ self.app_name = "FSIApp"
17
+ self.package = "com.fsi.app"
18
+ self._ensure_sdk()
19
+
20
+ def _sdk_manager_path(self):
21
+ p = os.path.join(self.sdk_dir, "cmdline-tools", "latest", "bin", "sdkmanager")
22
+ if os.path.exists(p):
23
+ return p
24
+ p2 = os.path.join(self.sdk_dir, "cmdline-tools", "bin", "sdkmanager")
25
+ return p2 if os.path.exists(p2) else None
26
+
27
+ def _ensure_sdk(self):
28
+ """Download Android SDK command-line tools if not present."""
29
+ if self._sdk_manager_path():
30
+ installed = self._ensure_platform()
31
+ if installed:
32
+ print(" ✅ Android SDK ready")
33
+ return True
34
+
35
+ print(" 📥 Downloading Android SDK command-line tools...")
36
+ os.makedirs(self.sdk_dir, exist_ok=True)
37
+
38
+ sdk_url = "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"
39
+ zip_path = f"{self.sdk_dir}/cmdline-tools.zip"
40
+
41
+ try:
42
+ urllib.request.urlretrieve(sdk_url, zip_path)
43
+ print(" ✅ SDK tools downloaded")
44
+
45
+ # Extract
46
+ with zipfile.ZipFile(zip_path, 'r') as z:
47
+ z.extractall(self.sdk_dir)
48
+
49
+ # Move to correct structure
50
+ old = os.path.join(self.sdk_dir, "cmdline-tools")
51
+ new = os.path.join(self.sdk_dir, "cmdline-tools", "latest")
52
+ if os.path.exists(new):
53
+ shutil.rmtree(new)
54
+ os.makedirs(os.path.dirname(new), exist_ok=True)
55
+ # Move contents rather than directory to avoid nested structure
56
+ for item in os.listdir(old):
57
+ shutil.move(os.path.join(old, item), new)
58
+ os.rmdir(old)
59
+
60
+ os.remove(zip_path)
61
+ print(" ✅ SDK ready")
62
+ return self._ensure_platform()
63
+
64
+ except Exception as e:
65
+ print(f" ⚠️ SDK download failed: {e}")
66
+ print(" 💡 Manual install: https://developer.android.com/studio#command-tools")
67
+ return False
68
+
69
+ def _ensure_platform(self):
70
+ """Ensure required Android platform and build tools are installed."""
71
+ sm = self._sdk_manager_path()
72
+ if not sm:
73
+ return False
74
+ try:
75
+ r = subprocess.run([sm, "--list", "--sdk_root=" + self.sdk_dir], capture_output=True, text=True, timeout=30)
76
+ if "platforms;android-34" not in r.stdout:
77
+ print(" 📥 Installing Android SDK platform 34...")
78
+ subprocess.run([sm, "--sdk_root=" + self.sdk_dir, "platforms;android-34"], capture_output=True, timeout=120)
79
+ if "build-tools;34.0.0" not in r.stdout:
80
+ print(" 📥 Installing build-tools 34.0.0...")
81
+ subprocess.run([sm, "--sdk_root=" + self.sdk_dir, "build-tools;34.0.0"], capture_output=True, timeout=120)
82
+ return True
83
+ except:
84
+ return False
85
+
86
+ def _find_aapt(self):
87
+ bt = os.path.join(self.sdk_dir, "build-tools")
88
+ if os.path.exists(bt):
89
+ for v in sorted(os.listdir(bt), reverse=True):
90
+ aapt = os.path.join(bt, v, "aapt")
91
+ if os.path.exists(aapt):
92
+ return aapt
93
+ return shutil.which("aapt")
94
+
95
+ def verify_apk(self, apk_path=None):
96
+ """Verify APK using aapt dump badging."""
97
+ path = apk_path or self._find_apk()
98
+ if not path or not os.path.exists(path):
99
+ return {"valid": False, "error": "APK not found"}
100
+ aapt = self._find_aapt()
101
+ if not aapt:
102
+ return {"valid": False, "error": "aapt not found — install Android build-tools"}
103
+ try:
104
+ r = subprocess.run([aapt, "dump", "badging", path], capture_output=True, text=True, timeout=15)
105
+ if r.returncode == 0:
106
+ lines = r.stdout.split("\n")
107
+ info = {"valid": True, "path": path, "size": os.path.getsize(path)}
108
+ for l in lines:
109
+ if "package:" in l:
110
+ for p in l.split():
111
+ if "name=" in p: info["package"] = p.split("=")[1].strip("'")
112
+ if "versionCode=" in p: info["version_code"] = p.split("=")[1].strip("'")
113
+ if "versionName=" in p: info["version_name"] = p.split("=")[1].strip("'")
114
+ if "launchable-activity:" in l:
115
+ info["activity"] = l.split("name=")[1].split()[0].strip("'")
116
+ if "sdkVersion:" in l:
117
+ info["min_sdk"] = l.split(":")[1].strip()
118
+ if "targetSdkVersion:" in l:
119
+ info["target_sdk"] = l.split(":")[1].strip()
120
+ return info
121
+ else:
122
+ return {"valid": False, "error": r.stderr[:200]}
123
+ except Exception as e:
124
+ return {"valid": False, "error": str(e)}
125
+
126
+ def install_apk(self, apk_path=None, serial=None):
127
+ """Install APK via adb."""
128
+ path = apk_path or self._find_apk()
129
+ if not path or not os.path.exists(path):
130
+ return {"success": False, "error": "APK not found"}
131
+ adb = shutil.which("adb")
132
+ if not adb:
133
+ return {"success": False, "error": "adb not found — install Android platform-tools"}
134
+ cmd = [adb]
135
+ if serial:
136
+ cmd += ["-s", serial]
137
+ cmd += ["install", "-r", path]
138
+ try:
139
+ r = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
140
+ if "Success" in r.stdout:
141
+ return {"success": True, "output": r.stdout.strip()}
142
+ else:
143
+ return {"success": False, "error": r.stdout[-200:] or r.stderr[-200:]}
144
+ except Exception as e:
145
+ return {"success": False, "error": str(e)}
146
+
147
+ def list_devices(self):
148
+ """List connected ADB devices."""
149
+ adb = shutil.which("adb")
150
+ if not adb:
151
+ return []
152
+ try:
153
+ r = subprocess.run([adb, "devices"], capture_output=True, text=True, timeout=5)
154
+ lines = r.stdout.strip().split("\n")[1:]
155
+ devices = []
156
+ for l in lines:
157
+ if l.strip() and "device" in l and "offline" not in l:
158
+ devices.append(l.split()[0])
159
+ return devices
160
+ except:
161
+ return []
162
+
163
+ def _find_apk(self):
164
+ if self.project_dir:
165
+ apks = glob.glob(self.project_dir + "/**/*.apk", recursive=True)
166
+ if apks:
167
+ return apks[0]
168
+ standard = os.path.join(self.project_dir, "app/build/outputs/apk/debug/app-debug.apk")
169
+ if os.path.exists(standard):
170
+ return standard
171
+ return None
172
+
173
+ def _ensure_gradle(self):
174
+ """Check if gradle is available."""
175
+ if shutil.which("gradle"):
176
+ return True
177
+ # Try to install via apt
178
+ try:
179
+ subprocess.run(["apt-get", "install", "-y", "gradle"],
180
+ capture_output=True, timeout=60)
181
+ return shutil.which("gradle") is not None
182
+ except:
183
+ return False
184
+
185
+ def generate_project(self, app_name="FSIApp", package="com.fsi.app", activity_name="MainActivity"):
186
+ """Generate a complete Android project."""
187
+ self.app_name = app_name
188
+ self.package = package
189
+ self.project_dir = f"/tmp/fsi_felon/android_projects/{app_name.lower().replace(' ', '_')}"
190
+
191
+ print(f"\n 🔨 GENERATING: {app_name}")
192
+ print(f" 📦 Package: {package}")
193
+
194
+ if os.path.exists(self.project_dir):
195
+ shutil.rmtree(self.project_dir)
196
+
197
+ # Create structure
198
+ pkg_path = package.replace('.', '/')
199
+ dirs = [
200
+ f"app/src/main/java/{pkg_path}",
201
+ "app/src/main/res/layout",
202
+ "app/src/main/res/values",
203
+ "app/src/main/res/drawable",
204
+ "gradle/wrapper"
205
+ ]
206
+ for d in dirs:
207
+ os.makedirs(os.path.join(self.project_dir, d), exist_ok=True)
208
+
209
+ # Write all files
210
+ self._write_build_gradle()
211
+ self._write_manifest(activity_name)
212
+ self._write_main_activity(activity_name, pkg_path)
213
+ self._write_layout()
214
+ self._write_resources()
215
+ self._write_gradle_props()
216
+
217
+ print(f" ✅ Generated: {len(dirs)} directories, 10+ files")
218
+ return self.project_dir
219
+
220
+ def _write_build_gradle(self):
221
+ content = f"""plugins {{
222
+ id 'com.android.application'
223
+ }}
224
+
225
+ android {{
226
+ namespace '{self.package}'
227
+ compileSdk 34
228
+
229
+ defaultConfig {{
230
+ applicationId "{self.package}"
231
+ minSdk 24
232
+ targetSdk 34
233
+ versionCode 1
234
+ versionName "1.0"
235
+ }}
236
+
237
+ buildTypes {{
238
+ release {{
239
+ minifyEnabled false
240
+ }}
241
+ }}
242
+ }}
243
+
244
+ dependencies {{
245
+ implementation 'androidx.appcompat:appcompat:1.6.1'
246
+ implementation 'com.google.android.material:material:1.11.0'
247
+ implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
248
+ }}
249
+ """
250
+ with open(os.path.join(self.project_dir, "app", "build.gradle"), 'w') as f:
251
+ f.write(content)
252
+
253
+ def _write_manifest(self, activity_name):
254
+ content = f"""<?xml version="1.0" encoding="utf-8"?>
255
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
256
+ package="{self.package}">
257
+ <application
258
+ android:label="@string/app_name"
259
+ android:theme="@style/Theme.FSI">
260
+ <activity android:name=".{activity_name}" android:exported="true">
261
+ <intent-filter>
262
+ <action android:name="android.intent.action.MAIN" />
263
+ <category android:name="android.intent.category.LAUNCHER" />
264
+ </intent-filter>
265
+ </activity>
266
+ </application>
267
+ </manifest>
268
+ """
269
+ with open(os.path.join(self.project_dir, "app", "src", "main", "AndroidManifest.xml"), 'w') as f:
270
+ f.write(content)
271
+
272
+ def _write_main_activity(self, activity_name, pkg_path):
273
+ content = f"""package {self.package};
274
+
275
+ import android.os.Bundle;
276
+ import androidx.appcompat.app.AppCompatActivity;
277
+
278
+ public class {activity_name} extends AppCompatActivity {{
279
+ @Override
280
+ protected void onCreate(Bundle savedInstanceState) {{
281
+ super.onCreate(savedInstanceState);
282
+ setContentView(R.layout.activity_main);
283
+ }}
284
+ }}
285
+ """
286
+ path = os.path.join(self.project_dir, f"app/src/main/java/{pkg_path}/{activity_name}.java")
287
+ with open(path, 'w') as f:
288
+ f.write(content)
289
+
290
+ def _write_layout(self):
291
+ content = """<?xml version="1.0" encoding="utf-8"?>
292
+ <androidx.constraintlayout.widget.ConstraintLayout
293
+ xmlns:android="http://schemas.android.com/apk/res/android"
294
+ android:layout_width="match_parent"
295
+ android:layout_height="match_parent"
296
+ android:background="#0A0A0F">
297
+
298
+ <TextView
299
+ android:id="@+id/title"
300
+ android:layout_width="wrap_content"
301
+ android:layout_height="wrap_content"
302
+ android:text="FSI_FELON"
303
+ android:textColor="#00FF41"
304
+ android:textSize="32sp"
305
+ android:textStyle="bold"
306
+ android:fontFamily="monospace" />
307
+
308
+ </androidx.constraintlayout.widget.ConstraintLayout>
309
+ """
310
+ with open(os.path.join(self.project_dir, "app/src/main/res/layout/activity_main.xml"), 'w') as f:
311
+ f.write(content)
312
+
313
+ def _write_resources(self):
314
+ with open(os.path.join(self.project_dir, "app/src/main/res/values/strings.xml"), 'w') as f:
315
+ f.write(f"""<?xml version="1.0" encoding="utf-8"?>
316
+ <resources>
317
+ <string name="app_name">{self.app_name}</string>
318
+ </resources>
319
+ """)
320
+
321
+ with open(os.path.join(self.project_dir, "app/src/main/res/values/themes.xml"), 'w') as f:
322
+ f.write("""<?xml version="1.0" encoding="utf-8"?>
323
+ <resources>
324
+ <style name="Theme.FSI" parent="Theme.Material3.Dark.NoActionBar">
325
+ <item name="android:statusBarColor">#0A0A0F</item>
326
+ </style>
327
+ </resources>
328
+ """)
329
+
330
+ def _write_gradle_props(self):
331
+ with open(os.path.join(self.project_dir, "gradle.properties"), 'w') as f:
332
+ f.write("""org.gradle.jvmargs=-Xmx2048m
333
+ android.useAndroidX=true
334
+ """)
335
+
336
+ with open(os.path.join(self.project_dir, "settings.gradle"), 'w') as f:
337
+ f.write(f"""pluginManagement {{
338
+ repositories {{ google(); mavenCentral(); gradlePluginPortal() }}
339
+ }}
340
+ dependencyResolutionManagement {{
341
+ repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
342
+ repositories {{ google(); mavenCentral() }}
343
+ }}
344
+ rootProject.name = "{self.app_name}"
345
+ include ':app'
346
+ """)
347
+
348
+ def compile(self):
349
+ """Compile using system gradle or gradlew."""
350
+ if not self.project_dir:
351
+ print(" ❌ No project. Generate first.")
352
+ return None
353
+
354
+ if not self._ensure_gradle():
355
+ print(" ❌ Gradle not found. Install: apt-get install gradle")
356
+ return None
357
+
358
+ print(f"\n 🔨 COMPILING...")
359
+ print(f" 📁 {self.project_dir}")
360
+
361
+ # Try gradle directly
362
+ gradle = shutil.which("gradle")
363
+ cmd = [gradle, "assembleDebug", "--no-daemon", "-p", self.project_dir]
364
+
365
+ print(f" ⚙️ {' '.join(cmd)}")
366
+ print(f" ⏳ First build takes 3-5 minutes (downloads dependencies)...")
367
+
368
+ try:
369
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=300)
370
+
371
+ if result.returncode == 0:
372
+ apk = os.path.join(self.project_dir, "app/build/outputs/apk/debug/app-debug.apk")
373
+ if os.path.exists(apk):
374
+ size = os.path.getsize(apk)
375
+ print(f"\n ✅ APK BUILT SUCCESSFULLY")
376
+ print(f" 📦 {apk}")
377
+ print(f" 📊 {size:,} bytes ({size/1024/1024:.2f} MB)")
378
+ print(f"\n 🚀 INSTALL: adb install {apk}")
379
+ print(f" 📤 SHARE: cp {apk} ~/Downloads/")
380
+ return apk
381
+ else:
382
+ # Find APK
383
+ apks = glob.glob(self.project_dir + "/**/*.apk", recursive=True)
384
+ if apks:
385
+ print(f" ✅ APK found: {apks[0]}")
386
+ return apks[0]
387
+ print(" ⚠️ Build succeeded but APK not found")
388
+ return None
389
+ else:
390
+ print(f" ��� BUILD FAILED")
391
+ err = result.stderr[-500:] if result.stderr else result.stdout[-500:]
392
+ print(f" {err}")
393
+ return None
394
+
395
+ except subprocess.TimeoutExpired:
396
+ print(" ⏱️ Timeout. Try again with more memory.")
397
+ return None
398
+ except Exception as e:
399
+ print(f" ❌ Error: {e}")
400
+ return None
401
+
402
+ def main():
403
+ compiler = AndroidCompiler()
404
+ project = compiler.generate_project("FSI_Demo", "com.fsi.demo", "MainActivity")
405
+ apk = compiler.compile()
406
+ if apk:
407
+ print(f"\n 🎉 DONE. APK ready at: {apk}")
408
+
409
+ if __name__ == '__main__':
410
+ main()