samuvelp/droiddoctor

AI-powered Android project health analyzer built with LangGraph

โ˜… 5Forks 0PythonGitHub โ†—Compare

README

DroidDoctor ๐Ÿค–๐Ÿฉบ

AI-powered Android project health analyzer built with LangGraph

Python 3.10+ License: MIT Built with LangGraph

Point it at any Android project. Get a full health report in 30 seconds.

pip install droiddoctor
droiddoctor ~/AndroidStudioProjects/MyApp

Why This Exists

Android projects rot silently. AGP falls behind, Compose BOM drifts, exported components leak, deprecated APIs linger, permissions creep.

You know you should audit. But it's scattered across lint, dependency checks, manual manifest review, and that one senior dev who "just knows."

DroidDoctor does the full audit in one command.


What It Checks

Check What It Does
Gradle Dependencies Parses build.gradle(.kts) + libs.versions.toml, checks every dep against Google Maven and Maven Central for latest versions
Manifest Security Exported components without permissions, dangerous permissions, cleartext traffic, hardcoded debuggable flag, missing backup rules
Deprecated APIs Regex scan for AsyncTask, startActivityForResult, ViewModelProviders.of, android.support.*, kapt, findViewById, and 7 more patterns
Compose Adoption Measures XML layout vs @Composable file split across all modules with adoption percentage
AI Analysis LLM synthesizes all findings into a prioritized action plan: ๐Ÿšจ FIX NOW / โš ๏ธ THIS SPRINT / ๐Ÿ“‹ NEXT QUARTER

Architecture

A 7-node LangGraph agent with conditional routing and shared state:

                    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚ analyze_gradle_dependenciesโ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
         โ”‚         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ”‚
         โ”‚                                               โ”‚
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ–ผ
โ”‚ scan_project  โ”‚โ”€โ–บโ”‚     audit_manifest        โ”‚โ”€โ”€โ–บ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”    โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  _structure   โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜     โ”‚ collect  โ”‚โ”€โ”€โ”€โ–บโ”‚ llm_analyze โ”‚โ”€โ”€โ”€โ–บโ”‚  generate   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”     โ”‚ _results โ”‚    โ”‚             โ”‚    โ”‚  _report    โ”‚
         โ”‚         โ”‚  detect_deprecated_apis   โ”‚โ”€โ”€โ–บ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
         โ”‚         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜          โ–ฒ
         โ”‚         โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”          โ”‚
         โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–บโ”‚  check_compose_adoption   โ”‚โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                   โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Design philosophy: The LLM never scans files. Static analysis handles scanning โ€” it's deterministic and fast. The LLM only does what LLMs are good at: correlating findings, prioritizing by risk, and generating actionable explanations.


Quick Start

1. Install

pip install droiddoctor

Or from source:

git clone https://github.com/samuvelp/droiddoctor.git
cd droiddoctor
pip install -e .

2. Set your API key

export ANTHROPIC_API_KEY=sk-ant-xxxxx

Or create a .env file:

ANTHROPIC_API_KEY=sk-ant-xxxxx

3. Run

droiddoctor /path/to/your/android/project

Multi-Provider Support

DroidDoctor supports 5 LLM providers. Use whatever key you already have:

# Claude (default)
export ANTHROPIC_API_KEY=sk-ant-...
droiddoctor /path/to/project

# OpenAI
pip install langchain-openai
export OPENAI_API_KEY=sk-...
droiddoctor /path/to/project --provider openai

# Google Gemini
pip install langchain-google-genai
export GOOGLE_API_KEY=...
droiddoctor /path/to/project --provider gemini

# Groq (free tier available)
pip install langchain-groq
export GROQ_API_KEY=gsk_...
droiddoctor /path/to/project --provider groq

# Ollama (fully local โ€” no API key needed)
pip install langchain-ollama
droiddoctor /path/to/project --provider ollama --model llama3.1

Override the default model for any provider:

droiddoctor /path/to/project --provider openai --model gpt-4.1
droiddoctor /path/to/project --provider gemini --model gemini-2.5-flash

Usage

# Full scan with AI analysis
droiddoctor /path/to/android/project

# Save markdown report to file
droiddoctor /path/to/project --save

# Custom output path
droiddoctor /path/to/project --save -o health-report.md

# Offline mode โ€” skip LLM, just raw data (no API key needed)
droiddoctor /path/to/project --no-llm

# Use a specific LLM provider
droiddoctor /path/to/project --provider openai

# Show version
droiddoctor --version

Example Output

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚  DroidDoctor v0.1.0                        โ”‚
โ”‚  AI-powered Android project health analyzerโ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Health Score โ€” MyApp โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                    62/100 ๐ŸŸ                       โ”‚
โ”‚               3 module(s) scanned                โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

                  Outdated Dependencies
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Dependency               โ”‚ Current โ”‚ Latest โ”‚ Severity โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ com.android.tools.build  โ”‚ 8.2.0   โ”‚ 8.7.0  โ”‚ CRITICAL โ”‚
โ”‚ org.jetbrains.kotlin     โ”‚ 1.9.22  โ”‚ 2.1.0  โ”‚ MAJOR    โ”‚
โ”‚ androidx.core:core-ktx   โ”‚ 1.10.0  โ”‚ 1.15.0 โ”‚ MINOR    โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

                    Manifest Issues
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ Severity โ”‚ Issue                                    โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ CRITICAL โ”‚ Cleartext (HTTP) traffic enabled         โ”‚
โ”‚ CRITICAL โ”‚ android:debuggable="true" hardcoded      โ”‚
โ”‚ WARNING  โ”‚ Dangerous permission: CAMERA             โ”‚
โ”‚ WARNING  โ”‚ Exported activity without permission     โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

                   Deprecated APIs
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚ API                    โ”‚ Replacement                โ”‚
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ผโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค
โ”‚ AsyncTask              โ”‚ Kotlin Coroutines          โ”‚
โ”‚ startActivityForResult โ”‚ Activity Result API        โ”‚
โ”‚ ViewModelProviders.of  โ”‚ by viewModels() delegate   โ”‚
โ”‚ kapt                   โ”‚ Migrate to KSP             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

