Hey can y'all debug this for me 🥹

I’m 13 and this is my first time vibe coding in rust, idk if the AI give me a normal code so I could launch it as .apk file, my idea is to make a launcher since Quickstep is kinda boring

use android_activity::input::{InputEvent, KeyCode, MotionAction, KeyState};
use android_activity::{AndroidApp, WindowEvent, PollEvent};
use softbuffer::GraphicsContext;
use log::info;
use std::time::{Duration, Instant};
use std::thread;
use jni::objects::{JObject, JValue};
use jni::JNIEnv;

// ---------- App info ----------
struct AppInfo {
    name: String,
    package: String,
    class: String,
}

// Get installed launcher apps via JNI
fn get_installed_apps(env: &mut JNIEnv, context: JObject) -> Vec<AppInfo> {
    let mut apps = Vec::new();
    let pm = match env.call_method(context, "getPackageManager", "()Landroid/content/pm/PackageManager;", &[]) {
        Ok(v) => v.l().unwrap(),
        Err(_) => return apps,
    };
    let intent = env.new_object("android/content/Intent", "(Ljava/lang/String;)V", &[
        env.new_string("android.intent.action.MAIN").unwrap().into()
    ]).unwrap();
    let category = env.new_string("android.intent.category.LAUNCHER").unwrap();
    let _ = env.call_method(&intent, "addCategory", "(Ljava/lang/String;)Landroid/content/Intent;", &[category.into()]);
    let resolve_list = env.call_method(&pm, "queryIntentActivities", "(Landroid/content/Intent;I)Ljava/util/List;", &[
        intent.into(),
        JValue::Int(0)
    ]).unwrap().l().unwrap();
    let size = env.call_method(&resolve_list, "size", "()I", &[]).unwrap().i().unwrap();
    for i in 0..size {
        let item = env.call_method(&resolve_list, "get", "(I)Ljava/lang/Object;", &[JValue::Int(i)]).unwrap().l().unwrap();
        let act_info = env.get_field(&item, "activityInfo", "Landroid/content/pm/ActivityInfo;").unwrap().l().unwrap();
        let pkg = env.get_field(&act_info, "packageName", "Ljava/lang/String;").unwrap().l().unwrap();
        let cls = env.get_field(&act_info, "name", "Ljava/lang/String;").unwrap().l().unwrap();
        let app_info = env.get_field(&act_info, "applicationInfo", "Landroid/content/pm/ApplicationInfo;").unwrap().l().unwrap();
        let label = env.call_method(&pm, "getApplicationLabel", "(Landroid/content/pm/ApplicationInfo;)Ljava/lang/CharSequence;", &[
            app_info.into()
        ]).unwrap().l().unwrap();
        let name: String = env.get_string(&label.into()).unwrap().into();
        let package: String = env.get_string(&pkg.into()).unwrap().into();
        let class: String = env.get_string(&cls.into()).unwrap().into();
        apps.push(AppInfo { name, package, class });
    }
    apps
}

// Launch app
fn launch_app(env: &mut JNIEnv, context: JObject, package: &str, class: &str) {
    let intent = env.new_object("android/content/Intent", "()V", &[]).unwrap();
    let comp = env.new_object("android/content/ComponentName", "(Ljava/lang/String;Ljava/lang/String;)V", &[
        env.new_string(package).unwrap().into(),
        env.new_string(class).unwrap().into(),
    ]).unwrap();
    let _ = env.call_method(&intent, "setComponent", "(Landroid/content/ComponentName;)Landroid/content/Intent;", &[comp.into()]);
    let _ = env.call_method(&intent, "addFlags", "(I)Landroid/content/Intent;", &[JValue::Int(0x10000000)]);
    let _ = env.call_method(context, "startActivity", "(Landroid/content/Intent;)V", &[intent.into()]);
}

// Open recent apps (system overview)
fn open_recent_apps(env: &mut JNIEnv, context: JObject) {
    let intent = env.new_object("android/content/Intent", "()V", &[]).unwrap();
    let _ = env.call_method(&intent, "setAction", "(Ljava/lang/String;)Landroid/content/Intent;", &[
        env.new_string("android.intent.action.RECENT_APPS").unwrap().into()
    ]);
    let _ = env.call_method(context, "startActivity", "(Landroid/content/Intent;)V", &[intent.into()]);
}

// Show toast
fn show_toast(env: &mut JNIEnv, context: JObject, msg: &str) {
    let toast_class = env.find_class("android/widget/Toast").unwrap();
    let jmsg = env.new_string(msg).unwrap();
    let toast = env.call_static_method(toast_class, "makeText", "(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast;", &[
        context.into(),
        jmsg.into(),
        JValue::Int(0)
    ]).unwrap().l().unwrap();
    let _ = env.call_method(&toast, "show", "()V", &[]);
}

