-- lua/user/android.lua -- Android development support for Neovim: -- * exports the Android SDK env (so LSP, Gradle and :terminal jobs see it) -- * :Android* commands + a* keymaps for build / run / test / logcat / emulator -- -- Java (nvim-jdtls) and Kotlin (JetBrains kotlin-lsp) are configured elsewhere; this -- module is purely the build/run/device tooling around them. local sdk = vim.fn.expand("~/Android/Sdk") -- 1. Make the Android SDK visible to LSP, Gradle and terminal jobs launched from nvim. vim.env.ANDROID_HOME = vim.env.ANDROID_HOME or sdk vim.env.ANDROID_SDK_ROOT = vim.env.ANDROID_SDK_ROOT or sdk do local path = vim.env.PATH or "" local extra = { sdk .. "/platform-tools", -- adb sdk .. "/emulator", -- emulator sdk .. "/cmdline-tools/latest/bin",-- sdkmanager/avdmanager vim.fn.expand("~/.local/bin"), -- the `android` CLI } for _, p in ipairs(extra) do if vim.fn.isdirectory(p) == 1 and not string.find(path, p, 1, true) then path = p .. ":" .. path end end vim.env.PATH = path end -- 2. Locate the Gradle project root (nearest ancestor with a gradlew wrapper). local function gradle_root() local start = vim.fn.expand("%:p:h") if start == "" then start = vim.fn.getcwd() end local found = vim.fs.find("gradlew", { upward = true, path = start }) if found and found[1] then return vim.fn.fnamemodify(found[1], ":h") end return vim.fn.getcwd() end -- 3. Run a shell command in a bottom terminal split (live, scrollable output). local function run_in_term(shell_cmd) vim.cmd("botright 15split") vim.cmd("terminal " .. shell_cmd) vim.cmd("startinsert") end local function run_gradle(args) run_in_term(string.format("cd %s && ./gradlew %s", vim.fn.shellescape(gradle_root()), args)) end local function run_cmd(c) run_in_term(string.format("cd %s && %s", vim.fn.shellescape(gradle_root()), c)) end -- 4. User commands. local cmd = vim.api.nvim_create_user_command cmd("AndroidBuild", function() run_gradle("assembleDebug") end, { desc = "Gradle assembleDebug" }) cmd("AndroidClean", function() run_gradle("clean") end, { desc = "Gradle clean" }) cmd("AndroidInstall", function() run_gradle("installDebug") end, { desc = "Gradle installDebug" }) cmd("AndroidTest", function() run_gradle("testDebugUnitTest") end,{ desc = "Gradle unit tests" }) cmd("AndroidRun", function() run_cmd("android run") end, { desc = "Deploy app (android run)" }) cmd("AndroidDevices", function() run_cmd("adb devices -l") end, { desc = "List adb devices" }) cmd("AndroidLogcat", function() run_cmd("adb logcat -c; adb logcat") end, { desc = "adb logcat (cleared)" }) cmd("AndroidEmulators", function() run_cmd("android emulator list") end, { desc = "List AVDs" }) cmd("AndroidEmulator", function(o) if o.args ~= "" then run_cmd("android emulator start " .. vim.fn.shellescape(o.args)) else -- Default: start the first available AVD. run_cmd([[android emulator start "$("]] .. sdk .. [[/emulator/emulator" -list-avds | head -1)"]]) end end, { nargs = "?", desc = "Start an emulator (optional AVD name)" }) cmd("AndroidStopEmulator", function() run_cmd("android emulator stop") end, { desc = "Stop emulator" }) -- 5. Keymaps (A* = Android). -- NOTE: uppercase A so it does NOT clash with a (LSP code action). local map = vim.keymap.set map("n", "Ab", "AndroidBuild", { silent = true, desc = "Android: build (assembleDebug)" }) map("n", "Ar", "AndroidRun", { silent = true, desc = "Android: run/deploy" }) map("n", "Ai", "AndroidInstall", { silent = true, desc = "Android: install" }) map("n", "At", "AndroidTest", { silent = true, desc = "Android: unit tests" }) map("n", "Al", "AndroidLogcat", { silent = true, desc = "Android: logcat" }) map("n", "Ad", "AndroidDevices", { silent = true, desc = "Android: devices" }) map("n", "Ae", "AndroidEmulator", { silent = true, desc = "Android: start emulator" }) map("n", "Ac", "AndroidClean", { silent = true, desc = "Android: clean" })