โ•ญโ”€โ”€โ”€ Compose Adoption โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚ XML Layouts: 42  |  Composable Files: 12  |  22.2% โ”‚
โ”‚ โ–ˆโ–ˆโ–ˆโ–ˆโ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘โ–‘ 22.2%                         โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

โ•ญโ”€โ”€โ”€ AI-Powered Analysis โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
โ”‚                                                      โ”‚
โ”‚  ๐Ÿšจ FIX NOW                                         โ”‚
โ”‚  โ€ข Remove android:debuggable="true" โ€” this makes    โ”‚
โ”‚    your release APK debuggable in production         โ”‚
โ”‚  โ€ข Disable cleartext traffic โ€” user data is being   โ”‚
โ”‚    sent over unencrypted HTTP                        โ”‚
โ”‚                                                      โ”‚
โ”‚  โš ๏ธ FIX THIS SPRINT                                 โ”‚
โ”‚  โ€ข Update AGP 8.2.0 โ†’ 8.7.0 โ€” you're missing       โ”‚
โ”‚    build performance improvements and R8 fixes       โ”‚
โ”‚  โ€ข Replace AsyncTask with viewModelScope.launch โ€”   โ”‚
โ”‚    deprecated since API 30                           โ”‚
โ”‚                                                      โ”‚
โ”‚  ๐Ÿ“‹ PLAN FOR NEXT QUARTER                            โ”‚
โ”‚  โ€ข Compose is at 22% โ€” create a migration tracker   โ”‚
โ”‚    and convert one screen per sprint                 โ”‚
โ”‚                                                      โ”‚
โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ

What It Handles

  • Single or multi-module โ€” reads settings.gradle(.kts) to discover all modules
  • Groovy or Kotlin DSL โ€” parses both build.gradle and build.gradle.kts
  • Version catalogs โ€” full gradle/libs.versions.toml support with version ref resolution
  • Network failures โ€” if Maven version checks fail, skips gracefully and continues
  • No Compose projects โ€” works fine on 100% XML projects, reports without judgment
  • Large projects โ€” automatically skips .gradle/, build/, .idea/, node_modules/

Project Structure

droiddoctor/
โ”œโ”€โ”€ droiddoctor/
โ”‚   โ”œโ”€โ”€ cli.py               # CLI entry point โ€” rich terminal UI
โ”‚   โ”œโ”€โ”€ graph.py              # LangGraph definition โ€” 7 nodes, conditional routing
โ”‚   โ”œโ”€โ”€ state.py              # AgentState TypedDict + data models
โ”‚   โ”œโ”€โ”€ nodes/
โ”‚   โ”‚   โ”œโ”€โ”€ scanner.py        # Node 1: Discover project layout and modules
โ”‚   โ”‚   โ”œโ”€โ”€ gradle.py         # Node 2: Parse deps, check versions via HTTP
โ”‚   โ”‚   โ”œโ”€โ”€ manifest.py       # Node 3: Security audit of AndroidManifest.xml
โ”‚   โ”‚   โ”œโ”€โ”€ deprecated.py     # Node 4: Regex scan for 13 deprecated patterns
โ”‚   โ”‚   โ”œโ”€โ”€ compose.py        # Node 5: XML vs Composable adoption metrics
โ”‚   โ”‚   โ”œโ”€โ”€ analyzer.py       # Node 6: LLM synthesis + health score
โ”‚   โ”‚   โ””โ”€โ”€ reporter.py       # Node 7: Final markdown report generation
โ”‚   โ””โ”€โ”€ parsers/
โ”‚       โ”œโ”€โ”€ gradle_parser.py   # build.gradle(.kts) + libs.versions.toml parser
โ”‚       โ””โ”€โ”€ manifest_parser.py # AndroidManifest.xml parser (xml.etree)
โ”œโ”€โ”€ tests/
โ”‚   โ”œโ”€โ”€ fixtures/              # Sample Android project files for testing
โ”‚   โ””โ”€โ”€ test_nodes.py          # 22 tests covering all parsers and nodes
โ”œโ”€โ”€ pyproject.toml
โ”œโ”€โ”€ requirements.txt
โ”œโ”€โ”€ .env.example
โ””โ”€โ”€ LICENSE

How to Extend

Each node is a pure function: (AgentState) -> dict. To add a new check:

  1. Create a new node in droiddoctor/nodes/
  2. Read what you need from AgentState, return only the keys you update
  3. Wire it into the graph in graph.py with add_node() + add_edge()
  4. Add a table for it in reporter.py

Ideas for new nodes:

  • ProGuard/R8 rules audit โ€” missing keep rules
  • Room migration checker โ€” detect missing migration paths
  • API level compatibility โ€” flag APIs unavailable on minSdk
  • Test coverage estimator โ€” test files vs source files ratio
  • CI config checker โ€” missing lint/test steps in GitHub Actions

Contributing

Contributions welcome! Good first issues:

  • Groovy build.gradle test fixtures
  • buildSrc and convention plugin support
  • JSON output format (--format json)
  • True parallel fan-out for analysis nodes
  • GitHub Actions integration
  • HTML report output
  • Diff mode โ€” compare two scans

License

MIT โ€” see LICENSE

Contributors

samuvelp

Issues