Skip to main content

Overview

Android Code Studio features an advanced AI Agent system that provides intelligent code generation, real-time code completion, and context-aware assistance. The AI Agent integrates with multiple AI providers including OpenAI, Anthropic Claude, Google Gemini, DeepSeek, Grok, and local LLMs.

Architecture

The AI system is built around a flexible agent architecture:

AI Agent Interface

Common interface for all AI providers

Agent Manager

Manages agent lifecycle and switching

Code Completion Service

Real-time code suggestions

Project Awareness

Context-aware with project structure

AI Agent Interface

The core AIAgent interface provides a unified API for all AI providers:
interface AIAgent {
  val providerId: String
  val providerName: String
  
  // Initialization
  fun initialize(apiKey: String, context: Context)
  fun reinitializeWithNewModel(apiKey: String, context: Context)
  fun setContext(context: Context)
  fun setProjectData(projectTreeResult: ProjectTreeResult)
  fun clearConversation()
  
  // Code generation
  suspend fun generateCode(
    prompt: String,
    context: String?,
    language: String,
    projectStructure: String?
  ): Result<String>
  
  // Modification tracking
  fun recordModification(
    filePath: String, 
    oldContent: String?, 
    newContent: String, 
    success: Boolean
  )
  fun undoLastModification(): Boolean
  fun getModificationHistory(): List<ModificationAttempt>
  
  // Retry logic
  fun resetAttemptCount()
  fun incrementAttemptCount()
  fun getCurrentAttemptCount(): Int
  fun canRetry(): Boolean
  
  // File operations
  fun writeFile(filePath: String, content: String): FileWriteResult
  fun isInitialized(): Boolean
}

data class ModificationAttempt(
  val timestamp: Long,
  val filePath: String,
  val previousContent: String?,
  val newContent: String,
  val attemptNumber: Int = 0,
  val success: Boolean = false
)

Supported AI Providers

class OpenAI : AIAgent {
  override val providerId = "openai"
  override val providerName = "OpenAI GPT"
  
  // Models: GPT-4, GPT-4 Turbo, GPT-3.5 Turbo
  private var client: OpenAIClient? = null
  
  override fun initialize(apiKey: String, context: Context) {
    client = OpenAIClient(apiKey)
  }
  
  override suspend fun generateCode(
    prompt: String,
    context: String?,
    language: String,
    projectStructure: String?
  ): Result<String> {
    return try {
      val response = client?.chat(
        messages = buildMessages(prompt, context, projectStructure),
        model = "gpt-4-turbo"
      )
      Result.success(response?.content ?: "")
    } catch (e: Exception) {
      Result.failure(e)
    }
  }
}

AI Agent Manager

Manage multiple AI agents and switch between them:
class AIAgentManager(private val context: Context) {
  private val agents = mutableMapOf<String, AIAgent>()
  private var currentAgent: AIAgent? = null
  
  fun registerAgent(agent: AIAgent) {
    agents[agent.providerId] = agent
  }
  
  fun setCurrentAgent(providerId: String, apiKey: String) {
    val agent = agents[providerId] ?: return
    agent.initialize(apiKey, context)
    currentAgent = agent
  }
  
  fun getCurrentAgent(): AIAgent? = currentAgent
  
  suspend fun generateCode(
    prompt: String,
    language: String = "kotlin"
  ): Result<String> {
    val agent = currentAgent ?: return Result.failure(
      IllegalStateException("No agent initialized")
    )
    
    return agent.generateCode(
      prompt = prompt,
      context = getProjectContext(),
      language = language,
      projectStructure = getProjectStructure()
    )
  }
}

Agent Registry

object AIAgentRegistry {
  private val agents = mutableMapOf<String, () -> AIAgent>()
  
  fun register(providerId: String, factory: () -> AIAgent) {
    agents[providerId] = factory
  }
  
  fun create(providerId: String): AIAgent? {
    return agents[providerId]?.invoke()
  }
  
  fun getAvailableProviders(): List<String> {
    return agents.keys.toList()
  }
}

