Commit bb98deffa3c8

Vincent Demeester <vincent@sbr.pm>
2026-01-06 14:38:44
refactor(skills): Consolidate Android workflows from 10 to 3
- Reduce duplication and maintenance burden (40% fewer lines) - Improve navigation with clear Setup→Build→Debug progression - Preserve all essential Android development guidance Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 73a9ff2
dots/.config/claude/skills/Android/workflows/Build.md
@@ -1,6 +1,25 @@
 # Build Workflow
 
-Build Android applications (APK/AAB) using Gradle build system.
+Build Android applications and Go libraries for Android, manage dependencies with Gradle, and publish to Google Play Store.
+
+## Quick Start
+
+```bash
+# Navigate to project
+cd /path/to/android-project
+
+# Build debug APK
+./gradlew assembleDebug
+
+# Build release APK
+./gradlew assembleRelease
+
+# Build release AAB (for Play Store)
+./gradlew bundleRelease
+
+# Install debug on device
+./gradlew installDebug
+```
 
 ## Build Types
 
@@ -16,53 +35,636 @@ Build Android applications (APK/AAB) using Gradle build system.
 - Cannot be directly installed
 - Use for: Play Store releases
 
-## Quick Start
+## Gradle Fundamentals
 
-```bash
-# Navigate to project
-cd /path/to/android-project
+### Project Structure
 
-# Build debug APK
-./gradlew assembleDebug
-
-# Build release APK
-./gradlew assembleRelease
-
-# Build release AAB
-./gradlew bundleRelease
-
-# Install debug on device
-./gradlew installDebug
+```
+myproject/
+├── gradle/
+│   ├── libs.versions.toml      # Version catalog (recommended)
+│   └── wrapper/
+│       ├── gradle-wrapper.jar
+│       └── gradle-wrapper.properties
+├── app/
+│   └── build.gradle.kts        # App module build file
+├── build.gradle.kts            # Root build file
+├── settings.gradle.kts         # Project settings
+├── gradle.properties           # Global Gradle properties
+└── local.properties            # Local config (not in git)
 ```
 
-## Build Variants
+### Version Catalogs (Modern Approach)
 
-Android projects typically have:
+**gradle/libs.versions.toml:**
+```toml
+[versions]
+agp = "8.3.0"
+kotlin = "1.9.22"
+compileSdk = "34"
+minSdk = "24"
+targetSdk = "34"
 
-| Variant | Use Case | Signed | Minified |
-|---------|----------|--------|----------|
-| Debug | Development | Auto | No |
-| Release | Production | Manual | Yes |
+androidx-core = "1.12.0"
+material = "1.11.0"
 