// Free memory in MB
fn get_free_memory(env: &mut JNIEnv, context: JObject) -> i64 {
    let am = env.call_method(context, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;", &[
        env.new_string("activity").unwrap().into()
    ]).unwrap().l().unwrap();
    let mem_info = env.new_object("android/app/ActivityManager$MemoryInfo", "()V", &[]).unwrap();
    let _ = env.call_method(&am, "getMemoryInfo", "(Landroid/app/ActivityManager$MemoryInfo;)V", &[mem_info.into()]);
    env.get_field(&mem_info, "availMem", "J").unwrap().j().unwrap() / (1024 * 1024)
}

// ---------- Performance manager ----------
struct PerformanceManager {
    last_interaction: Instant,
    frame_delay: Duration,
}

impl PerformanceManager {
    fn new() -> Self {
        Self { last_interaction: Instant::now(), frame_delay: Duration::from_millis(33) }
    }
    fn record_interaction(&mut self) { self.last_interaction = Instant::now(); }
    fn update_frame_delay(&mut self) -> Duration {
        let idle = self.last_interaction.elapsed();
        self.frame_delay = if idle > Duration::from_secs(5) {
            Duration::from_millis(166)
        } else if idle > Duration::from_secs(2) {
            Duration::from_millis(66)
        } else {
            Duration::from_millis(33)
        };
        self.frame_delay
    }
}

// ---------- Drawing ----------
fn draw_circle(buffer: &mut [u32], width: usize, height: usize, cx: usize, cy: usize, radius: usize, color: u32) {
    let x0 = cx.saturating_sub(radius);
    let x1 = (cx + radius).min(width - 1);
    let y0 = cy.saturating_sub(radius);
    let y1 = (cy + radius).min(height - 1);
    for y in y0..=y1 {
        let dy = (y as isize - cy as isize).abs();
        for x in x0..=x1 {
            let dx = (x as isize - cx as isize).abs();
            if dx * dx + dy * dy <= (radius * radius) as isize {
                buffer[y * width + x] = color;
            }
        }
    }
}

fn draw_rect(buffer: &mut [u32], width: usize, height: usize, x: usize, y: usize, w: usize, h: usize, color: u32) {
    let x_end = (x + w).min(width);
    let y_end = (y + h).min(height);
    for yy in y..y_end {
        let row_start = yy * width;
        for xx in x..x_end {
            buffer[row_start + xx] = color;
        }
    }
}