// Register agents
fun registerAgents() {
  AIAgentRegistry.register("openai") { OpenAI() }
  AIAgentRegistry.register("anthropic") { Anthropic() }
  AIAgentRegistry.register("google") { Gemini() }
  AIAgentRegistry.register("deepseek") { DeepSeek() }
  AIAgentRegistry.register("grok") { Grok() }
  AIAgentRegistry.register("local") { LocalLLM() }
}
The agent registry pattern allows easy addition of new AI providers without modifying existing code.

Code Completion Service

Real-time AI-powered code suggestions:
class AICodeCompletionService(
  private val context: Context,
  private val aiAgent: AIAgent
) {
  private val completionCache = mutableMapOf<String, CompletionResult>()
  
  data class CompletionResult(
    val text: String,
    val isMultiLine: Boolean,
    val lineCount: Int
  )
  
  suspend fun getSuggestion(
    currentCode: String,
    cursorPosition: Int,
    language: String = "kotlin"
  ): CompletionResult? = withContext(Dispatchers.IO) {
    if (!aiAgent.isInitialized()) {
      return@withContext null
    }
    
    try {
      val beforeCursor = currentCode.substring(
        0, cursorPosition.coerceAtMost(currentCode.length)
      )
      val afterCursor = currentCode.substring(
        cursorPosition.coerceAtMost(currentCode.length)
      )
      val lastLines = beforeCursor.lines().takeLast(20).joinToString("\n")
      
      // Check cache
      val cacheKey = lastLines.takeLast(150)
      if (completionCache.containsKey(cacheKey)) {
        return@withContext completionCache[cacheKey]
      }
      
      // Build prompt
      val prompt = buildCompletionPrompt(
        lastLines, 
        afterCursor.lines().take(5).joinToString("\n"), 
        language
      )
      
      // Get AI suggestion
      val result = aiAgent.generateCode(
        prompt = prompt,
        context = null,
        language = language,
        projectStructure = null
      )
      
      result.fold(
        onSuccess = { response ->
          val completionResult = extractSuggestion(response)
          if (completionResult != null && completionResult.text.isNotBlank()) {
            completionCache[cacheKey] = completionResult
            return@withContext completionResult
          }
          null
        },
        onFailure = { e ->
          handleException(e)
          null
        }
      )
    } catch (e: Exception) {
      handleException(e)
      null
    }
  }
  
  private fun buildCompletionPrompt(
    before: String,
    after: String,
    language: String
  ): String {
    return """
      Complete the following $language code. Provide only the completion, 
      no explanations.
      
      Code before cursor:
      ```$language
      $before
Code after cursor:
$after
Completion: """.trimIndent() } }

### Completion UI

