// agent

My Android APK Starter Studio

by prfs · Oct 06, 2026 Public

Choose how to run this agent

⚡ Local runs on your GPU. For usable speed it needs a WebGPU-capable browser — Chrome or Edge on a machine with a graphics card, or an Apple Silicon Mac. Without a supported GPU, pick OpenAI or Anthropic above instead. Check your machine
Try it now

Requires an API key and an AgentOp account.

7 downloads
0 forks
0.0 rating

Description

Based on the Android APK Starter Studio template.

What this agent can do

My Android APK Starter Studio is built from the Android APK Starter Studio template. Runs fully on your own device: llama.cpp compiled to WebAssembly, GPU-accelerated through WebGPU, with no API key and no server. After the one-time model download it works offline. Can run on OpenAI models with your own API key, encrypted in your browser. Can run on Anthropic Claude models with your own API key, encrypted in your browser.

Source Code

agent.py
import base64
import html
import io
import re
import zipfile


def _build_zip(name: str, package: str, idea: str, features: str, style: str) -> str:
    if not re.fullmatch(r'[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)+', package):
        raise ValueError('Invalid Android package ID.')
    if not name.strip() or not idea.strip():
        raise ValueError('App name and idea are required.')
    name = name.strip()[:60]
    idea = idea.strip()[:2200]
    lines = [line.strip()[:120] for line in features.splitlines() if line.strip()][:8]
    if not lines:
        lines = ['Customize your first screen', 'Build the features in your app plan']
    palette = {
        'indigo': ('#4355C9', '#F4F6FF'),
        'forest': ('#206B53', '#F0FAF5'),
        'sunset': ('#BD593F', '#FFF5EF'),
    }
    accent, background = palette.get(style, palette['indigo'])
    esc = lambda text: html.escape(text, quote=True).replace("'", ''')
    strings = ('<?xml version="1.0" encoding="utf-8"?>\n<resources>\n'
               '    <string name="app_name">' + esc(name) + '</string>\n'
               '    <string name="description">' + esc(idea[:450]) + '</string>\n'
               '    <string name="features">' + esc(' • '.join(lines)) + '</string>\n'
               '</resources>\n')
    activity = '''package __PACKAGE__

import android.app.Activity
import android.graphics.Color
import android.os.Bundle
import android.view.Gravity
import android.widget.Button
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import android.widget.Toast

class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val pad = (24 * resources.displayMetrics.density).toInt()
        val panel = LinearLayout(this).apply {
            orientation = LinearLayout.VERTICAL
            setPadding(pad, pad, pad, pad)
            setBackgroundColor(Color.parseColor("__BACKGROUND__"))
        }
        fun text(value: String, size: Float, color: String): TextView = TextView(this).apply {
            this.text = value
            textSize = size
            setTextColor(Color.parseColor(color))
            setPadding(0, 0, 0, pad)
        }
        panel.addView(text(getString(R.string.app_name), 32f, "__ACCENT__"))
        panel.addView(text(getString(R.string.description), 18f, "#213047"))
        panel.addView(text("STARTER FEATURES", 13f, "__ACCENT__"))
        panel.addView(text(getString(R.string.features), 18f, "#213047"))
        val action = Button(this).apply {
            this.text = "Get started"
            setOnClickListener {
                Toast.makeText(this@MainActivity, "Connect your first feature here", Toast.LENGTH_LONG).show()
            }
        }
        panel.addView(action)
        panel.gravity = Gravity.CENTER_VERTICAL
        setContentView(ScrollView(this).apply { addView(panel) })
    }
}
'''.replace('__PACKAGE__', package).replace('__BACKGROUND__', background).replace('__ACCENT__', accent)
    app_gradle = '''plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
}

android {
    namespace = "__PACKAGE__"
    compileSdk = 35
    defaultConfig {
        applicationId = "__PACKAGE__"
        minSdk = 24
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
    kotlinOptions { jvmTarget = "17" }
}
'''.replace('__PACKAGE__', package)
    manifest = '''<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:label="@string/app_name" android:theme="@android:style/Theme.Material.Light.NoActionBar" android:allowBackup="true">
        <activity android:name=".MainActivity" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>
'''
    readme = ('# ' + name.replace('\n', ' ') + '\n\n'
              'This is a compilable Kotlin Android starter UI, not a finished implementation of the requested features. The Get started button is a placeholder.\n\n'
              '## Build a debug APK\n'
              'Install Android SDK Platform 35, JDK 17, and Gradle 8.9. From this project folder, run:\n\n'
              '    gradle wrapper --gradle-version 8.9\n'
              '    ./gradlew assembleDebug\n\n'
              'On Windows, use gradlew.bat assembleDebug. Alternatively, open the folder in Android Studio, configure Gradle 8.9, sync, and choose Build > Build APK(s).\n'
              'Output: app/build/outputs/apk/debug/app-debug.apk\n\n'
              'The generated ZIP intentionally does not include a Gradle wrapper JAR; the first command creates the wrapper.\n'
              'The project needs an Android SDK and access to Gradle plugin dependencies for its first build.\n\n'
              '## Next steps\nImplement the requested screens, persistence, permissions, and any integrations before releasing. For distribution, configure signing and create a release build.\n')
    files = {
        'settings.gradle.kts': 'pluginManagement { repositories { google(); mavenCentral(); gradlePluginPortal() } }\ndependencyResolutionManagement { repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS); repositories { google(); mavenCentral() } }\nrootProject.name = "AndroidStarter"\ninclude(":app")\n',
        'build.gradle.kts': 'plugins {\n    id("com.android.application") version "8.6.1" apply false\n    id("org.jetbrains.kotlin.android") version "2.0.21" apply false\n}\n',
        'gradle.properties': 'org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8\nandroid.useAndroidX=true\n',
        'app/build.gradle.kts': app_gradle,
        'app/src/main/AndroidManifest.xml': manifest,
        'app/src/main/java/' + package.replace('.', '/') + '/MainActivity.kt': activity,
        'app/src/main/res/values/strings.xml': strings,
        'README.md': readme,
    }
    memory = io.BytesIO()
    with zipfile.ZipFile(memory, 'w', compression=zipfile.ZIP_DEFLATED) as archive:
        for path, content in files.items():
            archive.writestr(path, content)
    return base64.b64encode(memory.getvalue()).decode('ascii')


async def process_user_query(query: str) -> str:
    """Draft an Android implementation plan for the supplied app brief.

    Args:
        query: App name, package, idea, features, and palette.

    Returns:
        An actionable Android implementation plan.
    """
    prompt = ('Plan this Android app for a beginner. The ZIP download is a basic Kotlin Activity UI scaffold with app title, description, feature labels, and a placeholder button. It does NOT implement the described features. Do not say an APK has been compiled. Reply in plain text, concise, with headings: MVP, Screens, Data and permissions, Build next. Avoid unsupported promises.\n\nApp brief:\n' + query[:3500])
    return str(await agentop_llm.generate(prompt, globals().get('TEMPLATE_SYSTEM_PROMPT', '')))