// ---------- Main ----------
fn android_main(app: AndroidApp) {
    simple_logger::init_with_level(log::Level::Info).unwrap();
    info!("EggLauncher v1.0 started");

    let mut window = app.create_surface_window().unwrap();
    let mut width = window.width();
    let mut height = window.height();
    let mut context = GraphicsContext::new(window.native_window()).unwrap();
    let mut events = app.poll_events(Some(Duration::from_millis(0)));

    let vm = app.vm().unwrap();
    let mut env = vm.attach_current_thread().unwrap();
    let context_obj = env.new_local_ref(app.context()).unwrap();
    let apps = get_installed_apps(&mut env, context_obj.as_obj());
    drop(env);

    let mut perf = PerformanceManager::new();
    let mut selected = 0;

    let cols = 3;
    let rows = (apps.len() + cols - 1).max(1);
    let cell_w = width / cols;
    let cell_h = height / rows;

    let btn_w = 100;
    let btn_h = 40;
    let btn_x = if width > btn_w + 20 { width - btn_w - 20 } else { 10 };
    let ram_btn = (btn_x, 20, btn_w, btn_h);
    let recent_btn = (btn_x, 20 + btn_h + 10, btn_w, btn_h);

    'main: loop {
        let frame_delay = perf.update_frame_delay();

        while let Some(event) = events.next_event() {
            match event {
                PollEvent::Window(win_event) => match win_event {
                    WindowEvent::Resized => {
                        width = window.width();
                        height = window.height();
                        context.resize(width as u32, height as u32);
                    }
                    WindowEvent::Redraw => {
                        let mut buffer = context.buffer_mut().unwrap();

                        // Black background
                        for pixel in buffer.iter_mut() { *pixel = 0x000000; }

                        // Abstract colourful circles (static)
                        let bg_circles = [(100,200,40,0xFF4D4D), (400,600,60,0x4CAF50), (700,300,50,0xFFD700)];
                        for (cx, cy, rad, col) in bg_circles {
                            draw_circle(&mut buffer, width, height, cx, cy, rad, col);
                        }

                        // App grid
                        for (i, _) in apps.iter().enumerate() {
                            let col = i % cols;
                            let row = i / cols;
                            let cx = (col * cell_w) + cell_w / 2;
                            let cy = (row * cell_h) + cell_h / 2;
                            let rad = cell_w.min(cell_h) / 4;
                            let color = if i == selected { 0xFFD700 } else { 0x666666 };
                            draw_circle(&mut buffer, width, height, cx, cy, rad, color);
                        }

                        // Buttons
                        draw_rect(&mut buffer, width, height, ram_btn.0, ram_btn.1, ram_btn.2, ram_btn.3, 0x333333);
                        draw_rect(&mut buffer, width, height, recent_btn.0, recent_btn.1, recent_btn.2, recent_btn.3, 0x444444);

                        buffer.present().unwrap();
                    }
                    _ => {}
                },
                PollEvent::Input(input_event) => match input_event {
                    InputEvent::Key(key) => {
                        perf.record_interaction();
                        if key.state() == KeyState::Pressed && key.key_code() == KeyCode::BACK {
                            break 'main;
                        }
                    }
                    InputEvent::Motion(motion) => {
                        perf.record_interaction();
                        if let Some(pointer) = motion.pointers().next() {
                            let x = pointer.x() as usize;
                            let y = pointer.y() as usize;

                            // RAM button
                            if x >= ram_btn.0 && x < ram_btn.0 + ram_btn.2 && y >= ram_btn.1 && y < ram_btn.1 + ram_btn.3 {
                                if motion.action() == MotionAction::ButtonRelease {
                                    let mut env = vm.attach_current_thread().unwrap();
                                    let mem = get_free_memory(&mut env, context_obj.as_obj());
                                    show_toast(&mut env, context_obj.as_obj(), &format!("{} MB free", mem));
                                }
                                continue;
                            }
                            // Recent Apps button
                            if x >= recent_btn.0 && x < recent_btn.0 + recent_btn.2 && y >= recent_btn.1 && y < recent_btn.1 + recent_btn.3 {
                                if motion.action() == MotionAction::ButtonRelease {
                                    let mut env = vm.attach_current_thread().unwrap();
                                    open_recent_apps(&mut env, context_obj.as_obj());
                                }
                                continue;
                            }

                            // App selection
                            for (i, _) in apps.iter().enumerate() {
                                let col = i % cols;
                                let row = i / cols;
                                let left = col * cell_w;
                                let right = left + cell_w;
                                let top = row * cell_h;
                                let bottom = top + cell_h;
                                if x >= left && x < right && y >= top && y < bottom {
                                    selected = i;
                                    if motion.action() == MotionAction::ButtonRelease {
                                        let mut env = vm.attach_current_thread().unwrap();
                                        launch_app(&mut env, context_obj.as_obj(), &apps[selected].package, &apps[selected].class);
                                    }
                                    break;
                                }
                            }
                        }
                    }
                    _ => {}
                },
                _ => {}
            }
        }

        thread::sleep(frame_delay);
        window.refresh();
    }
}

Have you been able to compile it or run it? What errors did you see?

The short answer is usually "no, not debug it for you" but we are very willing to help where you get stuck.

The idea is to help you on your journey, without teleporting you to the destination. (teach a person to fish, as the saying goes, as it's less effort for the helper and much more helpful in the long run for the help-ee)

Hey, welcome. There's a lot that people could feedback on the code (but you probably won't get much when asking older people to debug something vibe coded - that's just how it is right now).

But I'd say it's most valuable to give a few tips to help you learn anyway.

I'm still not the best at prompting. But I'd suggest trying a couple of the following in separate sessions with a new context:

  • Please review this code critically and suggest where I can make changes to make it more idiomatic
  • (if you have suitable skills.md) Please suggest which lints I should enable, enable them and then explain the main issues which clippy finds to me. I'm new to coding and new to rust.
  • (if you have suitable skills.md) Please suggest a set of unit and integration tests for this app, create them, run them, fix any bugs in the code or tests and then explain them to me.
  • Please let me know how to compile and test this code. I'm 13 and new to vibe coding rust.

I'd also like to say - good on you for trying, rust is a wonderful language, coding is great fun and the people here are some of the friendliest coders I've ever met.

If you feel like you really need help moving forward with your idea - which is a great one, by the way - maybe it's too big a first step?`

You could also try asking your favourite agent to help you design and then guide you through making a project to learn and build the skills step-by-step to creating a quickstep replacement. With a good chat first you should be able to get it to design something that will give you a tangible and fun first app quickly, which you can understand and helps learn for the next step - over time it will help you build from this first app to a great quickstep replacement and you'll learn a lot about coding and rust as you go. Make sure you get it to create suitable agents.md, skills.md etc as the output from that first chat.

(You could always just dump this reply as your first prompt and see where it takes you!)