```kotlin
class CodeCompletionUI(private val editor: CodeEditor) {
  private var suggestionView: SuggestionView? = null
  
  fun showSuggestion(completion: CompletionResult) {
    suggestionView?.dismiss()
    
    suggestionView = SuggestionView(editor.context).apply {
      setSuggestion(completion.text)
      setOnAcceptListener {
        insertCompletion(completion.text)
      }
      show(editor, editor.cursor.line, editor.cursor.column)
    }
  }
  
  fun hideSuggestion() {
    suggestionView?.dismiss()
    suggestionView = null
  }
  
  private fun insertCompletion(text: String) {
    editor.insertText(text, editor.cursor.line, editor.cursor.column)
    hideSuggestion()
  }
}
The AI code completion provides:
  • Single-line completions: Quick suggestions for current line
  • Multi-line completions: Complete functions, classes, blocks
  • Context-aware: Uses surrounding code for better suggestions
  • Language-specific: Tailored for Java, Kotlin, XML, etc.
  • Caching: Fast repeat suggestions

Project Awareness

The AI agent has full awareness of your project structure:
data class ProjectTreeResult(
  val structure: String,
  val files: List<FileInfo>,
  val dependencies: List<Dependency>
)

data class FileInfo(
  val path: String,
  val type: FileType,
  val size: Long,
  val content: String?
)

// Set project context
val projectTree = buildProjectTree(workspace)
aiAgent.setProjectData(projectTree)

// Generate code with project awareness
val result = aiAgent.generateCode(
  prompt = "Create a new Repository class for User data",
  context = getCurrentFileContent(),
  language = "kotlin",
  projectStructure = projectTree.structure
)

Modification Tracking

Track all AI-generated modifications with undo capability:
// Record modification
aiAgent.recordModification(
  filePath = file.path,
  oldContent = originalContent,
  newContent = modifiedContent,
  success = true
)

// View history
val history = aiAgent.getModificationHistory()
history.forEach { attempt ->
  println("File: ${attempt.filePath}")
  println("Attempt: ${attempt.attemptNumber}")
  println("Success: ${attempt.success}")
  println("Time: ${Date(attempt.timestamp)}")
}

// Undo last modification
if (aiAgent.undoLastModification()) {
  println("Modification reverted")
}
AI-generated code should always be reviewed before committing. The AI may introduce bugs or not fully understand your requirements.

Error Handling

Handle AI service errors gracefully:
class AIExceptions {
  class RateLimitException(message: String) : Exception(message)
  class QuotaExceededException(message: String) : Exception(message)
  class InsufficientBalanceException(message: String) : Exception(message)
  class InvalidApiKeyException(message: String) : Exception(message)
}

private fun handleException(e: Throwable) {
  when (e) {
    is RateLimitException -> {
      showError("Rate limit exceeded. Please wait and try again.")
    }
    is QuotaExceededException -> {
      showError("API quota exceeded. Please check your plan.")
    }
    is InsufficientBalanceException -> {
      showError("Insufficient balance. Please add credits.")
    }
    is InvalidApiKeyException -> {
      showError("Invalid API key. Please check your settings.")
    }
    else -> {
      showError("AI request failed: ${e.message}")
    }
  }
}

Retry Logic

Automatic retry with backoff for failed requests:
suspend fun generateWithRetry(
  prompt: String,
  maxAttempts: Int = 3
): Result<String> {
  aiAgent.resetAttemptCount()
  
  repeat(maxAttempts) { attempt ->
    if (!aiAgent.canRetry()) {
      return Result.failure(
        Exception("Maximum retry attempts reached")
      )
    }
    
    aiAgent.incrementAttemptCount()
    
    val result = aiAgent.generateCode(
      prompt = prompt,
      context = null,
      language = "kotlin",
      projectStructure = null
    )
    
    if (result.isSuccess) {
      return result
    }
    
    // Exponential backoff
    delay(1000L * (attempt + 1))
  }
  
  return Result.failure(Exception("Failed after $maxAttempts attempts"))
}

Configuration Dialogs

Provider Switch Dialog

class ProviderSwitchDialog : DialogFragment() {
  private val providers = listOf(
    "openai" to "OpenAI GPT",
    "anthropic" to "Anthropic Claude",
    "google" to "Google Gemini",
    "deepseek" to "DeepSeek",
    "grok" to "Grok",
    "local" to "Local LLM"
  )
  
  override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return AlertDialog.Builder(requireContext())
      .setTitle("Select AI Provider")
      .setItems(providers.map { it.second }.toTypedArray()) { _, which ->
        switchProvider(providers[which].first)
      }
      .create()
  }
}

Permission Dialog

class AIPermissionDialog : DialogFragment() {
  override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
    return AlertDialog.Builder(requireContext())
      .setTitle("AI Features")
      .setMessage(
        "Enable AI-powered code completion and generation? "
        + "This will send code snippets to the selected AI provider."
      )
      .setPositiveButton("Enable") { _, _ ->
        enableAI()
      }
      .setNegativeButton("Cancel", null)
      .create()
  }
}

Best Practices

Review Generated Code

Always review AI suggestions before accepting

Provide Context

Give detailed prompts with context for better results

Use Project Awareness

Enable project context for more accurate suggestions

Monitor API Usage

Keep track of API calls and costs

Usage Examples

val prompt = """
  Create a suspend function that fetches user data from a REST API.
  - Function name: fetchUserData
  - Parameter: userId (Int)
  - Return type: Result<User>
  - Use Retrofit for HTTP calls
  - Handle errors properly
""".trimIndent()

val result = aiAgent.generateCode(
  prompt = prompt,
  context = getCurrentFileContent(),
  language = "kotlin",
  projectStructure = null
)

result.onSuccess { code ->
  editor.insertText(code)
}
For best results, provide specific requirements and constraints in your prompts. The more context you give, the better the AI can help.

Build docs developers (and LLMs) love