-### Debug Build
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
+material = { group = "com.google.android.material", name = "material", version.ref = "material" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+```
+
+**Root build.gradle.kts:**
+```kotlin
+plugins {
+    alias(libs.plugins.android.application) apply false
+    alias(libs.plugins.kotlin.android) apply false
+}
+```
+
+**App build.gradle.kts:**
+```kotlin
+plugins {
+    alias(libs.plugins.android.application)
+    alias(libs.plugins.kotlin.android)
+}
+
+android {
+    namespace = "com.example.myapp"
+    compileSdk = 34
+
+    defaultConfig {
+        applicationId = "com.example.myapp"
+        minSdk = 24
+        targetSdk = 34
+        versionCode = 1
+        versionName = "1.0"
+    }
+
+    buildTypes {
+        release {
+            isMinifyEnabled = true
+            proguardFiles(
+                getDefaultProguardFile("proguard-android-optimize.txt"),
+                "proguard-rules.pro"
+            )
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_17
+        targetCompatibility = JavaVersion.VERSION_17
+    }
+
+    kotlinOptions {
+        jvmTarget = "17"
+    }
+}
+
+dependencies {
+    implementation(libs.androidx.core.ktx)
+    implementation(libs.material)
+
+    // Gomobile AAR
+    implementation(files("libs/mylib.aar"))
+}
+```
+
+### Managing Dependencies
+
+**Add dependency to version catalog:**
+```toml
+[versions]
+retrofit = "2.9.0"
+
+[libraries]
+retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
+```
+
+**Use in build.gradle.kts:**
+```kotlin
+dependencies {
+    implementation(libs.retrofit)
+}
+```
+
+**Dependency scopes:**
+```kotlin
+dependencies {
+    implementation("...")           // Recommended: Not exposed to consumers
+    api("...")                      // Exposed to consumers
+    compileOnly("...")              // Compile only (not in APK)
+    runtimeOnly("...")              // Runtime only
+
+    testImplementation("...")       // Unit tests
+    androidTestImplementation("...")// Instrumentation tests
+
+    debugImplementation("...")      // Debug build only
+}
+```
+
+**Local AAR/JAR files:**
+```kotlin
+dependencies {
+    // Single AAR
+    implementation(files("libs/mylib.aar"))
+
+    // All AARs in libs directory
+    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
+}
+```
+
+### gradle.properties
+
+```properties
+# Performance
+org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m
+org.gradle.daemon=true
+org.gradle.parallel=true
+org.gradle.caching=true
+
+# AndroidX
+android.useAndroidX=true
+android.enableJetifier=true
+
+# Build
+android.nonTransitiveRClass=true
+```
+
+### Build Variants
+
+**Build types:**
+```kotlin
+android {
+    buildTypes {
+        debug {
+            applicationIdSuffix = ".debug"
+            isDebuggable = true
+        }
+
+        release {
+            isMinifyEnabled = true
+            isShrinkResources = true
+            proguardFiles(...)
+        }
+
+        create("staging") {
+            initWith(getByName("debug"))
+            applicationIdSuffix = ".staging"
+        }
+    }
+}
+```
+
+**Product flavors:**
+```kotlin
+android {
+    flavorDimensions += "version"
+
+    productFlavors {
+        create("free") {
+            dimension = "version"
+            applicationIdSuffix = ".free"
+        }
+
+        create("paid") {
+            dimension = "version"
+            applicationIdSuffix = ".paid"
+        }
+    }
+}
+```
+
+Build specific variant:
+```bash
+./gradlew assembleFreeDebug
+./gradlew assemblePaidRelease
+```
+
+### Common Gradle Tasks
 
 ```bash
-# Build
+# List all tasks
+./gradlew tasks
+
+# Clean build outputs
+./gradlew clean
+
+# Build all variants
+./gradlew build
+
+# Check dependencies
+./gradlew dependencies
+
+# Show dependency tree
+./gradlew app:dependencies --configuration debugRuntimeClasspath
+
+# Refresh dependencies
+./gradlew --refresh-dependencies
+```
+
+## Building Go Libraries (gomobile bind)
+
+Use gomobile bind to create Android AAR packages from Go code. This allows you to use Go libraries in Android apps written in Java/Kotlin.
+
+### When to Use
+
+- You have existing Go code (networking, crypto, business logic)
+- You want to integrate Go into an existing Android app
+- You need Go's concurrency or standard library in Android
+- You want to share code between backend and mobile
+
+### Prerequisites
+
+```bash
+# Install gomobile
+go install golang.org/x/mobile/cmd/gomobile@latest
+
+# Initialize (downloads Android toolchain)
+gomobile init
+
+# Verify
+gomobile version
+```
+
+### Go Code Requirements
+
+**What works in gomobile:**
+- ✅ Exported functions with basic types
+- ✅ Exported structs with exported fields
+- ✅ Methods on exported structs
+- ✅ Interfaces (for callbacks)
+- ✅ Error return values (become exceptions)
+- ✅ Slices of basic types (`[]byte`, `[]int`, `[]string`)
+
+**What doesn't work:**
+- ❌ Maps (use structs instead)
+- ❌ Channels (use callbacks via interfaces)
+- ❌ Generics
+- ❌ Unexported types in function signatures
+- ❌ Variadic functions
+
+**Example Go library:**
+```go
+// File: golib/network.go
+package network
+
+import (
+    "fmt"
+    "net/http"
+)
+
+// FetchURL fetches content from a URL
+func FetchURL(url string) (string, error) {
+    resp, err := http.Get(url)
+    if err != nil {
+        return "", err
+    }
+    defer resp.Body.Close()
+
+    // Implementation...
+    return "content", nil
+}
+
+// Config represents network configuration
+type Config struct {
+    Timeout int
+    BaseURL string
+}
+
+// NewConfig creates a new Config
+func NewConfig(timeout int, baseURL string) *Config {
+    return &Config{
+        Timeout: timeout,
+        BaseURL: baseURL,
+    }
+}
+
+// Callback interface for async operations
+type Callback interface {
+    OnSuccess(data string)
+    OnError(err error)
+}
+
+// FetchAsync fetches URL asynchronously
+func FetchAsync(url string, callback Callback) {
+    go func() {
+        result, err := FetchURL(url)
+        if err != nil {
+            callback.OnError(err)
+            return
+        }
+        callback.OnSuccess(result)
+    }()
+}
+```
+
+### Build AAR
+
+**Basic build (all architectures):**
+```bash
+gomobile bind -target=android -o mylib.aar ./golib
+```
+
+**Build specific architectures:**
+```bash
+# ARM64 only (most modern devices)
+gomobile bind -target=android/arm64 -o mylib.aar ./golib
+
+# ARM64 and ARM32
+gomobile bind -target=android/arm64,android/arm -o mylib.aar ./golib
+
+# All common architectures
+gomobile bind -target=android/arm64,android/arm,android/amd64,android/386 -o mylib.aar ./golib
+```
+
+**Architecture options:**
+- `android/arm64`: 64-bit ARM (arm64-v8a) - modern devices
+- `android/arm`: 32-bit ARM (armeabi-v7a) - older devices
+- `android/amd64`: 64-bit x86 (x86_64) - emulators
+- `android/386`: 32-bit x86 (x86) - old emulators
+
+### Integrate AAR into Android Project
+
+**Copy AAR:**
+```bash
+cp mylib.aar /path/to/android-project/app/libs/
+```
+
+**Add dependency in app/build.gradle.kts:**
+```kotlin
+dependencies {
+    implementation(files("libs/mylib.aar"))
+}
+```
+
+**Sync Gradle:**
+```bash
+./gradlew sync
+```
+
+### Use in Android Code
+
+**Kotlin example:**
+```kotlin
+import golib.Network
+
+class MainActivity : AppCompatActivity() {
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        // Call Go function
+        try {
+            val result = Network.fetchURL("https://example.com")
+            println("Result: $result")
+        } catch (e: Exception) {
+            println("Error: ${e.message}")
+        }
+
+        // Use Go struct
+        val config = Network.newConfig(30, "https://api.example.com")
+        println("Timeout: ${config.timeout}")
+
+        // Async with callback
+        Network.fetchAsync("https://example.com", object : Network.Callback {
+            override fun onSuccess(data: String) {
+                println("Success: $data")
+            }
+
+            override fun onError(err: Exception) {
+                println("Error: ${err.message}")
+            }
+        })
+    }
+}
+```
+
+### Type Conversion Reference
+
+| Go Type | Java/Kotlin Type | Notes |
+|---------|------------------|-------|
+| `bool` | `boolean` / `Boolean` | |
+| `int`, `int32` | `int` / `Int` | 32-bit signed |
+| `int64` | `long` / `Long` | 64-bit signed |
+| `float64` | `double` / `Double` | |
+| `string` | `String` | UTF-8 encoded |
+| `[]byte` | `byte[]` / `ByteArray` | |
+| `error` | `Exception` | Go errors become Java exceptions |
+| `struct` | `class` | Exported fields become getters/setters |
+| `interface` | `interface` | Must have exported methods only |
+
+### Optimization Tips
+
+**Minimize boundary crossings:**
+
+Bad (many crossings):
+```go
+func ProcessItem(item string) string { ... }
+// Called 1000 times from Android = 1000 Go calls
+```
+
+Good (single crossing):
+```go
+func ProcessItems(items []string) []string { ... }
+// Called once from Android = 1 Go call
+```
+
+**Use appropriate architectures:**
+
+For development (faster builds):
+```bash
+gomobile bind -target=android/arm64 -o mylib.aar ./golib
+```
+
+For release (wider compatibility):
+```bash
+gomobile bind -target=android/arm64,android/arm -o mylib.aar ./golib
+```
+
+### Build Automation Script
+
+```bash
+#!/usr/bin/env bash
+set -euo pipefail
+
+echo "Building AAR..."
+
+TARGETS=${ANDROID_TARGETS:-"android/arm64,android/arm"}
+OUTPUT=${AAR_OUTPUT:-"mylib.aar"}
+PACKAGE=${GO_PACKAGE:-"./golib"}
+
+gomobile bind \
+    -target="$TARGETS" \
+    -o "$OUTPUT" \
+    "$PACKAGE"
+
+echo "AAR built: $OUTPUT"
+
+# Copy to Android project if path is set
+if [ -n "${ANDROID_PROJECT:-}" ]; then
+    cp "$OUTPUT" "$ANDROID_PROJECT/app/libs/"
+    echo "Copied to Android project"
+fi
+```
+
+Usage:
+```bash
+# Build for ARM64 only
+ANDROID_TARGETS=android/arm64 ./build-aar.sh
+
+# Build and copy to Android project
+ANDROID_PROJECT=/path/to/android-app ./build-aar.sh
+```
+
+## Building Standalone Go Apps (gomobile build)
+
+Build standalone Android applications entirely in Go using `gomobile build`. Creates a complete APK with Go code as the main application logic.
+
+### When to Use vs gomobile bind
+
+| Feature | gomobile build | gomobile bind |
+|---------|----------------|---------------|
+| UI | OpenGL only | Full Android UI |
+| Android APIs | Limited | Full access |
+| Development speed | Fast | Medium |
+| Production apps | Demos/utilities | Production ready |
+| Learning curve | Low | Medium |
+
+**Use gomobile build when:**
+- You want to write the entire app in Go
+- You're building a simple utility or demo
+- You want to prototype quickly
+- You're comfortable with limited UI
+
+**Use gomobile bind when:**
+- You need complex native Android UI
+- You need full Android framework APIs
+- You're building a production app with rich UI
+
+### Basic App Example
+
+```go
+package main
+
+import (
+    "log"
+    "golang.org/x/mobile/app"
+    "golang.org/x/mobile/event/lifecycle"
+    "golang.org/x/mobile/event/paint"
+    "golang.org/x/mobile/gl"
+)
+
+func main() {
+    app.Main(func(a app.App) {
+        var glctx gl.Context
+
+        for e := range a.Events() {
+            switch e := a.Filter(e).(type) {
+            case lifecycle.Event:
+                switch e.Crosses(lifecycle.StageVisible) {
+                case lifecycle.CrossOn:
+                    glctx, _ = e.DrawContext.(gl.Context)
+                    log.Println("App started")
+                case lifecycle.CrossOff:
+                    log.Println("App stopped")
+                    glctx = nil
+                }
+
+            case paint.Event:
+                if glctx == nil {
+                    continue
+                }
+                // Clear screen
+                glctx.ClearColor(0.2, 0.2, 0.3, 1.0)
+                glctx.Clear(gl.COLOR_BUFFER_BIT)
+                a.Publish()
+            }
+        }
+    })
+}
+```
+
+### Build APK
+
+**Basic build:**
+```bash
+gomobile build -target=android .
+```
+
+**Build with custom app ID:**
+```bash
+gomobile build -target=android -appid=com.example.myapp .
+```
+
+**Build for specific architectures:**
+```bash
+# ARM64 only
+gomobile build -target=android/arm64 -appid=com.example.myapp .
+
+# ARM64 and ARM32
+gomobile build -target=android/arm64,android/arm -appid=com.example.myapp .
+```
+
+**Build with custom icon:**
+```bash
+# Place icon at assets/icon.png
+gomobile build -target=android -icon=assets/icon.png .
+```
+
+### Install and Run
+
+```bash
+# Build and install
+gomobile install -target=android -appid=com.example.myapp .
+
+# Or build then install separately
+gomobile build -target=android -appid=com.example.myapp .
+adb install -r myapp.apk
+```
+
+**View logs:**
+```bash
+adb logcat | grep GoLog
+```
+
+### Limitations
+
+**UI Limitations:**
+- ❌ No native Android UI widgets
+- ❌ No Material Design components
+- ❌ Limited text rendering
+- ✅ OpenGL ES 2.0 for custom graphics
+- ✅ Touch event handling
+
+**Platform Limitations:**
+- ❌ No direct access to Android framework APIs
+- ❌ Can't use Java/Kotlin libraries
+- ✅ Network access (HTTP, TCP, UDP)
+- ✅ File I/O
+- ✅ Concurrency with goroutines
+
+## Building Android Apps
+
+### Build Debug
+
+```bash
+# Build debug APK
 ./gradlew assembleDebug
 
 # Output location
 ls app/build/outputs/apk/debug/app-debug.apk
 
-# Install on connected device
+# Install on device
 adb install app/build/outputs/apk/debug/app-debug.apk
 
 # Or use Gradle install task
 ./gradlew installDebug
 ```
 
-### Release Build
-
-Requires signing configuration (see **Publish** workflow).
+### Build Release
 
 ```bash
 # Build signed release APK
@@ -78,151 +680,7 @@ ls app/build/outputs/apk/release/app-release.apk
 ls app/build/outputs/bundle/release/app-release.aab
 ```
 
-## Build Configuration
-
-### Gradle Wrapper
-
-Always use the wrapper (./gradlew) for consistent builds:
-
-```bash
-# Linux/Mac
-./gradlew <task>
-
-# Windows
-gradlew.bat <task>
-
-# Update wrapper
-./gradlew wrapper --gradle-version=8.6
-```
-
-### Common Gradle Tasks
-
-```bash
-# List all tasks
-./gradlew tasks
-
-# Clean build outputs
-./gradlew clean
-
-# Build all variants
-./gradlew build
-
-# Build and run tests
-./gradlew build test
-
-# Check dependencies
-./gradlew dependencies
-
-# Show project structure
-./gradlew projects
-```
-
-## Build Optimization
-
-### Enable Build Cache
-
-`gradle.properties`:
-```properties
-org.gradle.caching=true
-org.gradle.parallel=true
-org.gradle.daemon=true
-org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m
-```
-
-### Optimize Build Time
-
-```kotlin
-// app/build.gradle.kts
-android {
-    // Use only needed ABIs during development
-    splits {
-        abi {
-            isEnable = false
-        }
-    }
-
-    // Disable PNG crunching in debug
-    buildTypes {
-        debug {
-            isCrunchPngs = false
-        }
-    }
-}
-```
-
-### Incremental Builds
-
-Gradle automatically handles incremental builds. Avoid `clean` unless necessary.
-
-```bash
-# Good: Incremental build
-./gradlew assembleDebug
-
-# Bad: Full rebuild (slower)
-./gradlew clean assembleDebug
-```
-
-## Multi-Module Projects
-
-For projects with multiple modules:
-
-```bash
-# Build specific module
-./gradlew :app:assembleDebug
-./gradlew :library:build
-
-# Build all modules
-./gradlew build
-```
-
-## Build with Different ABIs
-
-### Split APKs by ABI
-
-Reduces APK size by creating separate APKs per architecture.
-
-```kotlin
-// app/build.gradle.kts
-android {
-    splits {
-        abi {
-            isEnable = true
-            reset()
-            include("arm64-v8a", "armeabi-v7a", "x86_64", "x86")
-            isUniversalApk = false  // Set true to also create universal APK
-        }
-    }
-}
-```
-
-Build output:
-```
-app/build/outputs/apk/release/
-├── app-arm64-v8a-release.apk
-├── app-armeabi-v7a-release.apk
-├── app-x86_64-release.apk
-└── app-x86-release.apk
-```
-
-### Filter ABIs (Development)
-
-Include only needed ABIs for faster builds:
-
-```kotlin
-android {
-    defaultConfig {
-        ndk {
-            // Only build for emulator during development
-            abiFilters.addAll(listOf("x86_64"))
-
-            // Or for physical device
-            // abiFilters.addAll(listOf("arm64-v8a"))
-        }
-    }
-}
-```
-
-## Build with Gomobile AAR
+### Build with Gomobile AAR
 
 If using gomobile bind:
 
@@ -258,59 +716,274 @@ cd ..
 echo "Done! APK: app/build/outputs/apk/debug/app-debug.apk"
 ```
 
-## Build Flavors
+### Build Optimization
 
-Create different versions of your app:
+**Enable build cache (gradle.properties):**
+```properties
+org.gradle.caching=true
+org.gradle.parallel=true
+org.gradle.daemon=true
+org.gradle.jvmargs=-Xmx4096m
+```
 
+**Optimize build time:**
 ```kotlin
-// app/build.gradle.kts
 android {
-    flavorDimensions += "version"
-
-    productFlavors {
-        create("free") {
-            dimension = "version"
-            applicationIdSuffix = ".free"
-            versionNameSuffix = "-free"
+    // Use only needed ABIs during development
+    splits {
+        abi {
+            isEnable = false
         }
+    }
 
-        create("paid") {
-            dimension = "version"
-            applicationIdSuffix = ".paid"
-            versionNameSuffix = "-paid"
+    // Disable PNG crunching in debug
+    buildTypes {
+        debug {
+            isCrunchPngs = false
         }
     }
 }
 ```
 
-Build specific flavor:
+**Incremental builds:**
 ```bash
-# Free debug
-./gradlew assembleFreeDebug
+# Good: Incremental build
+./gradlew assembleDebug
 
-# Paid release
-./gradlew assemblePaidRelease
-
-# All flavors and variants
-./gradlew assemble
+# Bad: Full rebuild (slower)
+./gradlew clean assembleDebug
 ```
 
-## Continuous Integration
+## Signing and Publishing
 
-### GitHub Actions Example
+### Create Signing Key
 
-`.github/workflows/android.yml`:
+Generate keystore:
+```bash
+keytool -genkey -v \
+    -keystore release.keystore \
+    -alias myapp-key \
+    -keyalg RSA \
+    -keysize 2048 \
+    -validity 10000
+```
+
+**Important:**
+- **NEVER commit keystore to git**
+- **Backup keystore securely**
+- **Remember passwords**
+
+View keystore details:
+```bash
+keytool -list -v -keystore release.keystore
+```
+
+### Configure Signing in Gradle
+
+**Using environment variables (recommended):**
+
+app/build.gradle.kts:
+```kotlin
+android {
+    signingConfigs {
+        create("release") {
+            storeFile = file("../release.keystore")
+            storePassword = System.getenv("KEYSTORE_PASSWORD")
+            keyAlias = "myapp-key"
+            keyPassword = System.getenv("KEY_PASSWORD")
+        }
+    }
+
+    buildTypes {
+        release {
+            signingConfig = signingConfigs.getByName("release")
+            isMinifyEnabled = true
+            isShrinkResources = true
+            proguardFiles(
+                getDefaultProguardFile("proguard-android-optimize.txt"),
+                "proguard-rules.pro"
+            )
+        }
+    }
+}
+```
+
+Build with environment variables:
+```bash
+export KEYSTORE_PASSWORD="your-keystore-password"
+export KEY_PASSWORD="your-key-password"
+./gradlew bundleRelease
+```
+
+### ProGuard / R8
+
+**Enable code shrinking:**
+```kotlin
+android {
+    buildTypes {
+        release {
+            isMinifyEnabled = true
+            isShrinkResources = true
+            proguardFiles(
+                getDefaultProguardFile("proguard-android-optimize.txt"),
+                "proguard-rules.pro"
+            )
+        }
+    }
+}
+```
+
+**proguard-rules.pro:**
+```proguard
+# Keep gomobile generated classes
+-keep class go.** { *; }
+-keep class mylib.** { *; }
+
+# Keep model classes
+-keep class com.example.myapp.models.** { *; }
+
+# Kotlin
+-keep class kotlin.** { *; }
+-keep class kotlin.Metadata { *; }
+```
+
+### Build Release AAB
+
+```bash
+./gradlew bundleRelease
+
+# Output location
+ls app/build/outputs/bundle/release/app-release.aab
+```
+
+### Verify Signature
+
+```bash
+# Check AAB signature
+jarsigner -verify -verbose app-release.aab
+
+# Check APK signature
+apksigner verify --verbose app-release.apk
+```
+
+### Test Release Build
+
+**Generate APKs from AAB (using bundletool):**
+```bash
+# Download bundletool from:
+# https://github.com/google/bundletool/releases
+
+# Generate APKs
+java -jar bundletool-all.jar build-apks \
+    --bundle=app-release.aab \
+    --output=app.apks \
+    --mode=universal
+
+# Extract universal APK
+unzip app.apks universal.apk
+
+# Install
+adb install universal.apk
+```
+
+**Test checklist:**
+- [ ] App installs successfully
+- [ ] App launches without crashes
+- [ ] All features work correctly
+- [ ] ProGuard hasn't broken anything
+- [ ] Gomobile integration works
+- [ ] Network requests succeed
+
+### Versioning
+
+**Version code (monotonically increasing):**
+```kotlin
+android {
+    defaultConfig {
+        versionCode = 1  // Increment for each release
+        versionName = "1.0"
+    }
+}
+```
+
+**Automated versioning from git:**
+```kotlin
+fun getVersionCode(): Int {
+    val process = Runtime.getRuntime().exec("git rev-list --count HEAD")
+    return process.inputStream.bufferedReader().readText().trim().toInt()
+}
+
+fun getVersionName(): String {
+    val process = Runtime.getRuntime().exec("git describe --tags --always")
+    return process.inputStream.bufferedReader().readText().trim()
+}
+
+android {
+    defaultConfig {
+        versionCode = getVersionCode()
+        versionName = getVersionName()
+    }
+}
+```
+
+### Upload to Play Console
+
+**Prerequisites:**
+1. Google Play Developer account ($25 one-time)
+2. App created in Play Console
+3. Store listing completed
+
+**Upload AAB:**
+1. Go to [Play Console](https://play.google.com/console)
+2. Select your app
+3. Production → Releases
+4. Create new release
+5. Upload AAB file
+6. Fill release notes
+7. Review and rollout
+
+**Release tracks:**
+| Track | Purpose | Audience |
+|-------|---------|----------|
+| Internal testing | Quick testing | Up to 100 testers |
+| Closed testing | Alpha/beta | Invited testers |
+| Open testing | Public beta | Anyone can join |
+| Production | Public release | All users |
+
+**Staged rollout (recommended):**
+1. Start with 5-10%
+2. Monitor crashes/issues
+3. Increase to 25%, 50%, 100%
+
+### Store Listing Requirements
+
+**Required assets:**
+- Screenshots: Minimum 2, recommended 4-8
+- Icon: 512x512 PNG
+- Feature graphic: 1024x500 PNG
+
+**Required information:**
+- App name
+- Short description (80 characters)
+- Full description (4000 characters)
+- Category
+- Content rating
+- Privacy policy URL (if app collects data)
+- Contact email
+
+### CI/CD Publishing Example
+
+**.github/workflows/release.yml:**
 ```yaml
-name: Android CI
+name: Release
 
 on:
   push:
-    branches: [ main ]
-  pull_request:
-    branches: [ main ]
+    tags:
+      - 'v*'
 
 jobs:
-  build:
+  release:
     runs-on: ubuntu-latest
     steps:
       - uses: actions/checkout@v4
@@ -321,55 +994,54 @@ jobs:
           distribution: 'temurin'
           java-version: '17'
 
-      - name: Setup Android SDK
-        uses: android-actions/setup-android@v3
-
       - name: Setup Go
         uses: actions/setup-go@v5
         with:
           go-version: '1.22'
 
-      - name: Install gomobile
+      - name: Build gomobile AAR
         run: |
           go install golang.org/x/mobile/cmd/gomobile@latest
           gomobile init
-
-      - name: Build Go library
-        run: |
           cd golib
           gomobile bind -target=android -o mylib.aar .
           cp mylib.aar ../app/libs/
 
-      - name: Build with Gradle
-        run: ./gradlew assembleDebug
+      - name: Build Release AAB
+        env:
+          KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
+          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
+        run: |
+          echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > release.keystore
+          ./gradlew bundleRelease
 
-      - name: Upload APK
-        uses: actions/upload-artifact@v4
+      - name: Upload to Play Store
+        uses: r0adkll/upload-google-play@v1
         with:
-          name: app-debug
-          path: app/build/outputs/apk/debug/app-debug.apk
+          serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
+          packageName: com.example.myapp
+          releaseFiles: app/build/outputs/bundle/release/app-release.aab
+          track: production
 ```
 
 ## Troubleshooting
 
-### Build fails with "SDK not found"
+### Build Issues
 
+**SDK not found:**
 ```bash
 # Create local.properties
 echo "sdk.dir=$ANDROID_HOME" > local.properties
 ```
 
-### OutOfMemoryError during build
-
-Increase Gradle memory in `gradle.properties`:
+**OutOfMemoryError during build:**
 ```properties
+# Increase Gradle memory in gradle.properties
 org.gradle.jvmargs=-Xmx8192m -XX:MaxMetaspaceSize=2048m
 ```
 
-### Duplicate class errors
-
+**Duplicate class errors:**
 ```kotlin
-// Use packagingOptions to exclude duplicates
 android {
     packagingOptions {
         resources {
@@ -379,55 +1051,112 @@ android {
 }
 ```
 
-### AAR not found
+### Gomobile Issues
 
-```kotlin
-// Ensure AAR is in libs directory
+**"gomobile: command not found":**
+```bash
+# Ensure Go bin is in PATH
+export PATH=$PATH:$(go env GOPATH)/bin
+
+# Reinstall gomobile
+go install golang.org/x/mobile/cmd/gomobile@latest
+```
+
+**"NDK not found":**
+```bash
+# Set ANDROID_HOME
+export ANDROID_HOME=$HOME/Android/Sdk
+
+# Install NDK
+sdkmanager "ndk;26.1.10909125"
+
+# Reinitialize gomobile
+gomobile init
+```
+
+**"Cannot use map in function signature":**
+```go
+// Bad - maps don't bind
+func BadFunc(data map[string]string) error { ... }
+
+// Good - use struct instead
+type KeyValue struct {
+    Key   string
+    Value string
+}
+func GoodFunc(data []KeyValue) error { ... }
+```
+
+**AAR is very large:**
+```bash
+# Build only needed architectures
+gomobile bind -target=android/arm64 -o mylib.aar ./golib
+```
+
+### Dependency Issues
+
+**Dependency conflicts:**
+```bash
+# View conflict resolution
+./gradlew app:dependencies
+
+# Force specific version
 dependencies {
-    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
-    // Or
-    implementation(files("libs/mylib.aar"))
+    implementation("com.example:library:1.0") {
+        force = true
+    }
+}
+
+# Exclude transitive dependency
+dependencies {
+    implementation("com.example:library:1.0") {
+        exclude(group = "com.unwanted", module = "module")
+    }
 }
 ```
 
-## Build Output
-
-### APK Structure
-
-```
-app-debug.apk
-├── AndroidManifest.xml
-├── classes.dex           # Compiled Kotlin/Java code
-├── lib/                  # Native libraries
-│   ├── arm64-v8a/
-│   ├── armeabi-v7a/
-│   └── x86_64/
-├── res/                  # Resources
-└── resources.arsc        # Compiled resources
-```
-
-### Inspect APK
-
+**Sync issues:**
 ```bash
-# Extract APK
-unzip app-debug.apk -d extracted
+# Refresh dependencies
+./gradlew --refresh-dependencies
 
-# View manifest
-aapt dump badging app-debug.apk
-
-# View method count
-# Install dex-method-counts first
-dex-method-counts app-debug.apk
+# Clear cache
+./gradlew clean cleanBuildCache
 ```
 
+### Signing Issues
+
+**"App not signed" error:**
+```bash
+# Check signing configuration
+./gradlew :app:signingReport
+
+# Verify keystore exists and credentials are correct
+```
+
+**ProGuard breaks app:**
+```proguard
+# Add keep rules in proguard-rules.pro
+-keep class com.your.package.** { *; }
+```
+
+**Upload rejected by Play Console:**
+- Version code not incremented
+- Signature mismatch
+- Missing required permissions
+- Policy violations
+
 ## Next Steps
 
-- **Install and test** - Use the **Debug** workflow
-- **Sign and publish** - Use the **Publish** workflow
-- **Run tests** - Use the **Test** workflow
+- **Debug and test** - Use the **Debug** workflow
+- **Monitor app performance** - Play Console metrics
+- **Respond to user feedback** - Reviews and ratings
+- **Plan updates** - New features and bug fixes
 
 ## Resources
 
 - [Gradle Build Documentation](https://developer.android.com/studio/build)
-- [Configure Build Variants](https://developer.android.com/studio/build/build-variants)
-- [Shrink, Obfuscate, and Optimize](https://developer.android.com/studio/build/shrink-code)
+- [Gomobile Documentation](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile)
+- [Publish Your App](https://developer.android.com/studio/publish)
+- [Play Console](https://play.google.com/console)
+- [App Signing](https://developer.android.com/studio/publish/app-signing)
dots/.config/claude/skills/Android/workflows/Debug.md
@@ -1,6 +1,6 @@
 # Debug Workflow
 
-Debug Android applications using adb, logcat, and Android debugging tools.
+Debug, test, and profile Android applications using adb, emulator, logcat, and testing frameworks.
 
 ## ADB (Android Debug Bridge)
 
@@ -13,6 +13,12 @@ adb devices
 # Connect to specific device (if multiple)
 adb -s <device-id> <command>
 
+# Target emulator specifically
+adb -e <command>
+
+# Target physical device specifically
+adb -d <command>
+
 # Check device info
 adb shell getprop ro.product.model
 adb shell getprop ro.build.version.release
@@ -57,6 +63,81 @@ adb shell am force-stop com.example.myapp
 adb shell am kill com.example.myapp
 ```
 
+### File Operations
+
+```bash
+# Push file to device
+adb push local-file.txt /sdcard/
+
+# Pull file from device
+adb pull /sdcard/file.txt ./
+
+# List files
+adb shell ls /sdcard/
+
+# View file content
+adb shell cat /sdcard/file.txt
+
+# Remove file
+adb shell rm /sdcard/file.txt
+```
+
+### Screenshots and Screen Recording
+
+```bash
+# Take screenshot
+adb shell screencap /sdcard/screen.png
+adb pull /sdcard/screen.png
+adb shell rm /sdcard/screen.png
+
+# Or in one command
+adb exec-out screencap -p > screen.png
+
+# Record screen (max 3 minutes)
+adb shell screenrecord /sdcard/demo.mp4
+# Press Ctrl+C to stop
+adb pull /sdcard/demo.mp4
+adb shell rm /sdcard/demo.mp4
+```
+
+### System Information
+
+```bash
+# CPU info
+adb shell cat /proc/cpuinfo
+
+# Memory info
+adb shell cat /proc/meminfo
+
+# Battery status
+adb shell dumpsys battery
+
+# Display info
+adb shell dumpsys display
+
+# Running processes
+adb shell ps
+
+# App processes
+adb shell ps | grep com.example.myapp
+```
+
+### Network
+
+```bash
+# Check network connectivity
+adb shell ping -c 4 google.com
+
+# View network interfaces
+adb shell ip addr
+
+# Port forwarding (device port to local port)
+adb forward tcp:8080 tcp:8080
+
+# Reverse port forwarding (local port to device)
+adb reverse tcp:9000 tcp:9000
+```
+
 ## Logcat
 
 ### View Logs
@@ -151,6 +232,575 @@ adb logcat | grep "panic:"
 adb logcat | grep "GoLog"
 ```
 
+## Emulator
+
+### System Images
+
+**Image Types:**
+
+| Type | Google Apps | Play Store | Use Case |
+|------|-------------|------------|----------|
+| `default` | ❌ | ❌ | AOSP, minimal |
+| `google_apis` | ✅ | ❌ | Google Maps, etc. |
+| `google_apis_playstore` | ✅ | ✅ | Full Play Services |
+
+**Install System Images:**
+
+```bash
+# List available system images
+sdkmanager --list | grep system-images
+
+# Android 14 (API 34) - x86_64 (for Intel/AMD)
+sdkmanager "system-images;android-34;google_apis;x86_64"
+
+# Android 14 (API 34) - ARM64 (for Apple Silicon)
+sdkmanager "system-images;android-34;google_apis;arm64-v8a"
+
+# Android 13 (API 33)
+sdkmanager "system-images;android-33;google_apis;x86_64"
+```
+
+**Choose architecture:**
+- Intel/AMD processors: `x86_64`
+- Apple Silicon (M1/M2/M3): `arm64-v8a`
+
+### Create AVD
+
+```bash
+# Quick start
+avdmanager create avd \
+    -n Pixel7 \
+    -k "system-images;android-34;google_apis;x86_64" \
+    -d "pixel_7"
+
+# List available device definitions
+avdmanager list device
+
+# Common devices: pixel_7, pixel_7_pro, pixel_6, pixel_5, pixel_tablet
+```
+
+**Examples:**
+
+```bash
+# Pixel 6 with Android 13
+avdmanager create avd \
+    -n Pixel6 \
+    -k "system-images;android-33;google_apis;x86_64" \
+    -d "pixel_6"
+
+# Tablet (Pixel Tablet)
+avdmanager create avd \
+    -n PixelTablet \
+    -k "system-images;android-34;google_apis;x86_64" \
+    -d "pixel_tablet"
+
+# Custom configuration
+avdmanager create avd \
+    -n CustomPhone \
+    -k "system-images;android-34;google_apis;x86_64" \
+    -d "pixel_7" \
+    -c 512M \  # SD card size
+    --force    # Overwrite if exists
+```
+
+### Manage AVDs
+
+```bash
+# List all AVDs
+avdmanager list avd
+
+# List with paths
+avdmanager list avd -c
+
+# Delete AVD
+avdmanager delete avd -n Pixel7
+```
+
+### Start Emulator
+
+**Basic:**
+
+```bash
+# Start AVD
+emulator -avd Pixel7
+
+# Start in background
+emulator -avd Pixel7 &
+
+# Start with GPU acceleration
+emulator -avd Pixel7 -gpu host
+```
+
+**Common Options:**
+
+```bash
+# Wipe data (factory reset)
+emulator -avd Pixel7 -wipe-data
+
+# Read-only system
+emulator -avd Pixel7 -read-only
+
+# Increase RAM
+emulator -avd Pixel7 -memory 4096
+
+# Cold boot (ignore snapshot)
+emulator -avd Pixel7 -no-snapshot-load
+
+# No audio
+emulator -avd Pixel7 -no-audio
+
+# Maximum performance
+emulator -avd Pixel7 \
+    -gpu host \
+    -memory 4096 \
+    -cores 4 \
+    -no-boot-anim \
+    -no-snapshot
+```
+
+**Network Options:**
+
+```bash
+# Use specific DNS
+emulator -avd Pixel7 -dns-server 8.8.8.8
+
+# Network latency simulation
+emulator -avd Pixel7 -netdelay gprs  # or edge, umts, none
+
+# Network speed simulation
+emulator -avd Pixel7 -netspeed full  # or gsm, hscsd, gprs, edge, umts, hsdpa
+
+# Proxy
+emulator -avd Pixel7 -http-proxy http://localhost:8888
+```
+
+### Emulator Control
+
+**Via Telnet:**
+
+```bash
+# Connect to emulator console
+telnet localhost 5554  # Port shown when emulator starts
+
+# In telnet session:
+# auth <token>  # Token is in ~/.emulator_console_auth_token
+# help
+# sms send 1234567890 "Test message"
+# geo fix -122.084 37.422  # Set GPS location
+# quit
+```
+
+**Via ADB:**
+
+```bash
+# Rotate screen
+adb shell settings put system accelerometer_rotation 0
+adb shell settings put system user_rotation 1  # 0=0°, 1=90°, 2=180°, 3=270°
+
+# Simulate low battery
+adb shell dumpsys battery set level 10
+
+# Reset battery
+adb shell dumpsys battery reset
+```
+
+### Multiple Emulators
+
+```bash
+# Start first emulator
+emulator -avd Pixel7 &
+
+# Start second emulator (different AVD)
+emulator -avd Pixel6 &
+
+# Install on specific emulator
+adb -s emulator-5554 install app-debug.apk
+adb -s emulator-5556 install app-debug.apk
+```
+
+**Emulator Ports:**
+- First emulator: console 5554, adb 5555
+- Second emulator: console 5556, adb 5557
+
+## Testing
+
+### Test Types
+
+| Type | Runs On | Speed | Use For |
+|------|---------|-------|---------|
+| Unit Tests | JVM | Fast | Business logic, utilities |
+| Instrumentation Tests | Android device/emulator | Slow | Android APIs, database |
+| UI Tests | Android device/emulator | Slowest | User interface, interactions |
+
+### Unit Tests (Local Tests)
+
+**Location:** `app/src/test/java/com/example/myapp/`
+
+**Dependencies:**
+
+```kotlin
+dependencies {
+    testImplementation("junit:junit:4.13.2")
+    testImplementation("org.mockito:mockito-core:5.7.0")
+    testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
+}
+```
+
+**Example:**
+
+```kotlin
+package com.example.myapp
+
+import org.junit.Test
+import org.junit.Assert.*
+
+class CalculatorTest {
+
+    @Test
+    fun addition_isCorrect() {
+        val calculator = Calculator()
+        val result = calculator.add(2, 3)
+        assertEquals(5, result)
+    }
+
+    @Test
+    fun division_byZero_throwsException() {
+        val calculator = Calculator()
+        assertThrows(ArithmeticException::class.java) {
+            calculator.divide(10, 0)
+        }
+    }
+}
+```
+
+**Run Unit Tests:**
+
+```bash
+# All unit tests
+./gradlew test
+
+# Specific variant
+./gradlew testDebug
+
+# Specific test class
+./gradlew test --tests CalculatorTest
+
+# With coverage report
+./gradlew testDebugUnitTest jacocoTestReport
+```
+
+**View Results:**
+
+```bash
+# HTML report
+open app/build/reports/tests/testDebugUnitTest/index.html
+
+# Coverage report
+open app/build/reports/jacoco/jacocoTestReport/html/index.html
+```
+
+### Instrumentation Tests
+
+**Location:** `app/src/androidTest/java/com/example/myapp/`
+
+**Dependencies:**
+
+```kotlin
+android {
+    defaultConfig {
+        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+    }
+}
+
+dependencies {
+    androidTestImplementation("androidx.test.ext:junit:1.1.5")
+    androidTestImplementation("androidx.test:runner:1.5.2")
+    androidTestImplementation("androidx.test:rules:1.5.0")
+    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
+}
+```
+
+**Example:**
+
+```kotlin
+package com.example.myapp
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.Assert.*
+
+@RunWith(AndroidJUnit4::class)
+class DatabaseTest {
+
+    @Test
+    fun useAppContext() {
+        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
+        assertEquals("com.example.myapp", appContext.packageName)
+    }
+}
+```
+
+**Run Instrumentation Tests:**
+
+```bash
+# Connect device/emulator first
+adb devices
+
+# All instrumentation tests
+./gradlew connectedAndroidTest
+
+# Specific variant
+./gradlew connectedDebugAndroidTest
+
+# Specific test
+./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.example.myapp.DatabaseTest
+```
+
+### UI Tests (Espresso)
+
+**Dependencies:**
+
+```kotlin
+dependencies {
+    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
+    androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1")
+    androidTestImplementation("androidx.test.espresso:espresso-contrib:3.5.1")
+}
+```
+
+**Example:**
+
+```kotlin
+package com.example.myapp
+
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.espresso.Espresso.onView
+import androidx.test.espresso.action.ViewActions.*
+import androidx.test.espresso.assertion.ViewAssertions.matches
+import androidx.test.espresso.matcher.ViewMatchers.*
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidJUnit4::class)
+class MainActivityTest {
+
+    @get:Rule
+    val activityRule = ActivityScenarioRule(MainActivity::class.java)
+
+    @Test
+    fun button_click_showsText() {
+        // Type text in EditText
+        onView(withId(R.id.editText))
+            .perform(typeText("Hello"), closeSoftKeyboard())
+
+        // Click button
+        onView(withId(R.id.button))
+            .perform(click())
+
+        // Verify text is displayed
+        onView(withId(R.id.textView))
+            .check(matches(withText("Hello")))
+    }
+}
+```
+
+**Common Espresso Operations:**
+
+```kotlin
+// Find views
+onView(withId(R.id.viewId))
+onView(withText("text"))
+onView(withContentDescription("description"))
+
+// Actions
+perform(click())
+perform(typeText("text"))
+perform(replaceText("new text"))
+perform(clearText())
+perform(closeSoftKeyboard())
+perform(swipeLeft())
+perform(swipeRight())
+perform(scrollTo())
+
+// Assertions
+check(matches(isDisplayed()))
+check(matches(withText("expected")))
+check(matches(isEnabled()))
+check(matches(isChecked()))
+check(doesNotExist())
+```
+
+### Testing Gomobile Integration
+
+**Unit Test Go Code (Before binding):**
+
+```bash
+cd golib
+go test ./...
+go test -v ./...
+go test -cover ./...
+```
+
+**Test AAR Integration:**
+
+```kotlin
+package com.example.myapp
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import mylib.Mylib  // Import gomobile generated package
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.Assert.*
+
+@RunWith(AndroidJUnit4::class)
+class GomobileTest {
+
+    @Test
+    fun goFunction_returnsExpectedResult() {
+        val result = Mylib.fetchURL("https://example.com")
+        assertNotNull(result)
+        assertTrue(result.isNotEmpty())
+    }
+
+    @Test
+    fun goFunction_handlesError() {
+        try {
+            Mylib.fetchURL("invalid-url")
+            fail("Should have thrown exception")
+        } catch (e: Exception) {
+            assertTrue(e.message?.contains("error") == true)
+        }
+    }
+}
+```
+
+### Code Coverage (JaCoCo)
+
+**Configure:**
+
+```kotlin
+plugins {
+    id("jacoco")
+}
+
+android {
+    buildTypes {
+        debug {
+            enableAndroidTestCoverage = true
+            enableUnitTestCoverage = true
+        }
+    }
+}
+
+tasks.register<JacocoReport>("jacocoTestReport") {
+    dependsOn("testDebugUnitTest", "createDebugCoverageReport")
+
+    reports {
+        xml.required.set(true)
+        html.required.set(true)
+    }
+
+    val fileFilter = listOf(
+        "**/R.class",
+        "**/R$*.class",
+        "**/BuildConfig.*",
+        "**/Manifest*.*"
+    )
+
+    val debugTree = fileTree("${project.buildDir}/intermediates/javac/debug") {
+        exclude(fileFilter)
+    }
+
+    val mainSrc = "${project.projectDir}/src/main/java"
+
+    sourceDirectories.setFrom(files(mainSrc))
+    classDirectories.setFrom(files(debugTree))
+    executionData.setFrom(fileTree(project.buildDir) {
+        include("jacoco/testDebugUnitTest.exec", "outputs/code_coverage/debugAndroidTest/connected/**/*.ec")
+    })
+}
+```
+
+**Run Coverage:**
+
+```bash
+./gradlew jacocoTestReport
+open app/build/reports/jacoco/jacocoTestReport/html/index.html
+```
+
+### Test Best Practices
+
+**1. Follow AAA Pattern:**
+
+```kotlin
+@Test
+fun testName() {
+    // Arrange - Set up test data
+    val calculator = Calculator()
+
+    // Act - Execute the function
+    val result = calculator.add(2, 3)
+
+    // Assert - Verify the result
+    assertEquals(5, result)
+}
+```
+
+**2. Use Descriptive Names:**
+
+```kotlin
+// Good
+@Test
+fun addition_withPositiveNumbers_returnsSum() { }
+
+@Test
+fun division_withZeroDivisor_throwsException() { }
+
+// Bad
+@Test
+fun test1() { }
+```
+
+**3. Test One Thing:**
+
+```kotlin
+// Good - separate tests
+@Test
+fun add_returnsCorrectSum() { }
+
+@Test
+fun add_handlesNegativeNumbers() { }
+```
+
+**4. Mock External Dependencies:**
+
+```kotlin
+import org.mockito.Mock
+import org.mockito.Mockito.*
+
+class UserRepositoryTest {
+
+    @Mock
+    lateinit var api: ApiService
+
+    @Test
+    fun getUser_callsApi() {
+        val userId = "123"
+        `when`(api.fetchUser(userId)).thenReturn(User("John"))
+
+        val repository = UserRepository(api)
+        val user = repository.getUser(userId)
+
+        verify(api).fetchUser(userId)
+        assertEquals("John", user.name)
+    }
+}
+```
+
 ## Debugging from Code
 
 ### Android (Kotlin/Java)
@@ -183,7 +833,8 @@ class MainActivity : AppCompatActivity() {
 }
 ```
 
-View in logcat:
+**View in logcat:**
+
 ```bash
 adb logcat -s MainActivity
 ```
@@ -198,7 +849,6 @@ import "log"
 func FetchData(url string) (string, error) {
     log.Printf("Fetching URL: %s", url)  // Appears in logcat with GoLog tag
 
-    // Your code
     result, err := doFetch(url)
 
     if err != nil {
@@ -211,87 +861,35 @@ func FetchData(url string) (string, error) {
 }
 ```
 
-View in logcat:
+**View in logcat:**
+
 ```bash
 adb logcat | grep GoLog
 ```
 
-## Device/Emulator Commands
+### Interactive Debugging
 
-### File Operations
+**Enable Debug Mode:**
 
-```bash
-# Push file to device
-adb push local-file.txt /sdcard/
-
-# Pull file from device
-adb pull /sdcard/file.txt ./
-
-# List files
-adb shell ls /sdcard/
-
-# View file content
-adb shell cat /sdcard/file.txt
-
-# Remove file
-adb shell rm /sdcard/file.txt
+```kotlin
+// app/build.gradle.kts
+android {
+    buildTypes {
+        debug {
+            isDebuggable = true
+        }
+    }
+}
 ```
 
-### Screenshots and Screen Recording
+**Using Android Studio Debugger:**
 
-```bash
-# Take screenshot
-adb shell screencap /sdcard/screen.png
-adb pull /sdcard/screen.png
-adb shell rm /sdcard/screen.png
-
-# Or in one command
-adb exec-out screencap -p > screen.png
-
-# Record screen (max 3 minutes)
-adb shell screenrecord /sdcard/demo.mp4
-# Press Ctrl+C to stop
-adb pull /sdcard/demo.mp4
-adb shell rm /sdcard/demo.mp4
-```
-
-### System Information
-
-```bash
-# CPU info
-adb shell cat /proc/cpuinfo
-
-# Memory info
-adb shell cat /proc/meminfo
-
-# Battery status
-adb shell dumpsys battery
-
-# Display info
-adb shell dumpsys display
-
-# Running processes
-adb shell ps
-
-# App processes
-adb shell ps | grep com.example.myapp
-```
-
-### Network
-
-```bash
-# Check network connectivity
-adb shell ping -c 4 google.com
-
-# View network interfaces
-adb shell ip addr
-
-# Port forwarding (device port to local port)
-adb forward tcp:8080 tcp:8080
-
-# Reverse port forwarding (local port to device)
-adb reverse tcp:9000 tcp:9000
-```
+1. Build debug APK: `./gradlew assembleDebug`
+2. Install on device: `adb install app-debug.apk`
+3. In Android Studio: Run → Attach Debugger to Android Process
+4. Select your app
+5. Set breakpoints in code
+6. Trigger the code path
 
 ## Performance Profiling
 
@@ -363,7 +961,8 @@ java.lang.NullPointerException: Attempt to invoke virtual method '...' on a null
 
 ### 4. Common Crash Types
 
-**NullPointerException**:
+**NullPointerException:**
+
 ```kotlin
 // Bad
 val user = getUser()  // Returns null
@@ -374,7 +973,8 @@ val user = getUser()
 val name = user?.name ?: "Unknown"
 ```
 
-**ClassCastException**:
+**ClassCastException:**
+
 ```kotlin
 // Bad
 val text = view as TextView  // Crash if not TextView!
@@ -383,7 +983,8 @@ val text = view as TextView  // Crash if not TextView!
 val text = view as? TextView
 ```
 
-**ActivityNotFoundException**:
+**ActivityNotFoundException:**
+
 ```kotlin
 // Check if intent can be handled
 if (intent.resolveActivity(packageManager) != null) {
@@ -391,43 +992,6 @@ if (intent.resolveActivity(packageManager) != null) {
 }
 ```
 
-## Interactive Debugging
-
-### Enable Debug Mode in App
-
-```kotlin
-// app/build.gradle.kts
-android {
-    buildTypes {
-        debug {
-            isDebuggable = true
-        }
-    }
-}
-```
-
-### Using Android Studio Debugger
-
-1. Build debug APK: `./gradlew assembleDebug`
-2. Install on device: `adb install app-debug.apk`
-3. In Android Studio: Run → Attach Debugger to Android Process
-4. Select your app
-5. Set breakpoints in code
-6. Trigger the code path
-
-### Command-line Debugging (jdb)
-
-```bash
-# Find app process
-adb shell ps | grep com.example.myapp
-
-# Forward debug port
-adb forward tcp:8000 jdwp:<pid>
-
-# Attach jdb
-jdb -attach localhost:8000
-```
-
 ## Network Debugging
 
 ### HTTP Traffic Inspection
@@ -446,9 +1010,11 @@ adb reverse tcp:8888 tcp:8888
 adb shell dumpsys netstats
 ```
 
-## Common Issues
+## Troubleshooting
 
-### "adb: device offline"
+### ADB Issues
+
+**"adb: device offline":**
 
 ```bash
 adb kill-server
@@ -456,7 +1022,7 @@ adb start-server
 # Reconnect device
 ```
 
-### "adb: device unauthorized"
+**"adb: device unauthorized":**
 
 - Check device screen for authorization prompt
 - Accept "Always allow from this computer"
@@ -467,7 +1033,7 @@ adb start-server
   adb start-server
   ```
 
-### "Logcat shows nothing"
+**"Logcat shows nothing":**
 
 ```bash
 # Clear buffer
@@ -480,21 +1046,92 @@ adb logcat -g
 adb logcat -G 16M
 ```
 
-### App not showing in process list
+### Emulator Issues
+
+**Emulator won't start:**
 
 ```bash
-# Check if app is running
-adb shell "ps -A | grep com.example.myapp"
+# Check emulator location
+which emulator
 
-# Start app if not running
-adb shell am start -n com.example.myapp/.MainActivity
+# Run with verbose logging
+emulator -avd Pixel7 -verbose
+
+# Check for errors
+emulator -avd Pixel7 -debug init
 ```
 
-## Debug Helpers
+**Slow emulator - Enable hardware acceleration:**
 
-### Monitor script
+**Linux (KVM):**
+
+```bash
+# Check KVM support
+egrep -c '(vmx|svm)' /proc/cpuinfo  # Should be > 0
+
+# Check KVM device
+ls -l /dev/kvm
+
+# Add user to kvm group
+sudo usermod -a -G kvm $USER
+# Log out and back in
+
+# Verify
+emulator -accel-check
+```
+
+**macOS:** Automatically uses Hypervisor Framework
+
+**Windows (HAXM):**
+
+```bash
+sdkmanager "extras;intel;Hardware_Accelerated_Execution_Manager"
+```
+
+**Graphics issues:**
+
+```bash
+# Try different GPU modes
+emulator -avd Pixel7 -gpu auto
+emulator -avd Pixel7 -gpu host
+emulator -avd Pixel7 -gpu swiftshader_indirect
+emulator -avd Pixel7 -gpu angle_indirect
+```
+
+### Test Issues
+
+**Tests fail on CI but pass locally:**
+
+- Check Android API level compatibility
+- Verify emulator configuration
+- Check for timing issues (add timeouts)
+- Ensure deterministic test data
+
+**Instrumentation tests hang:**
+
+```bash
+# Clear app data between tests
+adb shell pm clear com.example.myapp
+
+# Restart adb
+adb kill-server
+adb start-server
+```
+
+**Espresso can't find view:**
+
+```kotlin
+// Use idling resources for async operations
+import androidx.test.espresso.IdlingRegistry
+import androidx.test.espresso.IdlingResource
+```
+
+## Helper Scripts
+
+### Monitor App Script
 
 Create `monitor-app.sh`:
+
 ```bash
 #!/usr/bin/env bash
 set -euo pipefail
@@ -509,9 +1146,10 @@ adb logcat -c
 adb logcat | grep --color=always -E "$APP_PACKAGE|$LOG_TAG|FATAL|ERROR"
 ```
 
-### Install and monitor script
+### Install and Monitor Script
 
 Create `debug-install.sh`:
+
 ```bash
 #!/usr/bin/env bash
 set -euo pipefail
@@ -531,14 +1169,120 @@ adb logcat -c
 adb logcat | grep "$PACKAGE"
 ```
 
+### Start Emulator and Wait
+
+Create `start-emulator.sh`:
+
+```bash
+#!/usr/bin/env bash
+set -euo pipefail
+
+AVD_NAME="Pixel7"
+
+echo "Starting emulator: $AVD_NAME"
+emulator -avd "$AVD_NAME" -no-snapshot-save &
+
+# Wait for device to boot
+echo "Waiting for device to boot..."
+adb wait-for-device
+
+# Wait for boot animation to finish
+while [ "$(adb shell getprop init.svc.bootanim | tr -d '\r')" = "running" ]; do
+    sleep 1
+done
+
+echo "Emulator ready!"
+
+# Optional: Install app
+if [ -f "app-debug.apk" ]; then
+    echo "Installing app..."
+    adb install -r app-debug.apk
+fi
+
+echo "Done!"
+```
+
+## CI/CD Testing
+
+### GitHub Actions Example
+
+`.github/workflows/test.yml`:
+
+```yaml
+name: Android CI
+
+on:
+  push:
+    branches: [ main ]
+  pull_request:
+    branches: [ main ]
+
+jobs:
+  test:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Set up JDK 17
+        uses: actions/setup-java@v4
+        with:
+          distribution: 'temurin'
+          java-version: '17'
+
+      - name: Setup Go
+        uses: actions/setup-go@v5
+        with:
+          go-version: '1.22'
+
+      - name: Test Go code
+        run: |
+          cd golib
+          go test -v ./...
+
+      - name: Build gomobile AAR
+        run: |
+          go install golang.org/x/mobile/cmd/gomobile@latest
+          gomobile init
+          cd golib
+          gomobile bind -target=android -o mylib.aar .
+          cp mylib.aar ../app/libs/
+
+      - name: Run unit tests
+        run: ./gradlew test
+
+      - name: Run instrumented tests
+        uses: reactivecircus/android-emulator-runner@v2
+        with:
+          api-level: 34
+          target: google_apis
+          arch: x86_64
+          script: ./gradlew connectedCheck
+
+      - name: Upload test results
+        if: always()
+        uses: actions/upload-artifact@v4
+        with:
+          name: test-results
+          path: app/build/reports/tests/
+
+      - name: Upload coverage
+        uses: codecov/codecov-action@v3
+        with:
+          files: app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
+```
+
 ## Next Steps
 
-- **Run tests** - Use the **Test** workflow
-- **Profile performance** - Advanced profiling techniques
-- **Publish app** - Use the **Publish** workflow
+- **Build and publish** - Use the **Build** workflow for release builds
+- **Increase coverage** - Aim for >80% test coverage
+- **Profile performance** - Identify and fix bottlenecks
+- **Monitor crashes** - Set up crash reporting (Firebase Crashlytics)
 
 ## Resources
 
 - [ADB Documentation](https://developer.android.com/studio/command-line/adb)
 - [Logcat Command-line Tool](https://developer.android.com/studio/command-line/logcat)
 - [Debug Your App](https://developer.android.com/studio/debug)
+- [Android Testing](https://developer.android.com/training/testing)
+- [Espresso Documentation](https://developer.android.com/training/testing/espresso)
+- [Emulator Documentation](https://developer.android.com/studio/run/emulator)
dots/.config/claude/skills/Android/workflows/Emulator.md
@@ -1,471 +0,0 @@
-# Emulator Workflow
-
-Create, configure, and manage Android Virtual Devices (AVDs) for testing apps without physical devices.
-
-## Quick Start
-
-```bash
-# List available system images
-sdkmanager --list | grep system-images
-
-# Install system image (API 34, x86_64, Google APIs)
-sdkmanager "system-images;android-34;google_apis;x86_64"
-
-# Create AVD
-avdmanager create avd \
-    -n Pixel7 \
-    -k "system-images;android-34;google_apis;x86_64" \
-    -d "pixel_7"
-
-# List AVDs
-avdmanager list avd
-
-# Start emulator
-emulator -avd Pixel7
-```
-
-## System Images
-
-### Image Types
-
-| Type | Google Apps | Play Store | Use Case |
-|------|-------------|------------|----------|
-| `default` | ❌ | ❌ | AOSP, minimal |
-| `google_apis` | ✅ | ❌ | Google Maps, etc. |
-| `google_apis_playstore` | ✅ | ✅ | Full Play Services |
-
-### Common System Images
-
-```bash
-# Android 14 (API 34) - x86_64 (for Intel/AMD)
-sdkmanager "system-images;android-34;google_apis;x86_64"
-
-# Android 14 (API 34) - ARM64 (for Apple Silicon with Android Emulator for M1)
-sdkmanager "system-images;android-34;google_apis;arm64-v8a"
-
-# Android 13 (API 33)
-sdkmanager "system-images;android-33;google_apis;x86_64"
-
-# Android 12 (API 31)
-sdkmanager "system-images;android-31;google_apis;x86_64"
-```
-
-**Choose architecture based on your CPU:**
-- Intel/AMD processors: `x86_64`
-- Apple Silicon (M1/M2/M3): `arm64-v8a` (requires Android Emulator for M1+)
-
-## Create AVD
-
-### Interactive (using avdmanager)
-
-```bash
-avdmanager create avd \
-    -n <name> \
-    -k <system-image> \
-    -d <device-definition>
-```
-
-**Examples:**
-
-```bash
-# Pixel 7 with Android 14
-avdmanager create avd \
-    -n Pixel7 \
-    -k "system-images;android-34;google_apis;x86_64" \
-    -d "pixel_7"
-
-# Pixel 6 with Android 13
-avdmanager create avd \
-    -n Pixel6 \
-    -k "system-images;android-33;google_apis;x86_64" \
-    -d "pixel_6"
-
-# Tablet (Pixel Tablet)
-avdmanager create avd \
-    -n PixelTablet \
-    -k "system-images;android-34;google_apis;x86_64" \
-    -d "pixel_tablet"
-```
-
-### Custom Configuration
-
-```bash
-# Custom resolution and RAM
-avdmanager create avd \
-    -n CustomPhone \
-    -k "system-images;android-34;google_apis;x86_64" \
-    -d "pixel_7" \
-    -c 512M \  # SD card size
-    --force    # Overwrite if exists
-```
-
-### List Device Definitions
-
-```bash
-avdmanager list device
-```
-
-Common devices:
-- `pixel_7`, `pixel_7_pro`
-- `pixel_6`, `pixel_6_pro`
-- `pixel_5`
-- `pixel_tablet`
-
-## Manage AVDs
-
-### List AVDs
-
-```bash
-# List all AVDs
-avdmanager list avd
-
-# List with paths
-avdmanager list avd -c
-```
-
-### Delete AVD
-
-```bash
-avdmanager delete avd -n <name>
-
-# Example
-avdmanager delete avd -n Pixel7
-```
-
-### Move AVD
-
-AVDs are stored in `~/.android/avd/`. To move:
-
-```bash
-# Move AVD directory
-mv ~/.android/avd/Pixel7.avd /new/location/
-
-# Update .ini file
-echo "path=/new/location/Pixel7.avd" > ~/.android/avd/Pixel7.ini
-```
-
-## Start Emulator
-
-### Basic Start
-
-```bash
-# Start AVD
-emulator -avd Pixel7
-
-# Start in background
-emulator -avd Pixel7 &
-
-# Start with GPU acceleration
-emulator -avd Pixel7 -gpu host
-```
-
-### Start Options
-
-```bash
-# Wipe data (factory reset)
-emulator -avd Pixel7 -wipe-data
-
-# Read-only system
-emulator -avd Pixel7 -read-only
-
-# Specific resolution
-emulator -avd Pixel7 -skin 1080x1920
-
-# Increase RAM
-emulator -avd Pixel7 -memory 4096
-
-# Cold boot (ignore snapshot)
-emulator -avd Pixel7 -no-snapshot-load
-
-# No audio
-emulator -avd Pixel7 -no-audio
-```
-
-### Performance Options
-
-```bash
-# Maximum performance
-emulator -avd Pixel7 \
-    -gpu host \
-    -memory 4096 \
-    -cores 4 \
-    -no-boot-anim \
-    -no-snapshot
-
-# Quick boot (use snapshot)
-emulator -avd Pixel7 -no-snapshot-save
-```
-
-### Network Options
-
-```bash
-# Use specific DNS
-emulator -avd Pixel7 -dns-server 8.8.8.8
-
-# Network latency simulation
-emulator -avd Pixel7 -netdelay gprs  # or edge, umts, none
-
-# Network speed simulation
-emulator -avd Pixel7 -netspeed full  # or gsm, hscsd, gprs, edge, umts, hsdpa
-
-# Proxy
-emulator -avd Pixel7 -http-proxy http://localhost:8888
-```
-
-## Emulator Control
-
-### From Command Line
-
-While emulator is running:
-
-```bash
-# Connect to emulator console
-telnet localhost 5554  # Port is shown when emulator starts
-
-# In telnet session:
-# auth <token>  # Token is in ~/.emulator_console_auth_token
-# help
-# sms send 1234567890 "Test message"
-# geo fix -122.084 37.422  # Set GPS location
-# quit
-```
-
-### Using ADB
-
-```bash
-# List running emulators
-adb devices
-
-# Install app
-adb -e install app-debug.apk  # -e targets emulator
-
-# Rotate screen
-adb shell settings put system accelerometer_rotation 0
-adb shell settings put system user_rotation 1  # 0=0°, 1=90°, 2=180°, 3=270°
-
-# Simulate low battery
-adb shell dumpsys battery set level 10
-
-# Reset battery
-adb shell dumpsys battery reset
-```
-
-## Configuration Files
-
-### config.ini
-
-Located at `~/.android/avd/<name>.avd/config.ini`
-
-**Common settings:**
-```ini
-# RAM
-hw.ramSize=4096
-
-# Storage
-disk.dataPartition.size=8G
-
-# GPU
-hw.gpu.enabled=yes
-hw.gpu.mode=host
-
-# Camera
-hw.camera.back=emulated
-hw.camera.front=emulated
-
-# Keyboard
-hw.keyboard=yes
-
-# Network
-hw.net.speed=full
-hw.net.delay=none
-```
-
-## Snapshots
-
-### Quick Boot (Snapshots)
-
-Emulator saves state for faster startup.
-
-```bash
-# Start without loading snapshot (cold boot)
-emulator -avd Pixel7 -no-snapshot-load
-
-# Start without saving snapshot (testing)
-emulator -avd Pixel7 -no-snapshot-save
-
-# Disable snapshots completely
-emulator -avd Pixel7 -no-snapshot
-```
-
-### Manage Snapshots
-
-```bash
-# List snapshots
-emulator -avd Pixel7 -list-snapshots
-
-# Load specific snapshot
-emulator -avd Pixel7 -snapshot <name>
-```
-
-## Troubleshooting
-
-### Emulator won't start
-
-```bash
-# Check emulator location
-which emulator
-
-# Run with verbose logging
-emulator -avd Pixel7 -verbose
-
-# Check for errors
-emulator -avd Pixel7 -debug init
-```
-
-### Slow emulator
-
-**Enable hardware acceleration:**
-
-**Linux (KVM):**
-```bash
-# Check KVM support
-egrep -c '(vmx|svm)' /proc/cpuinfo  # Should be > 0
-
-# Check KVM device
-ls -l /dev/kvm
-
-# Add user to kvm group
-sudo usermod -a -G kvm $USER
-# Log out and back in
-
-# Verify
-emulator -accel-check
-```
-
-**macOS (Hypervisor Framework):**
-Automatically used on modern macOS.
-
-**Windows (HAXM):**
-Install Intel HAXM:
-```bash
-sdkmanager "extras;intel;Hardware_Accelerated_Execution_Manager"
-```
-
-### "PANIC: Cannot find AVD system path"
-
-```bash
-# Check ANDROID_AVD_HOME
-echo $ANDROID_AVD_HOME
-
-# Set if needed
-export ANDROID_AVD_HOME=$HOME/.android/avd
-```
-
-### Graphics issues
-
-```bash
-# Try different GPU modes
-emulator -avd Pixel7 -gpu auto
-emulator -avd Pixel7 -gpu host
-emulator -avd Pixel7 -gpu swiftshader_indirect
-emulator -avd Pixel7 -gpu angle_indirect
-```
-
-## Multiple Emulators
-
-### Run Multiple Instances
-
-```bash
-# Start first emulator
-emulator -avd Pixel7 &
-
-# Start second emulator (different AVD)
-emulator -avd Pixel6 &
-
-# Install on specific emulator
-adb -s emulator-5554 install app-debug.apk
-adb -s emulator-5556 install app-debug.apk
-```
-
-### Emulator Ports
-
-- First emulator: console port 5554, adb port 5555
-- Second emulator: console port 5556, adb port 5557
-- And so on...
-
-## Automation Scripts
-
-### Start emulator and wait for boot
-
-```bash
-#!/usr/bin/env bash
-set -euo pipefail
-
-AVD_NAME="Pixel7"
-
-echo "Starting emulator: $AVD_NAME"
-emulator -avd "$AVD_NAME" -no-snapshot-save &
-
-# Wait for device to boot
-echo "Waiting for device to boot..."
-adb wait-for-device
-
-# Wait for boot animation to finish
-while [ "$(adb shell getprop init.svc.bootanim | tr -d '\r')" = "running" ]; do
-    sleep 1
-done
-
-echo "Emulator ready!"
-
-# Optional: Install app
-if [ -f "app-debug.apk" ]; then
-    echo "Installing app..."
-    adb install -r app-debug.apk
-fi
-
-echo "Done!"
-```
-
-### Create standard test emulator
-
-```bash
-#!/usr/bin/env bash
-set -euo pipefail
-
-AVD_NAME="TestDevice"
-SYSTEM_IMAGE="system-images;android-34;google_apis;x86_64"
-
-# Install system image if not already installed
-sdkmanager "$SYSTEM_IMAGE"
-
-# Create AVD (force overwrite if exists)
-avdmanager create avd \
-    -n "$AVD_NAME" \
-    -k "$SYSTEM_IMAGE" \
-    -d "pixel_7" \
-    --force
-
-echo "AVD '$AVD_NAME' created successfully"
-echo "Start with: emulator -avd $AVD_NAME"
-```
-
-## Best Practices
-
-1. **Use hardware acceleration**: Essential for acceptable performance
-2. **Limit RAM**: Don't exceed 50% of your system RAM
-3. **Close when not in use**: Emulators consume significant resources
-4. **Use snapshots**: Enable quick boot for faster startup
-5. **Match target devices**: Create AVDs matching your target devices
-6. **Test on real devices**: Emulators don't perfectly match real hardware
-7. **Use x86_64 images**: Faster than ARM on x86 computers (except Apple Silicon)
-
-## Next Steps
-
-- **Install and test app** - Use the **Build** and **Debug** workflows
-- **Run automated tests** - Use the **Test** workflow
-
-## Resources
-
-- [AVD Manager Documentation](https://developer.android.com/studio/command-line/avdmanager)
-- [Emulator Documentation](https://developer.android.com/studio/run/emulator)
-- [Configure Hardware Acceleration](https://developer.android.com/studio/run/emulator-acceleration)
dots/.config/claude/skills/Android/workflows/GomobileApp.md
@@ -1,670 +0,0 @@
-# GomobileApp Workflow
-
-Build standalone Android applications entirely in Go using `gomobile build`. This creates a complete APK with Go code as the main application logic.
-
-## When to Use
-
-- You want to write the entire app in Go
-- You're building a simple utility or demo app
-- You want to prototype quickly without Java/Kotlin
-- You're comfortable with limited UI capabilities
-- You want to share code between CLI and mobile versions
-
-## When NOT to Use
-
-- You need complex native Android UI (use GomobileBind + Kotlin instead)
-- You need full access to Android framework APIs
-- You're building a production app with rich UI
-- You need Material Design components
-
-## Prerequisites
-
-- Go installed (`go version`)
-- Android SDK and NDK installed
-- `ANDROID_HOME` environment variable set
-- Gomobile installed (`gomobile version`)
-
-## Steps
-
-### 1. Install and Initialize Gomobile
-
-```bash
-# Install gomobile
-go install golang.org/x/mobile/cmd/gomobile@latest
-
-# Initialize (downloads Android toolchain)
-gomobile init
-
-# Verify
-gomobile version
-```
-
-### 2. Create Go Mobile App
-
-**Project structure:**
-```
-myapp/
-├── main.go
-├── go.mod
-└── assets/         # Optional: app resources
-    └── icon.png
-```
-
-**Basic app (`main.go`):**
-```go
-package main
-
-import (
-    "log"
-
-    "golang.org/x/mobile/app"
-    "golang.org/x/mobile/event/lifecycle"
-    "golang.org/x/mobile/event/paint"
-    "golang.org/x/mobile/event/size"
-    "golang.org/x/mobile/event/touch"
-    "golang.org/x/mobile/gl"
-)
-
-func main() {
-    app.Main(func(a app.App) {
-        var glctx gl.Context
-        var sz size.Event
-
-        for e := range a.Events() {
-            switch e := a.Filter(e).(type) {
-            case lifecycle.Event:
-                switch e.Crosses(lifecycle.StageVisible) {
-                case lifecycle.CrossOn:
-                    glctx, _ = e.DrawContext.(gl.Context)
-                    onStart(glctx)
-                case lifecycle.CrossOff:
-                    onStop(glctx)
-                    glctx = nil
-                }
-
-            case size.Event:
-                sz = e
-
-            case paint.Event:
-                if glctx == nil || e.External {
-                    continue
-                }
-                onPaint(glctx, sz)
-                a.Publish()
-                a.Send(paint.Event{}) // Keep animating
-
-            case touch.Event:
-                // Handle touch events
-                log.Printf("Touch: %v", e)
-            }
-        }
-    })
-}
-
-func onStart(glctx gl.Context) {
-    log.Println("App started")
-    // Initialize OpenGL resources
-}
-
-func onStop(glctx gl.Context) {
-    log.Println("App stopped")
-    // Clean up resources
-}
-
-func onPaint(glctx gl.Context, sz size.Event) {
-    // Clear screen
-    glctx.ClearColor(0.2, 0.2, 0.3, 1.0)
-    glctx.Clear(gl.COLOR_BUFFER_BIT)
-
-    // Draw your content here
-}
-```
-
-**Simple text-based app (no OpenGL):**
-```go
-package main
-
-import (
-    "fmt"
-    "log"
-
-    "golang.org/x/mobile/app"
-    "golang.org/x/mobile/event/lifecycle"
-    "golang.org/x/mobile/event/touch"
-)
-
-func main() {
-    app.Main(func(a app.App) {
-        for e := range a.Events() {
-            switch e := a.Filter(e).(type) {
-            case lifecycle.Event:
-                switch e.Crosses(lifecycle.StageAlive) {
-                case lifecycle.CrossOn:
-                    log.Println("App is alive")
-                    // Do work here
-                    processData()
-                case lifecycle.CrossOff:
-                    log.Println("App is closing")
-                }
-
-            case touch.Event:
-                if e.Type == touch.TypeBegin {
-                    log.Printf("Touch at: %v, %v", e.X, e.Y)
-                }
-            }
-        }
-    })
-}
-
-func processData() {
-    // Your app logic
-    fmt.Println("Processing...")
-}
-```
-
-**Practical example: Network utility**
-```go
-package main
-
-import (
-    "fmt"
-    "io"
-    "log"
-    "net/http"
-    "time"
-
-    "golang.org/x/mobile/app"
-    "golang.org/x/mobile/event/lifecycle"
-)
-
-func main() {
-    app.Main(func(a app.App) {
-        for e := range a.Events() {
-            switch e := a.Filter(e).(type) {
-            case lifecycle.Event:
-                if e.Crosses(lifecycle.StageAlive) == lifecycle.CrossOn {
-                    go runNetworkCheck()
-                }
-            }
-        }
-    })
-}
-
-func runNetworkCheck() {
-    ticker := time.NewTicker(5 * time.Second)
-    defer ticker.Stop()
-
-    for range ticker.C {
-        resp, err := http.Get("https://www.google.com")
-        if err != nil {
-            log.Printf("Network check failed: %v", err)
-            continue
-        }
-        io.Copy(io.Discard, resp.Body)
-        resp.Body.Close()
-
-        log.Printf("Network OK: %s", resp.Status)
-    }
-}
-```
-
-### 3. Create go.mod
-
-```bash
-go mod init example.com/myapp
-go get golang.org/x/mobile/app
-go get golang.org/x/mobile/event/lifecycle
-go mod tidy
-```
-
-### 4. Build APK
-
-**Basic build:**
-```bash
-gomobile build -target=android .
-```
-
-This creates `myapp.apk` in the current directory.
-
-**Build with custom app ID:**
-```bash
-gomobile build -target=android -appid=com.example.myapp .
-```
-
-**Build for specific architectures:**
-```bash
-# ARM64 only (smaller APK)
-gomobile build -target=android/arm64 -appid=com.example.myapp .
-
-# ARM64 and ARM32
-gomobile build -target=android/arm64,android/arm -appid=com.example.myapp .
-```
-
-**Build with custom icon:**
-```bash
-# Place icon at assets/icon.png
-gomobile build -target=android -icon=assets/icon.png .
-```
-
-**Build with version:**
-```bash
-gomobile build \
-    -target=android \
-    -appid=com.example.myapp \
-    -ldflags="-X main.version=1.0.0" \
-    .
-```
-
-### 5. Install and Run
-
-**Install on connected device:**
-```bash
-# Build and install
-gomobile install -target=android -appid=com.example.myapp .
-
-# Or build then install separately
-gomobile build -target=android -appid=com.example.myapp .
-adb install -r myapp.apk
-```
-
-**View logs:**
-```bash
-adb logcat | grep GoLog
-```
-
-### 6. Test the App
-
-**List installed apps:**
-```bash
-adb shell pm list packages | grep myapp
-```
-
-**Launch app:**
-```bash
-adb shell am start -n com.example.myapp/.MainActivity
-```
-
-**Stop app:**
-```bash
-adb shell am force-stop com.example.myapp
-```
-
-**Uninstall:**
-```bash
-adb uninstall com.example.myapp
-```
-
-## App Package Structure
-
-The gomobile app package provides:
-
-### Event Types
-
-```go
-import "golang.org/x/mobile/event"
-
-// Lifecycle events
-lifecycle.Event    // App start, stop, pause, resume
-
-// Touch events
-touch.Event        // Touch screen interactions
-touch.TypeBegin    // Finger down
-touch.TypeMove     // Finger dragging
-touch.TypeEnd      // Finger up
-
-// Paint events
-paint.Event        // Redraw requests
-
-// Size events
-size.Event         // Screen size, orientation changes
-
-// Key events
-key.Event          // Keyboard input
-```
-
-### GL Context
-
-```go
-import "golang.org/x/mobile/gl"
-
-// OpenGL ES 2.0 context
-glctx gl.Context
-
-// Common operations
-glctx.ClearColor(r, g, b, a float32)
-glctx.Clear(gl.COLOR_BUFFER_BIT)
-glctx.Enable(gl.BLEND)
-```
-
-## Advanced Examples
-
-### HTTP Server App
-
-```go
-package main
-
-import (
-    "fmt"
-    "log"
-    "net/http"
-
-    "golang.org/x/mobile/app"
-    "golang.org/x/mobile/event/lifecycle"
-)
-
-func main() {
-    app.Main(func(a app.App) {
-        var server *http.Server
-
-        for e := range a.Events() {
-            switch e := a.Filter(e).(type) {
-            case lifecycle.Event:
-                switch e.Crosses(lifecycle.StageAlive) {
-                case lifecycle.CrossOn:
-                    server = startServer()
-                case lifecycle.CrossOff:
-                    if server != nil {
-                        server.Close()
-                    }
-                }
-            }
-        }
-    })
-}
-
-func startServer() *http.Server {
-    mux := http.NewServeMux()
-    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
-        fmt.Fprintf(w, "Hello from Go on Android!")
-    })
-
-    server := &http.Server{
-        Addr:    ":8080",
-        Handler: mux,
-    }
-
-    go func() {
-        log.Println("Starting server on :8080")
-        if err := server.ListenAndServe(); err != http.ErrServerClosed {
-            log.Printf("Server error: %v", err)
-        }
-    }()
-
-    return server
-}
-```
-
-### File Operations
-
-```go
-package main
-
-import (
-    "log"
-    "os"
-    "path/filepath"
-
-    "golang.org/x/mobile/app"
-    "golang.org/x/mobile/event/lifecycle"
-)
-
-func main() {
-    app.Main(func(a app.App) {
-        for e := range a.Events() {
-            switch e := a.Filter(e).(type) {
-            case lifecycle.Event:
-                if e.Crosses(lifecycle.StageAlive) == lifecycle.CrossOn {
-                    workWithFiles()
-                }
-            }
-        }
-    })
-}
-
-func workWithFiles() {
-    // Get app data directory
-    dataDir, err := os.UserConfigDir()
-    if err != nil {
-        log.Printf("Error getting data dir: %v", err)
-        return
-    }
-
-    // Create file
-    filePath := filepath.Join(dataDir, "mydata.txt")
-    err = os.WriteFile(filePath, []byte("Hello, Android!"), 0644)
-    if err != nil {
-        log.Printf("Error writing file: %v", err)
-        return
-    }
-
-    // Read file
-    data, err := os.ReadFile(filePath)
-    if err != nil {
-        log.Printf("Error reading file: %v", err)
-        return
-    }
-
-    log.Printf("File content: %s", string(data))
-}
-```
-
-## Customization
-
-### AndroidManifest.xml
-
-Gomobile generates AndroidManifest.xml automatically. To customize:
-
-1. **Build APK first:**
-   ```bash
-   gomobile build -target=android -appid=com.example.myapp .
-   ```
-
-2. **Extract APK:**
-   ```bash
-   unzip myapp.apk -d myapp-extracted
-   ```
-
-3. **View manifest:**
-   ```bash
-   cat myapp-extracted/AndroidManifest.xml
-   ```
-
-4. **For advanced customization**, use gomobile bind instead and create a full Android project.
-
-### Permissions
-
-Gomobile apps have minimal permissions by default. For additional permissions, you'll need to:
-
-1. Use gomobile bind to create AAR
-2. Create full Android project
-3. Add permissions in AndroidManifest.xml
-
-## Limitations
-
-### UI Limitations
-
-- ❌ No native Android UI widgets (Button, TextView, etc.)
-- ❌ No Material Design components
-- ❌ Limited text rendering capabilities
-- ✅ OpenGL ES 2.0 for custom graphics
-- ✅ Touch event handling
-- ✅ Basic drawing and animations
-
-### Platform Limitations
-
-- ❌ No direct access to Android framework APIs
-- ❌ Can't use Java/Kotlin libraries
-- ❌ No system services (Camera, GPS, etc.)
-- ✅ Network access (HTTP, TCP, UDP)
-- ✅ File I/O
-- ✅ Concurrency with goroutines
-
-### When to Use gomobile build vs gomobile bind
-
-| Feature | gomobile build | gomobile bind |
-|---------|----------------|---------------|
-| UI | OpenGL only | Full Android UI |
-| Android APIs | Limited | Full access |
-| Development speed | Fast | Medium |
-| Code sharing | Easy | Moderate |
-| Production apps | Demos/utilities | Production ready |
-| Learning curve | Low | Medium |
-
-## Build Automation
-
-**build.sh script:**
-```bash
-#!/usr/bin/env bash
-set -euo pipefail
-
-APP_ID="com.example.myapp"
-VERSION="1.0.0"
-TARGET="android/arm64,android/arm"
-
-echo "Building Android app..."
-
-# Build APK
-gomobile build \
-    -target="$TARGET" \
-    -appid="$APP_ID" \
-    -ldflags="-X main.version=$VERSION" \
-    .
-
-echo "APK built: myapp.apk"
-
-# Optional: Install on device
-if adb devices | grep -q "device$"; then
-    echo "Installing on device..."
-    adb install -r myapp.apk
-    echo "Launch with: adb shell am start -n $APP_ID/.MainActivity"
-else
-    echo "No device connected. Install with: adb install myapp.apk"
-fi
-```
-
-## Debugging
-
-### View Logs
-
-```bash
-# All logs
-adb logcat
-
-# Go logs only
-adb logcat | grep GoLog
-
-# Your app logs
-adb logcat | grep "com.example.myapp"
-
-# Clear logs first
-adb logcat -c && adb logcat
-```
-
-### Add Debug Logging
-
-```go
-import "log"
-
-func main() {
-    log.SetPrefix("[MyApp] ")
-    log.Println("App starting...")
-
-    // Your code
-}
-```
-
-### Check App Status
-
-```bash
-# Check if app is running
-adb shell ps | grep myapp
-
-# Check app info
-adb shell dumpsys package com.example.myapp
-```
-
-## Performance Tips
-
-1. **Use goroutines wisely**: They're not free, even in Go
-2. **Minimize allocations**: GC pauses affect frame rate
-3. **Use buffered channels**: Reduce goroutine blocking
-4. **Profile your code**: Use pprof before deploying
-5. **Test on real devices**: Emulators don't show real performance
-
-## Common Issues
-
-### Issue: "No Android devices detected"
-
-```bash
-# Check connected devices
-adb devices
-
-# Restart adb if needed
-adb kill-server
-adb start-server
-adb devices
-```
-
-### Issue: "Installation failed"
-
-```bash
-# Uninstall old version first
-adb uninstall com.example.myapp
-
-# Reinstall
-adb install myapp.apk
-```
-
-### Issue: "App crashes immediately"
-
-```bash
-# View crash logs
-adb logcat | grep FATAL
-
-# Check for panic
-adb logcat | grep "panic:"
-```
-
-## Migration Path
-
-### From gomobile build to gomobile bind
-
-When your app outgrows gomobile build:
-
-1. **Extract core logic to library:**
-   ```go
-   // In mylib/network.go
-   package mylib
-
-   func FetchData(url string) (string, error) {
-       // Your logic
-   }
-   ```
-
-2. **Build AAR:**
-   ```bash
-   gomobile bind -target=android -o mylib.aar ./mylib
-   ```
-
-3. **Create Android project** (see **Build** workflow)
-
-4. **Use AAR from Kotlin:**
-   ```kotlin
-   import mylib.Mylib
-
-   val data = Mylib.fetchData("https://example.com")
-   ```
-
-## Next Steps
-
-- **Debug your app** - Use the **Debug** workflow
-- **Test on emulator** - Use the **Emulator** workflow
-- **Build production APK** - Use the **Build** and **Publish** workflows
-- **Add richer UI** - Consider migrating to gomobile bind + Android project
-
-## Resources
-
-- [Gomobile Documentation](https://pkg.go.dev/golang.org/x/mobile)
-- [Mobile App Package](https://pkg.go.dev/golang.org/x/mobile/app)
-- [Mobile GL Package](https://pkg.go.dev/golang.org/x/mobile/gl)
-- [Gomobile Examples](https://github.com/golang/mobile/tree/master/example)
dots/.config/claude/skills/Android/workflows/GomobileBind.md
@@ -1,485 +0,0 @@
-# GomobileBind Workflow
-
-Build Go libraries as Android AAR (Android Archive) packages using gomobile bind. This allows you to use Go code in Android apps written in Java or Kotlin.
-
-## When to Use
-
-- You have existing Go code (networking, crypto, business logic)
-- You want to integrate Go into an existing Android app
-- You need Go's concurrency or standard library in Android
-- You want to share code between backend and mobile
-
-## Prerequisites
-
-- Go installed (`go version`)
-- Android SDK and NDK installed
-- `ANDROID_HOME` environment variable set
-- Write access to the Go package you want to bind
-
-## Steps
-
-### 1. Verify Environment
-
-Check that required tools are available:
-
-```bash
-# Check Go
-go version
-
-# Check Android SDK
-echo $ANDROID_HOME
-ls $ANDROID_HOME/ndk
-
-# Check PATH includes Android tools
-which adb
-```
-
-If Android SDK is not set up, invoke the **Setup** workflow first.
-
-### 2. Install Gomobile
-
-```bash
-# Install gomobile command
-go install golang.org/x/mobile/cmd/gomobile@latest
-
-# Initialize gomobile (downloads Android toolchain)
-gomobile init
-
-# Verify installation
-gomobile version
-```
-
-**Note**: `gomobile init` downloads NDK toolchain and can take several minutes on first run.
-
-### 3. Prepare Go Package
-
-Your Go package must follow gomobile binding rules:
-
-**Good Go code for binding:**
-```go
-// File: golib/network.go
-package network
-
-import (
-    "fmt"
-    "net/http"
-)
-
-// FetchURL fetches content from a URL
-// Exported function will be available in Android
-func FetchURL(url string) (string, error) {
-    resp, err := http.Get(url)
-    if err != nil {
-        return "", err
-    }
-    defer resp.Body.Close()
-
-    // Implementation...
-    return "content", nil
-}
-
-// Config represents network configuration
-// Exported struct becomes Android class
-type Config struct {
-    Timeout int
-    BaseURL string
-}
-
-// NewConfig creates a new Config
-func NewConfig(timeout int, baseURL string) *Config {
-    return &Config{
-        Timeout: timeout,
-        BaseURL: baseURL,
-    }
-}
-
-// GetTimeout returns the timeout value
-func (c *Config) GetTimeout() int {
-    return c.Timeout
-}
-
-// Callback interface for async operations
-type Callback interface {
-    OnSuccess(data string)
-    OnError(err error)
-}
-
-// FetchAsync fetches URL asynchronously
-func FetchAsync(url string, callback Callback) {
-    go func() {
-        result, err := FetchURL(url)
-        if err != nil {
-            callback.OnError(err)
-            return
-        }
-        callback.OnSuccess(result)
-    }()
-}
-```
-
-**What works in gomobile:**
-- ✅ Exported functions with basic types
-- ✅ Exported structs with exported fields
-- ✅ Methods on exported structs
-- ✅ Interfaces (for callbacks)
-- ✅ Error return values (become exceptions)
-- ✅ Slices of basic types (`[]byte`, `[]int`, `[]string`)
-
-**What doesn't work:**
-- ❌ Maps (use structs instead)
-- ❌ Channels (use callbacks via interfaces)
-- ❌ Generics (Go 1.18+ type parameters)
-- ❌ Unexported types in function signatures
-- ❌ Variadic functions
-- ❌ Complex nested types
-
-### 4. Create go.mod
-
-```bash
-cd golib
-go mod init example.com/mylib
-go mod tidy
-```
-
-### 5. Build AAR
-
-**Basic build (all architectures):**
-```bash
-gomobile bind -target=android -o mylib.aar ./golib
-```
-
-**Build specific architectures (smaller AAR):**
-```bash
-# ARM64 only (most modern devices)
-gomobile bind -target=android/arm64 -o mylib.aar ./golib
-
-# ARM64 and ARM32
-gomobile bind -target=android/arm64,android/arm -o mylib.aar ./golib
-
-# All common architectures
-gomobile bind -target=android/arm64,android/arm,android/amd64,android/386 -o mylib.aar ./golib
-```
-
-**Build with javadoc comments:**
-```bash
-gomobile bind -target=android -javapkg=com.example.mylib -o mylib.aar ./golib
-```
-
-**Architecture options:**
-- `android/arm64`: 64-bit ARM (arm64-v8a) - modern devices
-- `android/arm`: 32-bit ARM (armeabi-v7a) - older devices
-- `android/amd64`: 64-bit x86 (x86_64) - emulators
-- `android/386`: 32-bit x86 (x86) - old emulators
-
-### 6. Integrate AAR into Android Project
-
-**Copy AAR to Android project:**
-```bash
-cp mylib.aar /path/to/android-project/app/libs/
-```
-
-**Add dependency in `app/build.gradle.kts`:**
-```kotlin
-dependencies {
-    implementation(files("libs/mylib.aar"))
-
-    // Other dependencies...
-}
-```
-
-**Sync Gradle:**
-```bash
-cd /path/to/android-project
-./gradlew sync
-```
-
-### 7. Use in Android Code
-
-**Kotlin example:**
-```kotlin
-package com.example.myapp
-
-import android.os.Bundle
-import androidx.appcompat.app.AppCompatActivity
-import golib.Network  // Import generated package
-
-class MainActivity : AppCompatActivity() {
-    override fun onCreate(savedInstanceState: Bundle?) {
-        super.onCreate(savedInstanceState)
-
-        // Call Go function
-        try {
-            val result = Network.fetchURL("https://example.com")
-            println("Result: $result")
-        } catch (e: Exception) {
-            println("Error: ${e.message}")
-        }
-
-        // Use Go struct
-        val config = Network.newConfig(30, "https://api.example.com")
-        println("Timeout: ${config.timeout}")
-
-        // Async with callback
-        Network.fetchAsync("https://example.com", object : Network.Callback {
-            override fun onSuccess(data: String) {
-                println("Success: $data")
-            }
-
-            override fun onError(err: Exception) {
-                println("Error: ${err.message}")
-            }
-        })
-    }
-}
-```
-
-**Java example:**
-```java
-package com.example.myapp;
-
-import golib.Network;
-
-public class MainActivity extends AppCompatActivity {
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-
-        try {
-            String result = Network.fetchURL("https://example.com");
-            System.out.println("Result: " + result);
-        } catch (Exception e) {
-            System.out.println("Error: " + e.getMessage());
-        }
-
-        Network.Config config = Network.newConfig(30, "https://api.example.com");
-        System.out.println("Timeout: " + config.getTimeout());
-    }
-}
-```
-
-## Type Conversion Reference
-
-| Go Type | Java/Kotlin Type | Notes |
-|---------|------------------|-------|
-| `bool` | `boolean` / `Boolean` | |
-| `int`, `int32` | `int` / `Int` | 32-bit signed |
-| `int64` | `long` / `Long` | 64-bit signed |
-| `float32` | `float` / `Float` | |
-| `float64` | `double` / `Double` | |
-| `string` | `String` | UTF-8 encoded |
-| `[]byte` | `byte[]` / `ByteArray` | |
-| `[]int` | `long[]` / `LongArray` | Note: int becomes long[] |
-| `error` | `Exception` | Go errors become Java exceptions |
-| `struct` | `class` | Exported fields become getters/setters |
-| `interface` | `interface` | Must have exported methods only |
-
-## Optimization Tips
-
-### 1. Minimize Boundary Crossings
-
-Each call from Java/Kotlin to Go has overhead. Batch operations when possible:
-
-**Bad (many crossings):**
-```go
-func ProcessItem(item string) string { ... }
-
-// Called 1000 times from Android
-for item in items {
-    ProcessItem(item)  // 1000 Go calls
-}
-```
-
-**Good (single crossing):**
-```go
-func ProcessItems(items []string) []string { ... }
-
-// Called once from Android
-ProcessItems(items)  // 1 Go call
-```
-
-### 2. Use Appropriate Architectures
-
-**For development (faster builds):**
-```bash
-gomobile bind -target=android/arm64 -o mylib.aar ./golib
-```
-
-**For release (wider compatibility):**
-```bash
-gomobile bind -target=android/arm64,android/arm -o mylib.aar ./golib
-```
-
-### 3. Keep Go API Simple
-
-- Flat function signatures (avoid nested types)
-- Use basic types when possible
-- Return errors as second value
-- Document expected behavior
-
-## Common Issues
-
-### Issue: "gomobile: command not found"
-
-**Solution:**
-```bash
-# Ensure Go bin is in PATH
-export PATH=$PATH:$(go env GOPATH)/bin
-
-# Reinstall gomobile
-go install golang.org/x/mobile/cmd/gomobile@latest
-```
-
-### Issue: "NDK not found"
-
-**Solution:**
-```bash
-# Set ANDROID_HOME
-export ANDROID_HOME=$HOME/Android/Sdk
-
-# Install NDK via sdkmanager
-sdkmanager --install "ndk;26.1.10909125"
-
-# Reinitialize gomobile
-gomobile init
-```
-
-### Issue: "Cannot use map in function signature"
-
-**Problem:**
-```go
-func BadFunc(data map[string]string) error { ... }  // Won't bind!
-```
-
-**Solution:** Use struct instead:
-```go
-type KeyValue struct {
-    Key   string
-    Value string
-}
-
-func GoodFunc(data []KeyValue) error { ... }  // Works!
-```
-
-### Issue: AAR is very large
-
-**Problem:** All architectures included by default
-
-**Solution:** Build only needed architectures:
-```bash
-# Just ARM64 (most devices)
-gomobile bind -target=android/arm64 -o mylib.aar ./golib
-
-# ARM64 + ARM (smaller than all 4)
-gomobile bind -target=android/arm64,android/arm -o mylib.aar ./golib
-```
-
-## Testing Go Code
-
-Test Go code separately before binding:
-
-```bash
-cd golib
-go test ./...
-go test -v ./...
-go test -cover ./...
-```
-
-## Automation Script
-
-Create `build-aar.sh` for consistent builds:
-
-```bash
-#!/usr/bin/env bash
-set -euo pipefail
-
-# Build AAR for Android
-echo "Building AAR..."
-
-# Set targets (can be overridden)
-TARGETS=${ANDROID_TARGETS:-"android/arm64,android/arm"}
-OUTPUT=${AAR_OUTPUT:-"mylib.aar"}
-PACKAGE=${GO_PACKAGE:-"./golib"}
-
-gomobile bind \
-    -target="$TARGETS" \
-    -o "$OUTPUT" \
-    "$PACKAGE"
-
-echo "AAR built successfully: $OUTPUT"
-
-# Copy to Android project if path is set
-if [ -n "${ANDROID_PROJECT:-}" ]; then
-    cp "$OUTPUT" "$ANDROID_PROJECT/app/libs/"
-    echo "Copied to Android project"
-fi
-```
-
-Usage:
-```bash
-chmod +x build-aar.sh
-
-# Build for ARM64 only
-ANDROID_TARGETS=android/arm64 ./build-aar.sh
-
-# Build and copy to Android project
-ANDROID_PROJECT=/path/to/android-app ./build-aar.sh
-```
-
-## Debugging Tips
-
-### 1. Check Generated Java Code
-
-Extract AAR to inspect generated Java:
-
-```bash
-unzip mylib.aar -d mylib-extracted
-cd mylib-extracted
-javap -p classes.jar  # View Java classes
-```
-
-### 2. Add Logging in Go
-
-Use Android's logging system from Go:
-
-```go
-package mylib
-
-import "log"
-
-func FetchURL(url string) (string, error) {
-    log.Printf("Fetching URL: %s", url)  // Will appear in logcat
-    // ...
-}
-```
-
-View in logcat:
-```bash
-adb logcat | grep GoLog
-```
-
-### 3. Test Type Conversions
-
-Create simple test functions to verify type conversion:
-
-```go
-func TestString(s string) string { return s }
-func TestInt(i int) int { return i }
-func TestBytes(b []byte) []byte { return b }
-```
-
-## Next Steps
-
-After successfully binding your Go library:
-
-1. **Build the Android app** - Use the **Build** workflow
-2. **Test on device/emulator** - Use the **Debug** workflow
-3. **Optimize AAR size** - Build only needed architectures
-4. **Add tests** - Use the **Test** workflow
-5. **Publish app** - Use the **Publish** workflow
-
-## Resources
-
-- [Gomobile Documentation](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile)
-- [Gomobile Wiki](https://github.com/golang/go/wiki/Mobile)
-- [Gomobile Examples](https://github.com/golang/mobile/tree/master/example)
-- [Go on Mobile Tutorial](https://github.com/golang/mobile/blob/master/README.md)
dots/.config/claude/skills/Android/workflows/Gradle.md
@@ -1,507 +0,0 @@
-# Gradle Workflow
-
-Manage Android project dependencies, build configuration, and Gradle tasks.
-
-## Gradle Basics
-
-### Gradle Wrapper
-
-Always use the wrapper for reproducible builds:
-
-```bash
-# Linux/Mac
-./gradlew <task>
-
-# Windows
-gradlew.bat <task>
-
-# Update wrapper
-./gradlew wrapper --gradle-version=8.6
-```
-
-### Project Structure
-
-```
-myproject/
-├── gradle/
-│   ├── libs.versions.toml      # Version catalog (recommended)
-│   └── wrapper/
-│       ├── gradle-wrapper.jar
-│       └── gradle-wrapper.properties
-├── app/
-│   └── build.gradle.kts        # App module build file
-├── build.gradle.kts            # Root build file
-├── settings.gradle.kts         # Project settings
-├── gradle.properties           # Global Gradle properties
-└── local.properties            # Local config (not in git)
-```
-
-## Version Catalogs (Modern Approach)
-
-### gradle/libs.versions.toml
-
-```toml
-[versions]
-agp = "8.3.0"                       # Android Gradle Plugin
-kotlin = "1.9.22"
-compileSdk = "34"
-minSdk = "24"
-targetSdk = "34"
-
-# Dependencies
-androidx-core = "1.12.0"
-androidx-appcompat = "1.6.1"
-material = "1.11.0"
-androidx-activity = "1.8.2"
-androidx-constraintlayout = "2.1.4"
-
-# Testing
-junit = "4.13.2"
-androidx-test-ext-junit = "1.1.5"
-espresso-core = "3.5.1"
-
-[libraries]
-androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core" }
-androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" }
-material = { group = "com.google.android.material", name = "material", version.ref = "material" }
-androidx-activity = { group = "androidx.activity", name = "activity", version.ref = "androidx-activity" }
-androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" }
-
-# Testing
-junit = { group = "junit", name = "junit", version.ref = "junit" }
-androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
-androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
-
-[plugins]
-android-application = { id = "com.android.application", version.ref = "agp" }
-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
-```
-
-### Root build.gradle.kts
-
-```kotlin
-// Top-level build file
-plugins {
-    alias(libs.plugins.android.application) apply false
-    alias(libs.plugins.kotlin.android) apply false
-}
-```
-
-### App build.gradle.kts
-
-```kotlin
-plugins {
-    alias(libs.plugins.android.application)
-    alias(libs.plugins.kotlin.android)
-}
-
-android {
-    namespace = "com.example.myapp"
-    compileSdk = 34
-
-    defaultConfig {
-        applicationId = "com.example.myapp"
-        minSdk = 24
-        targetSdk = 34
-        versionCode = 1
-        versionName = "1.0"
-
-        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
-    }
-
-    buildTypes {
-        release {
-            isMinifyEnabled = true
-            proguardFiles(
-                getDefaultProguardFile("proguard-android-optimize.txt"),
-                "proguard-rules.pro"
-            )
-        }
-    }
-
-    compileOptions {
-        sourceCompatibility = JavaVersion.VERSION_17
-        targetCompatibility = JavaVersion.VERSION_17
-    }
-
-    kotlinOptions {
-        jvmTarget = "17"
-    }
-}
-
-dependencies {
-    implementation(libs.androidx.core.ktx)
-    implementation(libs.androidx.appcompat)
-    implementation(libs.material)
-    implementation(libs.androidx.activity)
-    implementation(libs.androidx.constraintlayout)
-
-    testImplementation(libs.junit)
-    androidTestImplementation(libs.androidx.test.ext.junit)
-    androidTestImplementation(libs.androidx.espresso.core)
-
-    // Gomobile AAR
-    implementation(files("libs/mylib.aar"))
-}
-```
-
-## Managing Dependencies
-
-### Add Dependencies
-
-**Using version catalog (recommended):**
-
-1. Add to `gradle/libs.versions.toml`:
-```toml
-[versions]
-retrofit = "2.9.0"
-
-[libraries]
-retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
-```
-
-2. Use in `app/build.gradle.kts`:
-```kotlin
-dependencies {
-    implementation(libs.retrofit)
-}
-```
-
-**Direct declaration:**
-```kotlin
-dependencies {
-    implementation("com.squareup.retrofit2:retrofit:2.9.0")
-}
-```
-
-### Dependency Scopes
-
-```kotlin
-dependencies {
-    // Compile and runtime
-    implementation("...")      // Recommended: Not exposed to consumers
-
-    // Compile only (not in APK)
-    compileOnly("...")         // Example: annotations
-
-    // Runtime only
-    runtimeOnly("...")         // Example: JDBC drivers
-
-    // Exposed to consumers
-    api("...")                 // Use sparingly in libraries
-
-    // Testing
-    testImplementation("...")           // Unit tests (JVM)
-    androidTestImplementation("...")    // Instrumentation tests (Android)
-
-    // Debug build only
-    debugImplementation("...")          // Example: debug tools
-}
-```
-
-### Local AAR/JAR Files
-
-```kotlin
-dependencies {
-    // Single AAR
-    implementation(files("libs/mylib.aar"))
-
-    // All JARs in libs directory
-    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
-
-    // All AARs in libs directory
-    implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
-}
-```
-
-### Multi-module Dependencies
-
-```kotlin
-dependencies {
-    // Depend on another module in the project
-    implementation(project(":library"))
-}
-```
-
-## Configuration
-
-### gradle.properties
-
-```properties
-# Performance
-org.gradle.jvmargs=-Xmx4096m -XX:MaxMetaspaceSize=1024m
-org.gradle.daemon=true
-org.gradle.parallel=true
-org.gradle.caching=true
-org.gradle.configureondemand=true
-
-# AndroidX
-android.useAndroidX=true
-android.enableJetifier=true
-
-# Kotlin
-kotlin.code.style=official
-
-# Build
-android.nonTransitiveRClass=true
-android.defaults.buildfeatures.buildconfig=true
-```
-
-### local.properties
-
-```properties
-# SDK location (don't commit to git)
-sdk.dir=/home/user/Android/Sdk
-
-# NDK location (optional)
-ndk.dir=/home/user/Android/Sdk/ndk/26.1.10909125
-```
-
-## Build Variants
-
-### Build Types
-
-```kotlin
-android {
-    buildTypes {
-        debug {
-            applicationIdSuffix = ".debug"
-            isDebuggable = true
-            isMinifyEnabled = false
-        }
-
-        release {
-            isMinifyEnabled = true
-            isShrinkResources = true
-            proguardFiles(
-                getDefaultProguardFile("proguard-android-optimize.txt"),
-                "proguard-rules.pro"
-            )
-        }
-
-        create("staging") {
-            initWith(getByName("debug"))
-            applicationIdSuffix = ".staging"
-        }
-    }
-}
-```
-
-### Product Flavors
-
-```kotlin
-android {
-    flavorDimensions += "version"
-
-    productFlavors {
-        create("free") {
-            dimension = "version"
-            applicationIdSuffix = ".free"
-            versionNameSuffix = "-free"
-        }
-
-        create("paid") {
-            dimension = "version"
-            applicationIdSuffix = ".paid"
-            versionNameSuffix = "-paid"
-        }
-    }
-}
-```
-
-Build combinations:
-```bash
-./gradlew assembleFreeDebug
-./gradlew assembleFreeRelease
-./gradlew assemblePaidDebug
-./gradlew assemblePaidRelease
-```
-
-## ProGuard / R8
-
-### Enable Code Shrinking
-
-```kotlin
-android {
-    buildTypes {
-        release {
-            isMinifyEnabled = true
-            isShrinkResources = true
-            proguardFiles(
-                getDefaultProguardFile("proguard-android-optimize.txt"),
-                "proguard-rules.pro"
-            )
-        }
-    }
-}
-```
-
-### proguard-rules.pro
-
-```proguard
-# Keep gomobile generated classes
--keep class go.** { *; }
--keep class mylib.** { *; }
-
-# Keep model classes
--keep class com.example.myapp.models.** { *; }
-
-# Retrofit
--keepattributes Signature
--keepattributes Exceptions
-
-# Kotlin
--keep class kotlin.** { *; }
--keep class kotlin.Metadata { *; }
--dontwarn kotlin.**
-```
-
-## Common Tasks
-
-```bash
-# List all tasks
-./gradlew tasks
-
-# List dependencies
-./gradlew app:dependencies
-
-# Show dependency tree
-./gradlew app:dependencies --configuration debugRuntimeClasspath
-
-# Check for updates
-./gradlew dependencyUpdates
-
-# Clean build
-./gradlew clean
-
-# Build all variants
-./gradlew build
-
-# Build specific variant
-./gradlew assembleDebug
-./gradlew assembleRelease
-
-# Run tests
-./gradlew test
-./gradlew connectedAndroidTest
-
-# Install on device
-./gradlew installDebug
-```
-
-## Troubleshooting
-
-### Dependency Conflicts
-
-```bash
-# View conflict resolution
-./gradlew app:dependencies
-
-# Force specific version
-dependencies {
-    implementation("com.example:library:1.0") {
-        force = true
-    }
-}
-
-# Exclude transitive dependency
-dependencies {
-    implementation("com.example:library:1.0") {
-        exclude(group = "com.unwanted", module = "module")
-    }
-}
-```
-
-### Sync Issues
-
-```bash
-# Refresh dependencies
-./gradlew --refresh-dependencies
-
-# Clear cache
-./gradlew clean cleanBuildCache
-
-# Delete .gradle folder
-rm -rf .gradle
-./gradlew build
-```
-
-### Memory Issues
-
-Increase memory in `gradle.properties`:
-```properties
-org.gradle.jvmargs=-Xmx8192m -XX:MaxMetaspaceSize=2048m
-```
-
-## Multi-module Projects
-
-### settings.gradle.kts
-
-```kotlin
-pluginManagement {
-    repositories {
-        google()
-        mavenCentral()
-        gradlePluginPortal()
-    }
-}
-
-dependencyResolutionManagement {
-    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
-    repositories {
-        google()
-        mavenCentral()
-    }
-}
-
-rootProject.name = "MyProject"
-include(":app")
-include(":library")
-```
-
-### Library Module
-
-`library/build.gradle.kts`:
-```kotlin
-plugins {
-    alias(libs.plugins.android.library)
-    alias(libs.plugins.kotlin.android)
-}
-
-android {
-    namespace = "com.example.library"
-    compileSdk = 34
-
-    defaultConfig {
-        minSdk = 24
-    }
-}
-```
-
-Use from app:
-```kotlin
-dependencies {
-    implementation(project(":library"))
-}
-```
-
-## Optimization Tips
-
-1. **Use version catalogs**: Centralize dependency versions
-2. **Enable build cache**: Faster incremental builds
-3. **Use Gradle daemon**: Reduces startup time
-4. **Parallel execution**: Builds modules in parallel
-5. **Configure on demand**: Only configure needed projects
-6. **Avoid clean**: Let Gradle handle incremental builds
-
-## Next Steps
-
-- **Build APK/AAB** - Use the **Build** workflow
-- **Manage gomobile integration** - Use the **GomobileBind** workflow
-- **Run tests** - Use the **Test** workflow
-
-## Resources
-
-- [Gradle Plugin User Guide](https://developer.android.com/build)
-- [Version Catalogs](https://docs.gradle.org/current/userguide/platforms.html)
-- [Dependency Management](https://developer.android.com/studio/build/dependencies)
dots/.config/claude/skills/Android/workflows/Nix.md
@@ -1,517 +0,0 @@
-# Nix Workflow
-
-Configure Android development on NixOS and with Nix package manager using nixpkgs androidenv.
-
-## Why Nix for Android?
-
-**Benefits:**
-- Declarative configuration
-- Reproducible builds
-- Version pinning
-- No manual SDK management
-- Share configuration across machines
-
-**Challenges:**
-- More complex initial setup
-- Not all Android SDK versions available
-- May need to use imperative setup for some tools
-
-## NixOS System Configuration
-
-### System Packages
-
-`systems/common/programs/android.nix`:
-```nix
-{ config, pkgs, ... }:
-
-{
-  programs.adb.enable = true;  # Enable adb system-wide
-  users.users.youruser.extraGroups = [ "adbusers" ];  # Add user to adbusers
-
-  environment.systemPackages = with pkgs; [
-    android-tools  # adb, fastboot
-  ];
-}
-```
-
-### Home Manager Configuration
-
-`home/common/dev/android.nix`:
-```nix
-{ config, pkgs, ... }:
-
-let
-  # Compose Android SDK with specific components
-  androidSdk = pkgs.androidenv.composeAndroidPackages {
-    platformVersions = [ "34" "33" ];
-    buildToolsVersions = [ "34.0.0" "33.0.2" ];
-    includeNDK = true;
-    ndkVersion = "26.1.10909125";
-    includeEmulator = true;
-    includeSources = false;
-    includeSystemImages = true;
-    systemImageTypes = [ "google_apis" ];
-    abiVersions = [ "x86_64" "arm64-v8a" ];
-    cmakeVersions = [ "3.22.1" ];
-  };
-
-in
-{
-  home.packages = with pkgs; [
-    # Android SDK (composed above)
-    androidSdk.androidsdk
-
-    # Development tools
-    android-studio
-    jdk17
-
-    # Go and gomobile
-    go
-    # Note: gomobile needs to be installed via go install
-  ];
-
-  home.sessionVariables = {
-    ANDROID_HOME = "${androidSdk.androidsdk}/libexec/android-sdk";
-    ANDROID_SDK_ROOT = "${androidSdk.androidsdk}/libexec/android-sdk";
-  };
-
-  home.sessionPath = [
-    "${androidSdk.androidsdk}/libexec/android-sdk/platform-tools"
-    "${androidSdk.androidsdk}/libexec/android-sdk/tools"
-    "${androidSdk.androidsdk}/libexec/android-sdk/emulator"
-  ];
-}
-```
-
-Apply configuration:
-```bash
-# For NixOS systems
-sudo nixos-rebuild switch
-
-# For home-manager
-home-manager switch --flake .#youruser@hostname
-```
-
-## androidenv.composeAndroidPackages
-
-### Available Options
-
-```nix
-pkgs.androidenv.composeAndroidPackages {
-  # Platform versions (Android API levels)
-  platformVersions = [ "34" "33" "31" ];
-
-  # Build tools versions
-  buildToolsVersions = [ "34.0.0" "33.0.2" ];
-
-  # Include NDK
-  includeNDK = true;
-  ndkVersion = "26.1.10909125";  # Specific NDK version
-
-  # Include Android Emulator
-  includeEmulator = true;
-
-  # Include system images for emulator
-  includeSystemImages = true;
-  systemImageTypes = [ "google_apis" "google_apis_playstore" "default" ];
-  abiVersions = [ "x86_64" "arm64-v8a" "armeabi-v7a" ];
-
-  # Include platform sources
-  includeSources = false;
-
-  # Include CMake (for C++ projects)
-  cmakeVersions = [ "3.22.1" ];
-
-  # Include extras
-  includeExtras = [ "extras;google;gcm" ];
-}
-```
-
-### Minimal Configuration
-
-For basic Android development:
-
-```nix
-androidSdk = pkgs.androidenv.composeAndroidPackages {
-  platformVersions = [ "34" ];
-  buildToolsVersions = [ "34.0.0" ];
-  includeNDK = false;
-  includeEmulator = false;
-};
-```
-
-### Full Configuration
-
-For complete Android development with emulator:
-
-```nix
-androidSdk = pkgs.androidenv.composeAndroidPackages {
-  platformVersions = [ "34" "33" "31" ];
-  buildToolsVersions = [ "34.0.0" "33.0.2" ];
-  includeNDK = true;
-  ndkVersion = "26.1.10909125";
-  includeEmulator = true;
-  includeSystemImages = true;
-  systemImageTypes = [ "google_apis" ];
-  abiVersions = [ "x86_64" "arm64-v8a" ];
-  includeSources = true;
-  cmakeVersions = [ "3.22.1" ];
-};
-```
-
-## Development Shell (nix develop)
-
-Create a project-specific development shell.
-
-`flake.nix`:
-```nix
-{
-  description = "Android app with gomobile";
-
-  inputs = {
-    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
-    flake-utils.url = "github:numtide/flake-utils";
-  };
-
-  outputs = { self, nixpkgs, flake-utils }:
-    flake-utils.lib.eachDefaultSystem (system:
-      let
-        pkgs = nixpkgs.legacyPackages.${system};
-
-        androidSdk = pkgs.androidenv.composeAndroidPackages {
-          platformVersions = [ "34" ];
-          buildToolsVersions = [ "34.0.0" ];
-          includeNDK = true;
-          ndkVersion = "26.1.10909125";
-          includeEmulator = true;
-          includeSystemImages = true;
-          systemImageTypes = [ "google_apis" ];
-          abiVersions = [ "x86_64" ];
-        };
-
-      in
-      {
-        devShells.default = pkgs.mkShell {
-          buildInputs = with pkgs; [
-            androidSdk.androidsdk
-            jdk17
-            gradle
-            go
-          ];
-
-          shellHook = ''
-            export ANDROID_HOME="${androidSdk.androidsdk}/libexec/android-sdk"
-            export ANDROID_SDK_ROOT="$ANDROID_HOME"
-            export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$ANDROID_HOME/emulator:$PATH"
-
-            # Install gomobile if not already installed
-            if ! command -v gomobile &> /dev/null; then
-              echo "Installing gomobile..."
-              go install golang.org/x/mobile/cmd/gomobile@latest
-              gomobile init
-            fi
-
-            echo "Android development environment ready!"
-            echo "ANDROID_HOME: $ANDROID_HOME"
-            echo "Java: $(java -version 2>&1 | head -n 1)"
-            echo "Gradle: $(gradle --version | grep Gradle)"
-            echo "Go: $(go version)"
-          '';
-        };
-      }
-    );
-}
-```
-
-Enter development shell:
-```bash
-nix develop
-
-# Or use direnv for automatic activation
-echo "use flake" > .envrc
-direnv allow
-```
-
-## Gomobile with Nix
-
-### Install Gomobile
-
-Gomobile isn't packaged in nixpkgs, so install via Go:
-
-```bash
-# In nix shell
-go install golang.org/x/mobile/cmd/gomobile@latest
-gomobile init
-```
-
-### Build Script
-
-`build-aar.sh`:
-```bash
-#!/usr/bin/env nix-shell
-#! nix-shell -i bash -p go android-tools
-
-set -euo pipefail
-
-# Install gomobile if needed
-if ! command -v gomobile &> /dev/null; then
-    go install golang.org/x/mobile/cmd/gomobile@latest
-    gomobile init
-fi
-
-# Build AAR
-cd golib
-gomobile bind -target=android -o mylib.aar .
-cp mylib.aar ../app/libs/
-
-echo "AAR built and copied to app/libs/"
-```
-
-Make executable and run:
-```bash
-chmod +x build-aar.sh
-./build-aar.sh
-```
-
-## Android Emulator with Nix
-
-### Create AVD
-
-```bash
-# Enter nix shell with Android SDK
-nix develop
-
-# List available system images
-ls $ANDROID_HOME/system-images/
-
-# Create AVD
-avdmanager create avd \
-    -n NixPixel \
-    -k "system-images;android-34;google_apis;x86_64" \
-    -d "pixel_7"
-
-# Start emulator
-emulator -avd NixPixel
-```
-
-### Emulator Issues on NixOS
-
-**Graphics acceleration:**
-
-```nix
-# System configuration for KVM support
-virtualisation.libvirtd.enable = true;
-users.users.youruser.extraGroups = [ "libvirtd" "kvm" ];
-
-# Or use hardware.opengl
-hardware.opengl.enable = true;
-hardware.opengl.driSupport = true;
-hardware.opengl.driSupport32Bit = true;
-```
-
-**If emulator fails to start:**
-
-```bash
-# Use software rendering
-emulator -avd NixPixel -gpu swiftshader_indirect
-
-# Or check emulator log
-emulator -avd NixPixel -verbose
-```
-
-## Gradle with Nix
-
-### Using Nix-provided Gradle
-
-```nix
-devShells.default = pkgs.mkShell {
-  buildInputs = with pkgs; [
-    gradle
-    jdk17
-  ];
-};
-```
-
-### Using Gradle Wrapper (Recommended)
-
-Gradle wrapper is more portable:
-
-```bash
-# Generate wrapper
-gradle wrapper --gradle-version=8.6
-
-# Use wrapper (works without nix shell)
-./gradlew build
-```
-
-## Building Android App with Nix
-
-### Simple Build
-
-`default.nix`:
-```nix
-{ pkgs ? import <nixpkgs> {} }:
-
-let
-  androidSdk = pkgs.androidenv.composeAndroidPackages {
-    platformVersions = [ "34" ];
-    buildToolsVersions = [ "34.0.0" ];
-    includeNDK = true;
-    ndkVersion = "26.1.10909125";
-  };
-
-in
-pkgs.stdenv.mkDerivation {
-  pname = "myapp";
-  version = "1.0";
-
-  src = ./.;
-
-  buildInputs = with pkgs; [
-    androidSdk.androidsdk
-    jdk17
-    gradle
-    go
-  ];
-
-  buildPhase = ''
-    export ANDROID_HOME="${androidSdk.androidsdk}/libexec/android-sdk"
-    export GRADLE_USER_HOME="$PWD/.gradle"
-
-    # Build gomobile AAR
-    cd golib
-    ${pkgs.go}/bin/go install golang.org/x/mobile/cmd/gomobile@latest
-    export PATH="$HOME/go/bin:$PATH"
-    gomobile bind -target=android -o mylib.aar .
-    cp mylib.aar ../app/libs/
-    cd ..
-
-    # Build APK
-    gradle assembleDebug --no-daemon
-  '';
-
-  installPhase = ''
-    mkdir -p $out
-    cp app/build/outputs/apk/debug/app-debug.apk $out/
-  '';
-}
-```
-
-Build:
-```bash
-nix-build
-# APK at: result/app-debug.apk
-```
-
-## Troubleshooting
-
-### ANDROID_HOME not set
-
-```bash
-# Check variable
-echo $ANDROID_HOME
-
-# Should point to:
-/nix/store/.../libexec/android-sdk
-
-# If not set, add to shell:
-export ANDROID_HOME="$(nix-build '<nixpkgs>' -A androidenv.composeAndroidPackages {...} --no-out-link)/libexec/android-sdk"
-```
-
-### adb permission denied
-
-```bash
-# On NixOS, enable adb
-# In configuration.nix:
-programs.adb.enable = true;
-users.users.youruser.extraGroups = [ "adbusers" ];
-
-# Rebuild
-sudo nixos-rebuild switch
-
-# Log out and back in
-```
-
-### Gradle can't find Android SDK
-
-Create `local.properties`:
-```bash
-echo "sdk.dir=$ANDROID_HOME" > local.properties
-```
-
-### NDK not found by gomobile
-
-```bash
-# Check NDK location
-ls $ANDROID_HOME/ndk/
-
-# gomobile init should find it automatically
-gomobile init
-```
-
-## Hybrid Approach (Recommended)
-
-Use Nix for system tools, imperative for SDK:
-
-**System (Nix):**
-```nix
-environment.systemPackages = with pkgs; [
-  android-tools  # adb, fastboot
-  jdk17
-  gradle
-  go
-];
-```
-
-**SDK (Imperative):**
-```bash
-# Install SDK manually
-mkdir -p ~/Android/Sdk
-# Download cmdline-tools
-# Use sdkmanager for packages
-```
-
-This provides flexibility while keeping tools reproducible.
-
-## Integration with this Repository
-
-In your home repository, add:
-
-`home/common/dev/android.nix`:
-```nix
-{ config, pkgs, lib, ... }:
-
-{
-  home.packages = with pkgs; [
-    android-tools
-    android-studio  # Optional
-    jdk17
-  ];
-
-  # Add to shell init
-  programs.zsh.initExtra = lib.mkAfter ''
-    # Android SDK (if using manual installation)
-    export ANDROID_HOME=$HOME/Android/Sdk
-    export PATH=$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH
-  '';
-}
-```
-
-Import in your home configuration:
-```nix
-imports = [
-  ./common/dev/android.nix
-];
-```
-
-## Next Steps
-
-- **Build Android app** - Use the **Build** workflow
-- **Setup emulator** - Use the **Emulator** workflow
-- **Integrate gomobile** - Use the **GomobileBind** workflow
-
-## Resources
-
-- [Nixpkgs Android Documentation](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/mobile/androidenv/README.md)
-- [androidenv Reference](https://nixos.org/manual/nixpkgs/stable/#android)
-- [Nix Android Examples](https://github.com/NixOS/nixpkgs/tree/master/pkgs/development/mobile/androidenv/examples)
dots/.config/claude/skills/Android/workflows/Publish.md
@@ -1,492 +0,0 @@
-# Publish Workflow
-
-Sign, build, and publish Android applications to Google Play Store.
-
-## Overview
-
-Steps to publish:
-1. Create signing key (keystore)
-2. Configure signing in Gradle
-3. Build release AAB
-4. Test release build
-5. Upload to Play Console
-6. Complete store listing
-7. Submit for review
-
-## Create Signing Key
-
-### Generate Keystore
-
-```bash
-keytool -genkey -v \
-    -keystore release.keystore \
-    -alias myapp-key \
-    -keyalg RSA \
-    -keysize 2048 \
-    -validity 10000
-
-# You'll be prompted for:
-# - Keystore password
-# - Key password
-# - Name, organization, etc.
-```
-
-**Important:**
-- **NEVER commit keystore to git**
-- **Backup keystore securely** (losing it means you can't update your app)
-- **Remember passwords** (write them down securely)
-
-### Keystore Information
-
-View keystore details:
-```bash
-keytool -list -v -keystore release.keystore
-```
-
-### Alternative: Use Play App Signing
-
-Google can manage signing for you:
-1. Google generates and stores the key
-2. You upload signed AAB
-3. Google re-signs with production key
-4. Safer but requires Play App Signing enrollment
-
-## Configure Gradle Signing
-
-### Option 1: Environment Variables (Recommended)
-
-**Store credentials outside repository:**
-
-`app/build.gradle.kts`:
-```kotlin
-android {
-    signingConfigs {
-        create("release") {
-            storeFile = file("../release.keystore")
-            storePassword = System.getenv("KEYSTORE_PASSWORD")
-            keyAlias = "myapp-key"
-            keyPassword = System.getenv("KEY_PASSWORD")
-        }
-    }
-
-    buildTypes {
-        release {
-            signingConfig = signingConfigs.getByName("release")
-            isMinifyEnabled = true
-            isShrinkResources = true
-            proguardFiles(
-                getDefaultProguardFile("proguard-android-optimize.txt"),
-                "proguard-rules.pro"
-            )
-        }
-    }
-}
-```
-
-**Build with environment variables:**
-```bash
-export KEYSTORE_PASSWORD="your-keystore-password"
-export KEY_PASSWORD="your-key-password"
-./gradlew bundleRelease
-```
-
-### Option 2: gradle.properties (Local only)
-
-**Add to `~/.gradle/gradle.properties` (NOT project gradle.properties):**
-```properties
-MYAPP_RELEASE_STORE_FILE=../release.keystore
-MYAPP_RELEASE_KEY_ALIAS=myapp-key
-MYAPP_RELEASE_STORE_PASSWORD=your-keystore-password
-MYAPP_RELEASE_KEY_PASSWORD=your-key-password
-```
-
-`app/build.gradle.kts`:
-```kotlin
-android {
-    signingConfigs {
-        create("release") {
-            storeFile = file(project.properties["MYAPP_RELEASE_STORE_FILE"] as String)
-            storePassword = project.properties["MYAPP_RELEASE_STORE_PASSWORD"] as String
-            keyAlias = project.properties["MYAPP_RELEASE_KEY_ALIAS"] as String
-            keyPassword = project.properties["MYAPP_RELEASE_KEY_PASSWORD"] as String
-        }
-    }
-
-    buildTypes {
-        release {
-            signingConfig = signingConfigs.getByName("release")
-        }
-    }
-}
-```
-
-## Build Release
-
-### Build AAB (Play Store)
-
-```bash
-./gradlew bundleRelease
-
-# Output location
-ls app/build/outputs/bundle/release/app-release.aab
-```
-
-### Build APK (Direct distribution)
-
-```bash
-./gradlew assembleRelease
-
-# Output location
-ls app/build/outputs/apk/release/app-release.apk
-```
-
-### Verify Signature
-
-```bash
-# Check AAB signature
-jarsigner -verify -verbose app-release.aab
-
-# Check APK signature
-apksigner verify --verbose app-release.apk
-```
-
-## Test Release Build
-
-**Important: Test before uploading!**
-
-### Install Release APK
-
-```bash
-# Build APK from AAB (using bundletool)
-# Download bundletool from:
-# https://github.com/google/bundletool/releases
-
-# Generate APKs
-java -jar bundletool-all.jar build-apks \
-    --bundle=app-release.aab \
-    --output=app.apks \
-    --mode=universal
-
-# Extract universal APK
-unzip app.apks universal.apk
-
-# Install
-adb install universal.apk
-```
-
-### Test Checklist
-
-- [ ] App installs successfully
-- [ ] App launches without crashes
-- [ ] All features work correctly
-- [ ] ProGuard hasn't broken anything
-- [ ] Gomobile integration works
-- [ ] Network requests succeed
-- [ ] UI looks correct
-- [ ] Performance is acceptable
-
-## Upload to Play Console
-
-### Prerequisites
-
-1. **Google Play Developer account** ($25 one-time fee)
-2. **App created in Play Console**
-3. **Store listing completed**
-
-### Upload AAB
-
-1. Go to [Play Console](https://play.google.com/console)
-2. Select your app
-3. Production → Releases
-4. Create new release
-5. Upload AAB file
-6. Fill release notes
-7. Review and rollout
-
-### Release Tracks
-
-| Track | Purpose | Audience |
-|-------|---------|----------|
-| Internal testing | Quick testing | Up to 100 testers |
-| Closed testing | Alpha/beta | Invited testers |
-| Open testing | Public beta | Anyone can join |
-| Production | Public release | All users |
-
-## Versioning
-
-### Version Code
-
-Monotonically increasing integer:
-```kotlin
-android {
-    defaultConfig {
-        versionCode = 1  // Increment for each release
-        versionName = "1.0"
-    }
-}
-```
-
-### Version Name
-
-Human-readable version string:
-```
-1.0.0 - Major.Minor.Patch (Semantic Versioning)
-```
-
-### Automated Versioning
-
-```kotlin
-// Read version from git tags
-fun getVersionCode(): Int {
-    val process = Runtime.getRuntime().exec("git rev-list --count HEAD")
-    return process.inputStream.bufferedReader().readText().trim().toInt()
-}
-
-fun getVersionName(): String {
-    val process = Runtime.getRuntime().exec("git describe --tags --always")
-    return process.inputStream.bufferedReader().readText().trim()
-}
-
-android {
-    defaultConfig {
-        versionCode = getVersionCode()
-        versionName = getVersionName()
-    }
-}
-```
-
-## Store Listing
-
-### Required Assets
-
-**Screenshots:**
-- Minimum 2 screenshots
-- Recommended: 4-8 screenshots
-- Resolutions: phone, tablet, TV, Wear OS
-
-**Icon:**
-- 512x512 PNG
-- 32-bit color + alpha channel
-
-**Feature Graphic:**
-- 1024x500 PNG
-- Displayed in Play Store
-
-### Required Information
-
-- App name
-- Short description (80 characters)
-- Full description (4000 characters)
-- Category
-- Content rating (via questionnaire)
-- Privacy policy URL (if app collects data)
-- Contact email
-
-## CI/CD Publishing
-
-### GitHub Actions Example
-
-`.github/workflows/release.yml`:
-```yaml
-name: Release
-
-on:
-  push:
-    tags:
-      - 'v*'
-
-jobs:
-  release:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v4
-
-      - name: Set up JDK 17
-        uses: actions/setup-java@v4
-        with:
-          distribution: 'temurin'
-          java-version: '17'
-
-      - name: Setup Go
-        uses: actions/setup-go@v5
-        with:
-          go-version: '1.22'
-
-      - name: Build gomobile AAR
-        run: |
-          go install golang.org/x/mobile/cmd/gomobile@latest
-          gomobile init
-          cd golib
-          gomobile bind -target=android -o mylib.aar .
-          cp mylib.aar ../app/libs/
-
-      - name: Build Release AAB
-        env:
-          KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
-          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
-        run: |
-          echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 -d > release.keystore
-          ./gradlew bundleRelease
-
-      - name: Upload to Play Store
-        uses: r0adkll/upload-google-play@v1
-        with:
-          serviceAccountJsonPlainText: ${{ secrets.SERVICE_ACCOUNT_JSON }}
-          packageName: com.example.myapp
-          releaseFiles: app/build/outputs/bundle/release/app-release.aab
-          track: production
-          status: completed
-```
-
-**Setup secrets in GitHub:**
-1. Settings → Secrets → Actions
-2. Add:
-   - `KEYSTORE_PASSWORD`
-   - `KEY_PASSWORD`
-   - `KEYSTORE_BASE64` (base64 encoded keystore file)
-   - `SERVICE_ACCOUNT_JSON` (Play Console API credentials)
-
-### Get Play Console API Credentials
-
-1. Google Cloud Console → Create Service Account
-2. Download JSON key
-3. Play Console → API access
-4. Link service account
-5. Grant permissions
-
-## Post-Release
-
-### Monitor Crashes
-
-- Play Console → Quality → Crashes
-- View crash reports and ANR (Application Not Responding) reports
-- Consider using Firebase Crashlytics for detailed reports
-
-### Monitor Reviews
-
-- Respond to user reviews
-- Address common issues in updates
-
-### Track Metrics
-
-- Installs
-- Uninstalls
-- Active users
-- Retention rates
-- Crashes
-
-## Update Releases
-
-### Prepare Update
-
-1. Fix bugs / add features
-2. Increment `versionCode`
-3. Update `versionName`
-4. Update release notes
-
-### Release Process
-
-```bash
-# Build new release
-./gradlew bundleRelease
-
-# Test thoroughly
-# Upload to Play Console
-# Submit for review
-```
-
-### Staged Rollout (Recommended)
-
-Release to percentage of users:
-1. Start with 5-10%
-2. Monitor crashes/issues
-3. Increase to 25%, 50%, 100%
-
-If issues found:
-- Halt rollout
-- Fix and release new version
-
-## Best Practices
-
-### Security
-
-- ✅ Enable ProGuard/R8
-- ✅ Remove logging in release builds
-- ✅ Validate all inputs
-- ✅ Use HTTPS for network
-- ✅ Store keystore securely
-- ✅ Use Play App Signing
-- ❌ Never commit keystore passwords
-
-### Build Configuration
-
-```kotlin
-buildTypes {
-    release {
-        isMinifyEnabled = true
-        isShrinkResources = true
-        proguardFiles(...)
-
-        // Remove debug code
-        buildConfigField("Boolean", "DEBUG_MODE", "false")
-
-        // Disable logging
-        buildConfigField("Boolean", "ENABLE_LOGGING", "false")
-    }
-
-    debug {
-        applicationIdSuffix = ".debug"
-        isDebuggable = true
-        buildConfigField("Boolean", "DEBUG_MODE", "true")
-        buildConfigField("Boolean", "ENABLE_LOGGING", "true")
-    }
-}
-```
-
-### Testing
-
-- Test release build on multiple devices
-- Test all features thoroughly
-- Check ProGuard hasn't broken anything
-- Verify gomobile integration
-- Test with poor network conditions
-- Test edge cases
-
-## Troubleshooting
-
-### "App not signed" error
-
-```bash
-# Check signing configuration
-./gradlew :app:signingReport
-
-# Verify keystore exists and credentials are correct
-```
-
-### ProGuard breaks app
-
-Add keep rules in `proguard-rules.pro`:
-```proguard
--keep class com.your.package.** { *; }
-```
-
-### Upload rejected by Play Console
-
-Common reasons:
-- Version code not incremented
-- Signature mismatch
-- Missing required permissions
-- Policy violations
-
-## Next Steps
-
-- **Monitor app performance** - Play Console metrics
-- **Respond to user feedback** - Reviews and ratings
-- **Plan updates** - New features and bug fixes
-
-## Resources
-
-- [Publish Your App](https://developer.android.com/studio/publish)
-- [Play Console](https://play.google.com/console)
-- [App Signing](https://developer.android.com/studio/publish/app-signing)
-- [Launch Checklist](https://developer.android.com/distribute/best-practices/launch/launch-checklist)
dots/.config/claude/skills/Android/workflows/Setup.md
@@ -1,26 +1,19 @@
 # Setup Workflow
 
-Configure Android development environment including SDK, NDK, command-line tools, and necessary environment variables.
+Configure Android development environment including SDK, NDK, command-line tools, and gomobile for both traditional and NixOS setups.
 
-## Installation Options
+## Installation Methods
 
-### Option 1: Android Studio (Recommended for beginners)
+Choose based on your system:
+- **Traditional Linux/Mac/Windows**: Command-line tools or Android Studio
+- **NixOS/Nix**: Declarative configuration with androidenv
+- **Android Studio**: GUI-based (easiest for beginners)
 
-**Pros**: GUI, automatic SDK management, emulator, IDE
-**Cons**: Large download (~1GB), more resources
+## Traditional Setup (Linux/Mac/Windows)
 
-**Steps:**
-1. Download [Android Studio](https://developer.android.com/studio)
-2. Install and launch
-3. Follow setup wizard to install SDK and tools
-4. SDK installed at: `~/Android/Sdk` (Linux/Mac) or `C:\Users\<user>\AppData\Local\Android\Sdk` (Windows)
+### Option 1: Command-line Tools (Recommended)
 
-### Option 2: Command-line Tools (Recommended for this setup)
-
-**Pros**: Lightweight, scriptable, no GUI
-**Cons**: Manual configuration, no IDE
-
-**Steps:**
+Lightweight, scriptable setup without Android Studio.
 
 #### 1. Download Command-line Tools
 
@@ -75,7 +68,7 @@ Type `y` and press Enter for each license.
 # Platform tools (adb, fastboot)
 sdkmanager "platform-tools"
 
-# Latest Android platform (API 34 as of 2025)
+# Latest Android platform (API 34 as of 2026)
 sdkmanager "platforms;android-34"
 
 # Build tools
@@ -105,11 +98,182 @@ echo $ANDROID_HOME
 ls $ANDROID_HOME
 ```
 
-### Option 3: NixOS / Nix Package Manager
+### Option 2: Android Studio
 
-See the **Nix** workflow for NixOS-specific setup.
+**Pros**: GUI, automatic SDK management, emulator, IDE
+**Cons**: Large download (~1GB), more resources
 
-## Post-Installation
+**Steps:**
+1. Download [Android Studio](https://developer.android.com/studio)
+2. Install and launch
+3. Follow setup wizard to install SDK and tools
+4. SDK installed at: `~/Android/Sdk` (Linux/Mac) or `C:\Users\<user>\AppData\Local\Android\Sdk` (Windows)
+
+## NixOS / Nix Setup
+
+### Why Nix for Android?
+
+**Benefits:**
+- Declarative configuration
+- Reproducible builds
+- Version pinning
+- No manual SDK management
+
+**Challenges:**
+- More complex initial setup
+- Not all Android SDK versions available
+
+### System Configuration (NixOS)
+
+`systems/common/programs/android.nix`:
+```nix
+{ config, pkgs, ... }:
+
+{
+  programs.adb.enable = true;
+  users.users.youruser.extraGroups = [ "adbusers" ];
+
+  environment.systemPackages = with pkgs; [
+    android-tools  # adb, fastboot
+  ];
+}
+```
+
+Apply configuration:
+```bash
+sudo nixos-rebuild switch
+```
+
+### Home Manager Configuration
+
+`home/common/dev/android.nix`:
+```nix
+{ config, pkgs, ... }:
+
+let
+  androidSdk = pkgs.androidenv.composeAndroidPackages {
+    platformVersions = [ "34" "33" ];
+    buildToolsVersions = [ "34.0.0" ];
+    includeNDK = true;
+    ndkVersion = "26.1.10909125";
+    includeEmulator = true;
+    includeSystemImages = true;
+    systemImageTypes = [ "google_apis" ];
+    abiVersions = [ "x86_64" "arm64-v8a" ];
+  };
+
+in
+{
+  home.packages = with pkgs; [
+    androidSdk.androidsdk
+    jdk17
+    go
+  ];
+
+  home.sessionVariables = {
+    ANDROID_HOME = "${androidSdk.androidsdk}/libexec/android-sdk";
+    ANDROID_SDK_ROOT = "${androidSdk.androidsdk}/libexec/android-sdk";
+  };
+
+  home.sessionPath = [
+    "${androidSdk.androidsdk}/libexec/android-sdk/platform-tools"
+    "${androidSdk.androidsdk}/libexec/android-sdk/tools"
+    "${androidSdk.androidsdk}/libexec/android-sdk/emulator"
+  ];
+}
+```
+
+Apply configuration:
+```bash
+home-manager switch --flake .#youruser@hostname
+```
+
+### Nix Development Shell (Project-specific)
+
+Create `flake.nix` in your project:
+
+```nix
+{
+  description = "Android development environment";
+
+  inputs = {
+    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+    flake-utils.url = "github:numtide/flake-utils";
+  };
+
+  outputs = { self, nixpkgs, flake-utils }:
+    flake-utils.lib.eachDefaultSystem (system:
+      let
+        pkgs = nixpkgs.legacyPackages.${system};
+
+        androidSdk = pkgs.androidenv.composeAndroidPackages {
+          platformVersions = [ "34" ];
+          buildToolsVersions = [ "34.0.0" ];
+          includeNDK = true;
+          ndkVersion = "26.1.10909125";
+        };
+
+      in
+      {
+        devShells.default = pkgs.mkShell {
+          buildInputs = with pkgs; [
+            androidSdk.androidsdk
+            jdk17
+            gradle
+            go
+          ];
+
+          shellHook = ''
+            export ANDROID_HOME="${androidSdk.androidsdk}/libexec/android-sdk"
+            export ANDROID_SDK_ROOT="$ANDROID_HOME"
+            export PATH="$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools:$PATH"
+
+            echo "Android development environment ready!"
+            echo "ANDROID_HOME: $ANDROID_HOME"
+          '';
+        };
+      }
+    );
+}
+```
+
+Enter development shell:
+```bash
+nix develop
+
+# Or use direnv for automatic activation
+echo "use flake" > .envrc
+direnv allow
+```
+
+### Nix androidenv Options
+
+```nix
+pkgs.androidenv.composeAndroidPackages {
+  # Platform versions (Android API levels)
+  platformVersions = [ "34" "33" "31" ];
+
+  # Build tools versions
+  buildToolsVersions = [ "34.0.0" ];
+
+  # Include NDK
+  includeNDK = true;
+  ndkVersion = "26.1.10909125";
+
+  # Include Android Emulator
+  includeEmulator = true;
+
+  # Include system images for emulator
+  includeSystemImages = true;
+  systemImageTypes = [ "google_apis" "google_apis_playstore" ];
+  abiVersions = [ "x86_64" "arm64-v8a" ];
+
+  # Include CMake (for C++ projects)
+  cmakeVersions = [ "3.22.1" ];
+}
+```
+
+## Post-Installation (All Methods)
 
 ### Install Gomobile
 
@@ -120,7 +284,7 @@ gomobile init
 
 This downloads additional toolchains for cross-compilation.
 
-### Configure Gradle (if using Android projects)
+### Configure Gradle
 
 Create `~/.gradle/gradle.properties`:
 
@@ -144,7 +308,7 @@ android.enableJetifier=true
 
 ### Setup Device for Development
 
-#### Enable Developer Options on Android Device
+#### Enable Developer Options
 
 1. Go to **Settings** → **About phone**
 2. Tap **Build number** 7 times
@@ -264,9 +428,27 @@ adb start-server
 # Try different USB port
 ```
 
+### NixOS: adb permission denied
+
+```bash
+# Add user to adbusers group (in configuration.nix)
+programs.adb.enable = true;
+users.users.youruser.extraGroups = [ "adbusers" ];
+
+# Rebuild and log out/in
+sudo nixos-rebuild switch
+```
+
+### Nix: Gradle can't find Android SDK
+
+```bash
+# Create local.properties
+echo "sdk.dir=$ANDROID_HOME" > local.properties
+```
+
 ## Updating Components
 
-### Update SDK components
+### Traditional Setup
 
 ```bash
 # List installed and available packages
@@ -280,6 +462,16 @@ sdkmanager "platforms;android-35"
 sdkmanager "build-tools;35.0.0"
 ```
 
+### Nix Setup
+
+```bash
+# Update flake inputs
+nix flake update
+
+# Rebuild configuration
+nixos-rebuild switch  # or home-manager switch
+```
+
 ### Update Gomobile
 
 ```bash
@@ -292,9 +484,8 @@ gomobile init
 After successful setup:
 
 - **Build Android app** - Use the **Build** workflow
-- **Create emulator** - Use the **Emulator** workflow
-- **Build Go library** - Use the **GomobileBind** workflow
 - **Debug app** - Use the **Debug** workflow
+- **Create emulator** - See emulator section in **Debug** workflow
 
 ## Resources
 
@@ -302,3 +493,4 @@ After successful setup:
 - [sdkmanager Reference](https://developer.android.com/studio/command-line/sdkmanager)
 - [ADB Documentation](https://developer.android.com/studio/command-line/adb)
 - [Gomobile Installation](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile)
+- [Nixpkgs Android Documentation](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/mobile/androidenv/README.md)
dots/.config/claude/skills/Android/workflows/Test.md
@@ -1,536 +0,0 @@
-# Test Workflow
-
-Write and run tests for Android applications: unit tests, instrumentation tests, and UI tests.
-
-## Test Types
-
-| Type | Runs On | Speed | Use For |
-|------|---------|-------|---------|
-| Unit Tests | JVM | Fast | Business logic, utilities |
-| Instrumentation Tests | Android device/emulator | Slow | Android APIs, database |
-| UI Tests | Android device/emulator | Slowest | User interface, interactions |
-
-## Unit Tests (Local Tests)
-
-### Location
-
-`app/src/test/java/com/example/myapp/`
-
-### Dependencies
-
-`app/build.gradle.kts`:
-```kotlin
-dependencies {
-    testImplementation("junit:junit:4.13.2")
-    testImplementation("org.mockito:mockito-core:5.7.0")
-    testImplementation("org.mockito.kotlin:mockito-kotlin:5.2.1")
-}
-```
-
-### Example Unit Test
-
-```kotlin
-// app/src/test/java/com/example/myapp/CalculatorTest.kt
-package com.example.myapp
-
-import org.junit.Test
-import org.junit.Assert.*
-
-class CalculatorTest {
-
-    @Test
-    fun addition_isCorrect() {
-        val calculator = Calculator()
-        val result = calculator.add(2, 3)
-        assertEquals(5, result)
-    }
-
-    @Test
-    fun division_byZero_throwsException() {
-        val calculator = Calculator()
-        assertThrows(ArithmeticException::class.java) {
-            calculator.divide(10, 0)
-        }
-    }
-}
-```
-
-### Run Unit Tests
-
-```bash
-# All unit tests
-./gradlew test
-
-# Specific variant
-./gradlew testDebug
-./gradlew testRelease
-
-# Specific test class
-./gradlew test --tests CalculatorTest
-
-# Specific test method
-./gradlew test --tests CalculatorTest.addition_isCorrect
-
-# With coverage report
-./gradlew testDebugUnitTest jacocoTestReport
-```
-
-### View Results
-
-```bash
-# HTML report location
-open app/build/reports/tests/testDebugUnitTest/index.html
-
-# Coverage report
-open app/build/reports/jacoco/jacocoTestReport/html/index.html
-```
-
-## Instrumentation Tests
-
-### Location
-
-`app/src/androidTest/java/com/example/myapp/`
-
-### Dependencies
-
-`app/build.gradle.kts`:
-```kotlin
-android {
-    defaultConfig {
-        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
-    }
-}
-
-dependencies {
-    androidTestImplementation("androidx.test.ext:junit:1.1.5")
-    androidTestImplementation("androidx.test:runner:1.5.2")
-    androidTestImplementation("androidx.test:rules:1.5.0")
-    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
-}
-```
-
-### Example Instrumentation Test
-
-```kotlin
-// app/src/androidTest/java/com/example/myapp/DatabaseTest.kt
-package com.example.myapp
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.platform.app.InstrumentationRegistry
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.Assert.*
-
-@RunWith(AndroidJUnit4::class)
-class DatabaseTest {
-
-    @Test
-    fun useAppContext() {
-        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
-        assertEquals("com.example.myapp", appContext.packageName)
-    }
-
-    @Test
-    fun database_insertAndQuery() {
-        val appContext = InstrumentationRegistry.getInstrumentation().targetContext
-        // Test database operations
-    }
-}
-```
-
-### Run Instrumentation Tests
-
-```bash
-# Connect device/emulator first
-adb devices
-
-# All instrumentation tests
-./gradlew connectedAndroidTest
-
-# Specific variant
-./gradlew connectedDebugAndroidTest
-
-# Specific test
-./gradlew connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.class=com.example.myapp.DatabaseTest
-```
-
-## UI Tests (Espresso)
-
-### Dependencies
-
-```kotlin
-dependencies {
-    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
-    androidTestImplementation("androidx.test.espresso:espresso-intents:3.5.1")
-    androidTestImplementation("androidx.test.espresso:espresso-contrib:3.5.1")
-}
-```
-
-### Example UI Test
-
-```kotlin
-// app/src/androidTest/java/com/example/myapp/MainActivityTest.kt
-package com.example.myapp
-
-import androidx.test.ext.junit.rules.ActivityScenarioRule
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import androidx.test.espresso.Espresso.onView
-import androidx.test.espresso.action.ViewActions.*
-import androidx.test.espresso.assertion.ViewAssertions.matches
-import androidx.test.espresso.matcher.ViewMatchers.*
-import org.junit.Rule
-import org.junit.Test
-import org.junit.runner.RunWith
-
-@RunWith(AndroidJUnit4::class)
-class MainActivityTest {
-
-    @get:Rule
-    val activityRule = ActivityScenarioRule(MainActivity::class.java)
-
-    @Test
-    fun button_click_showsText() {
-        // Type text in EditText
-        onView(withId(R.id.editText))
-            .perform(typeText("Hello"), closeSoftKeyboard())
-
-        // Click button
-        onView(withId(R.id.button))
-            .perform(click())
-
-        // Verify text is displayed
-        onView(withId(R.id.textView))
-            .check(matches(withText("Hello")))
-    }
-
-    @Test
-    fun recyclerView_scrollAndClick() {
-        // Scroll to position
-        onView(withId(R.id.recyclerView))
-            .perform(scrollToPosition<RecyclerView.ViewHolder>(10))
-
-        // Click item
-        onView(withText("Item 10"))
-            .perform(click())
-    }
-}
-```
-
-### Common Espresso Operations
-
-```kotlin
-// Find views
-onView(withId(R.id.viewId))
-onView(withText("text"))
-onView(withContentDescription("description"))
-
-// Actions
-perform(click())
-perform(typeText("text"))
-perform(replaceText("new text"))
-perform(clearText())
-perform(closeSoftKeyboard())
-perform(swipeLeft())
-perform(swipeRight())
-perform(scrollTo())
-
-// Assertions
-check(matches(isDisplayed()))
-check(matches(withText("expected")))
-check(matches(isEnabled()))
-check(matches(isChecked()))
-check(doesNotExist())
-```
-
-## Testing Gomobile Integration
-
-### Unit Test Go Code (Before binding)
-
-```bash
-cd golib
-go test ./...
-go test -v ./...
-go test -cover ./...
-```
-
-### Test AAR Integration
-
-```kotlin
-// app/src/androidTest/java/com/example/myapp/GomobileTest.kt
-package com.example.myapp
-
-import androidx.test.ext.junit.runners.AndroidJUnit4
-import mylib.Mylib  // Import gomobile generated package
-import org.junit.Test
-import org.junit.runner.RunWith
-import org.junit.Assert.*
-
-@RunWith(AndroidJUnit4::class)
-class GomobileTest {
-
-    @Test
-    fun goFunction_returnsExpectedResult() {
-        val result = Mylib.fetchURL("https://example.com")
-        assertNotNull(result)
-        assertTrue(result.isNotEmpty())
-    }
-
-    @Test
-    fun goFunction_handlesError() {
-        try {
-            Mylib.fetchURL("invalid-url")
-            fail("Should have thrown exception")
-        } catch (e: Exception) {
-            // Expected
-            assertTrue(e.message?.contains("error") == true)
-        }
-    }
-}
-```
-
-## Test Configuration
-
-### JaCoCo (Code Coverage)
-
-`app/build.gradle.kts`:
-```kotlin
-plugins {
-    id("jacoco")
-}
-
-android {
-    buildTypes {
-        debug {
-            enableAndroidTestCoverage = true
-            enableUnitTestCoverage = true
-        }
-    }
-}
-
-tasks.register<JacocoReport>("jacocoTestReport") {
-    dependsOn("testDebugUnitTest", "createDebugCoverageReport")
-
-    reports {
-        xml.required.set(true)
-        html.required.set(true)
-    }
-
-    val fileFilter = listOf(
-        "**/R.class",
-        "**/R$*.class",
-        "**/BuildConfig.*",
-        "**/Manifest*.*"
-    )
-
-    val debugTree = fileTree("${project.buildDir}/intermediates/javac/debug") {
-        exclude(fileFilter)
-    }
-
-    val mainSrc = "${project.projectDir}/src/main/java"
-
-    sourceDirectories.setFrom(files(mainSrc))
-    classDirectories.setFrom(files(debugTree))
-    executionData.setFrom(fileTree(project.buildDir) {
-        include("jacoco/testDebugUnitTest.exec", "outputs/code_coverage/debugAndroidTest/connected/**/*.ec")
-    })
-}
-```
-
-Run coverage:
-```bash
-./gradlew jacocoTestReport
-open app/build/reports/jacoco/jacocoTestReport/html/index.html
-```
-
-## Test Best Practices
-
-### 1. Follow AAA Pattern
-
-```kotlin
-@Test
-fun testName() {
-    // Arrange - Set up test data
-    val calculator = Calculator()
-    val a = 2
-    val b = 3
-
-    // Act - Execute the function
-    val result = calculator.add(a, b)
-
-    // Assert - Verify the result
-    assertEquals(5, result)
-}
-```
-
-### 2. Use Descriptive Names
-
-```kotlin
-// Good
-@Test
-fun addition_withPositiveNumbers_returnsSum() { }
-
-@Test
-fun division_withZeroDivisor_throwsException() { }
-
-// Bad
-@Test
-fun test1() { }
-
-@Test
-fun testAdd() { }
-```
-
-### 3. Test One Thing
-
-```kotlin
-// Good - separate tests
-@Test
-fun add_returnsCorrectSum() { }
-
-@Test
-fun add_handlesNegativeNumbers() { }
-
-// Bad - testing multiple things
-@Test
-fun testAddition() {
-    // Tests multiple scenarios in one test
-}
-```
-
-### 4. Mock External Dependencies
-
-```kotlin
-import org.mockito.Mock
-import org.mockito.Mockito.*
-
-class UserRepositoryTest {
-
-    @Mock
-    lateinit var api: ApiService
-
-    @Test
-    fun getUser_callsApi() {
-        val userId = "123"
-        `when`(api.fetchUser(userId)).thenReturn(User("John"))
-
-        val repository = UserRepository(api)
-        val user = repository.getUser(userId)
-
-        verify(api).fetchUser(userId)
-        assertEquals("John", user.name)
-    }
-}
-```
-
-## CI/CD Testing
-
-### GitHub Actions
-
-`.github/workflows/test.yml`:
-```yaml
-name: Android CI
-
-on:
-  push:
-    branches: [ main ]
-  pull_request:
-    branches: [ main ]
-
-jobs:
-  test:
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v4
-
-      - name: Set up JDK 17
-        uses: actions/setup-java@v4
-        with:
-          distribution: 'temurin'
-          java-version: '17'
-
-      - name: Setup Go
-        uses: actions/setup-go@v5
-        with:
-          go-version: '1.22'
-
-      - name: Test Go code
-        run: |
-          cd golib
-          go test -v ./...
-
-      - name: Build gomobile AAR
-        run: |
-          go install golang.org/x/mobile/cmd/gomobile@latest
-          gomobile init
-          cd golib
-          gomobile bind -target=android -o mylib.aar .
-          cp mylib.aar ../app/libs/
-
-      - name: Run unit tests
-        run: ./gradlew test
-
-      - name: Run instrumented tests
-        uses: reactivecircus/android-emulator-runner@v2
-        with:
-          api-level: 34
-          target: google_apis
-          arch: x86_64
-          script: ./gradlew connectedCheck
-
-      - name: Upload test results
-        if: always()
-        uses: actions/upload-artifact@v4
-        with:
-          name: test-results
-          path: app/build/reports/tests/
-
-      - name: Upload coverage
-        uses: codecov/codecov-action@v3
-        with:
-          files: app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml
-```
-
-## Troubleshooting
-
-### Tests fail on CI but pass locally
-
-- Check Android API level compatibility
-- Verify emulator configuration
-- Check for timing issues (add timeouts)
-- Ensure deterministic test data
-
-### Instrumentation tests hang
-
-```bash
-# Clear app data between tests
-adb shell pm clear com.example.myapp
-
-# Restart adb
-adb kill-server
-adb start-server
-```
-
-### Espresso can't find view
-
-```kotlin
-// Wait for view with idling resources
-import androidx.test.espresso.IdlingRegistry
-import androidx.test.espresso.IdlingResource
-
-// Or use explicit waits (not ideal)
-Thread.sleep(1000)  // Avoid this in production tests
-```
-
-## Next Steps
-
-- **Increase coverage** - Aim for >80%
-- **Add UI tests** - Cover critical user flows
-- **Integrate CI/CD** - Automated testing on every commit
-- **Monitor flaky tests** - Fix non-deterministic tests
-
-## Resources
-
-- [Android Testing](https://developer.android.com/training/testing)
-- [Espresso Documentation](https://developer.android.com/training/testing/espresso)
-- [JUnit Documentation](https://junit.org/junit4/)
-- [Mockito Documentation](https://site.mockito.org/)
dots/.config/claude/skills/Android/SKILL.md
@@ -35,16 +35,9 @@ Running the **WorkflowName** workflow from the **Android** skill...
 
 | Workflow | Trigger | File |
 |----------|---------|------|
-| **GomobileBind** | "build go library for android", "create AAR", "gomobile bind" | `workflows/GomobileBind.md` |
-| **GomobileApp** | "build android app with go", "gomobile build", "standalone go app" | `workflows/GomobileApp.md` |
-| **Setup** | "setup android dev", "install android sdk", "android environment" | `workflows/Setup.md` |
-| **Build** | "build apk", "build aab", "gradle build", "assemble" | `workflows/Build.md` |
-| **Debug** | "debug android", "adb logcat", "android logs", "troubleshoot app" | `workflows/Debug.md` |
-| **Emulator** | "android emulator", "avd", "virtual device", "start emulator" | `workflows/Emulator.md` |
-| **Gradle** | "gradle dependencies", "build.gradle", "android dependencies" | `workflows/Gradle.md` |
-| **Publish** | "publish to play store", "sign apk", "release build", "keystore" | `workflows/Publish.md` |
-| **Test** | "android tests", "instrumentation", "ui tests", "espresso" | `workflows/Test.md` |
-| **Nix** | "android on nix", "nixpkgs android", "android sdk nix" | `workflows/Nix.md` |
+| **Setup** | "setup android dev", "install android sdk", "android environment", "nixos android", "android on nix" | `workflows/Setup.md` |
+| **Build** | "build apk", "build aab", "gradle build", "assemble", "build go library", "create AAR", "gomobile bind", "gomobile build", "publish to play store", "sign apk", "release build", "keystore", "gradle dependencies" | `workflows/Build.md` |
+| **Debug** | "debug android", "adb logcat", "android logs", "troubleshoot app", "android emulator", "avd", "virtual device", "start emulator", "android tests", "instrumentation", "ui tests", "espresso" | `workflows/Debug.md` |
 
 ## Core Principles
 
@@ -474,7 +467,7 @@ This repository uses NixOS. Android SDK can be configured via:
 }
 ```
 
-See `workflows/Nix.md` for detailed Nix integration.
+See **Setup** workflow for detailed Nix integration.
 
 ## Best Practices
 
@@ -550,7 +543,7 @@ See `workflows/Nix.md` for detailed Nix integration.
 **Example 1: Build Go library as Android AAR**
 ```
 User: "I have a Go package with networking code. How do I use it in my Android app?"
-→ Invokes GomobileBind workflow
+→ Invokes Build workflow
 → Checks gomobile installation
 → Generates AAR with `gomobile bind`
 → Shows how to add AAR to build.gradle.kts
@@ -570,7 +563,7 @@ User: "My app crashes when I click the submit button. How do I debug this?"
 **Example 3: Setup Android development on NixOS**
 ```
 User: "How do I set up Android development on my NixOS machine?"
-→ Invokes Nix workflow
+→ Invokes Setup workflow
 → Shows nixpkgs Android SDK configuration
 → Explains how to compose Android packages with androidenv
 → Configures environment variables (ANDROID_HOME, PATH)