Commit 057cb55018c1
Changed files (11)
dots
.config
claude
skills
Android
dots/.config/claude/skills/Android/workflows/Build.md
@@ -0,0 +1,433 @@
+# Build Workflow
+
+Build Android applications (APK/AAB) using Gradle build system.
+
+## Build Types
+
+### APK (Android Package)
+- Direct install on devices
+- Can be shared and sideloaded
+- Larger file size (contains all ABIs)
+- Use for: Development, testing, direct distribution
+
+### AAB (Android App Bundle)
+- Play Store distribution format
+- Smaller download size (dynamic delivery)
+- Cannot be directly installed
+- Use for: Play Store releases
+
+## Quick Start
+
+```bash
+# Navigate to project
+cd /path/to/android-project
+
+# Build debug APK
+./gradlew assembleDebug
+
+# Build release APK
+./gradlew assembleRelease
+
+# Build release AAB
+./gradlew bundleRelease
+
+# Install debug on device
+./gradlew installDebug
+```
+
+## Build Variants
+
+Android projects typically have:
+
+| Variant | Use Case | Signed | Minified |
+|---------|----------|--------|----------|
+| Debug | Development | Auto | No |
+| Release | Production | Manual | Yes |
+
+### Debug Build
+
+```bash
+# Build
+./gradlew assembleDebug
+
+# Output location
+ls app/build/outputs/apk/debug/app-debug.apk
+
+# Install on connected 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).
+
+```bash
+# Build signed release APK
+./gradlew assembleRelease
+
+# Output
+ls app/build/outputs/apk/release/app-release.apk
+
+# Build AAB for Play Store
+./gradlew bundleRelease
+
+# Output
+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
+
+If using gomobile bind:
+
+```bash
+# 1. Build AAR from Go code
+cd golib
+gomobile bind -target=android -o mylib.aar .
+
+# 2. Copy to Android project
+cp mylib.aar /path/to/android-project/app/libs/
+
+# 3. Build Android app
+cd /path/to/android-project
+./gradlew assembleDebug
+```
+
+Automate in script:
+```bash
+#!/usr/bin/env bash
+set -euo pipefail
+
+echo "Building Go library..."
+cd golib
+gomobile bind -target=android -o mylib.aar .
+
+echo "Copying AAR to Android project..."
+cp mylib.aar ../app/libs/
+
+echo "Building Android app..."
+cd ..
+./gradlew assembleDebug
+
+echo "Done! APK: app/build/outputs/apk/debug/app-debug.apk"
+```
+
+## Build Flavors
+
+Create different versions of your app:
+
+```kotlin
+// app/build.gradle.kts
+android {
+ flavorDimensions += "version"
+
+ productFlavors {
+ create("free") {
+ dimension = "version"
+ applicationIdSuffix = ".free"
+ versionNameSuffix = "-free"
+ }
+
+ create("paid") {
+ dimension = "version"
+ applicationIdSuffix = ".paid"
+ versionNameSuffix = "-paid"
+ }
+ }
+}
+```
+
+Build specific flavor:
+```bash
+# Free debug
+./gradlew assembleFreeDebug
+
+# Paid release
+./gradlew assemblePaidRelease
+
+# All flavors and variants
+./gradlew assemble
+```
+
+## Continuous Integration
+
+### GitHub Actions Example
+
+`.github/workflows/android.yml`:
+```yaml
+name: Android CI
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ build:
+ 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 Android SDK
+ uses: android-actions/setup-android@v3
+
+ - name: Setup Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: '1.22'
+
+ - name: Install gomobile
+ 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: Upload APK
+ uses: actions/upload-artifact@v4
+ with:
+ name: app-debug
+ path: app/build/outputs/apk/debug/app-debug.apk
+```
+
+## Troubleshooting
+
+### Build fails with "SDK not found"
+
+```bash
+# Create local.properties
+echo "sdk.dir=$ANDROID_HOME" > local.properties
+```
+
+### OutOfMemoryError during build
+
+Increase Gradle memory in `gradle.properties`:
+```properties
+org.gradle.jvmargs=-Xmx8192m -XX:MaxMetaspaceSize=2048m
+```
+
+### Duplicate class errors
+
+```kotlin
+// Use packagingOptions to exclude duplicates
+android {
+ packagingOptions {
+ resources {
+ excludes += "META-INF/*.kotlin_module"
+ }
+ }
+}
+```
+
+### AAR not found
+
+```kotlin
+// Ensure AAR is in libs directory
+dependencies {
+ implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
+ // Or
+ implementation(files("libs/mylib.aar"))
+}
+```
+
+## 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
+
+```bash
+# Extract APK
+unzip app-debug.apk -d extracted
+
+# View manifest
+aapt dump badging app-debug.apk
+
+# View method count
+# Install dex-method-counts first
+dex-method-counts app-debug.apk
+```
+
+## Next Steps
+
+- **Install and test** - Use the **Debug** workflow
+- **Sign and publish** - Use the **Publish** workflow
+- **Run tests** - Use the **Test** workflow
+
+## 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)
dots/.config/claude/skills/Android/workflows/Debug.md
@@ -0,0 +1,544 @@
+# Debug Workflow
+
+Debug Android applications using adb, logcat, and Android debugging tools.
+
+## ADB (Android Debug Bridge)
+
+### Basic Commands
+
+```bash
+# List connected devices
+adb devices
+
+# Connect to specific device (if multiple)
+adb -s <device-id> <command>
+
+# Check device info
+adb shell getprop ro.product.model
+adb shell getprop ro.build.version.release
+```
+
+### App Management
+
+```bash
+# Install APK
+adb install app-debug.apk
+
+# Reinstall (keep data)
+adb install -r app-debug.apk
+
+# Uninstall app
+adb uninstall com.example.myapp
+
+# Clear app data
+adb shell pm clear com.example.myapp
+
+# List installed packages
+adb shell pm list packages
+adb shell pm list packages | grep myapp
+
+# Get app info
+adb shell dumpsys package com.example.myapp
+```
+
+### Start/Stop App
+
+```bash
+# Start app activity
+adb shell am start -n com.example.myapp/.MainActivity
+
+# Start with intent data
+adb shell am start -n com.example.myapp/.MainActivity -d "https://example.com"
+
+# Stop (force close) app
+adb shell am force-stop com.example.myapp
+
+# Kill app process
+adb shell am kill com.example.myapp
+```
+
+## Logcat
+
+### View Logs
+
+```bash
+# All logs (verbose)
+adb logcat
+
+# Clear logs first, then follow
+adb logcat -c && adb logcat
+
+# Filter by app package
+adb logcat | grep "com.example.myapp"
+
+# Filter by tag
+adb logcat -s MyTag
+
+# Filter by priority (V=Verbose, D=Debug, I=Info, W=Warn, E=Error, F=Fatal)
+adb logcat *:E # Only errors
+
+# Multiple filters
+adb logcat MyTag:D *:E # MyTag at Debug level, everything else at Error
+```
+
+### Log Priorities
+
+```
+V - Verbose (lowest priority)
+D - Debug
+I - Info
+W - Warning
+E - Error
+F - Fatal
+S - Silent (highest priority, nothing printed)
+```
+
+### Format Options
+
+```bash
+# Brief format (default)
+adb logcat -v brief
+
+# Time format (with timestamps)
+adb logcat -v time
+
+# Threadtime (time + PID + TID)
+adb logcat -v threadtime
+
+# Long format (all metadata)
+adb logcat -v long
+```
+
+### Save Logs to File
+
+```bash
+# Save all logs
+adb logcat > logs.txt
+
+# Save with timestamp in filename
+adb logcat > "logs-$(date +%Y%m%d-%H%M%S).txt"
+
+# Save for 10 seconds then stop
+timeout 10s adb logcat > logs.txt
+```
+
+### Filter Crash Logs
+
+```bash
+# Show only fatal errors
+adb logcat *:F
+
+# Show crashes and errors
+adb logcat *:E
+
+# Find specific crash
+adb logcat | grep -A 50 "FATAL EXCEPTION"
+
+# Show stack traces
+adb logcat | grep -E "(at |Caused by)"
+```
+
+### Gomobile / Go-specific Logs
+
+```bash
+# Go runtime logs
+adb logcat | grep GoLog
+
+# Go panic messages
+adb logcat | grep "panic:"
+
+# Your Go log.Printf output
+adb logcat | grep "GoLog"
+```
+
+## Debugging from Code
+
+### Android (Kotlin/Java)
+
+```kotlin
+import android.util.Log
+
+class MainActivity : AppCompatActivity() {
+ companion object {
+ private const val TAG = "MainActivity"
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ // Log levels
+ Log.v(TAG, "Verbose message")
+ Log.d(TAG, "Debug message")
+ Log.i(TAG, "Info message")
+ Log.w(TAG, "Warning message")
+ Log.e(TAG, "Error message")
+
+ // With exception
+ try {
+ // Code that might throw
+ } catch (e: Exception) {
+ Log.e(TAG, "Error occurred", e)
+ }
+ }
+}
+```
+
+View in logcat:
+```bash
+adb logcat -s MainActivity
+```
+
+### Go Code (Gomobile)
+
+```go
+package mylib
+
+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 {
+ log.Printf("Error fetching: %v", err)
+ return "", err
+ }
+
+ log.Printf("Fetch successful, got %d bytes", len(result))
+ return result, nil
+}
+```
+
+View in logcat:
+```bash
+adb logcat | grep GoLog
+```
+
+## Device/Emulator Commands
+
+### 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
+```
+
+## Performance Profiling
+
+### CPU Profiling
+
+```bash
+# Start profiling
+adb shell am profile start com.example.myapp /sdcard/profile.trace
+
+# Stop profiling
+adb shell am profile stop com.example.myapp
+
+# Pull trace file
+adb pull /sdcard/profile.trace
+
+# Analyze with Android Studio or Perfetto
+```
+
+### Memory Profiling
+
+```bash
+# Get memory info for app
+adb shell dumpsys meminfo com.example.myapp
+
+# Heap dump
+adb shell am dumpheap com.example.myapp /sdcard/heap.hprof
+adb pull /sdcard/heap.hprof
+
+# Analyze with Android Studio Memory Profiler
+```
+
+### Monitor App Performance
+
+```bash
+# CPU usage
+adb shell top | grep com.example.myapp
+
+# Memory usage over time
+adb shell dumpsys meminfo com.example.myapp | grep TOTAL
+```
+
+## Debugging Crashes
+
+### 1. View Crash Logs
+
+```bash
+adb logcat -c # Clear old logs
+# Reproduce crash
+adb logcat *:E # View errors
+```
+
+### 2. Find Stack Trace
+
+Look for:
+```
+FATAL EXCEPTION: main
+Process: com.example.myapp, PID: 12345
+java.lang.NullPointerException: Attempt to invoke virtual method '...' on a null object reference
+ at com.example.myapp.MainActivity.onCreate(MainActivity.kt:25)
+ at android.app.Activity.performCreate(Activity.java:...)
+```
+
+### 3. Analyze Stack Trace
+
+- **Line number**: `MainActivity.kt:25` - exact location
+- **Exception type**: `NullPointerException` - what went wrong
+- **Message**: Details about the error
+- **Call stack**: How we got there
+
+### 4. Common Crash Types
+
+**NullPointerException**:
+```kotlin
+// Bad
+val user = getUser() // Returns null
+val name = user.name // Crash!
+
+// Good
+val user = getUser()
+val name = user?.name ?: "Unknown"
+```
+
+**ClassCastException**:
+```kotlin
+// Bad
+val text = view as TextView // Crash if not TextView!
+
+// Good
+val text = view as? TextView
+```
+
+**ActivityNotFoundException**:
+```kotlin
+// Check if intent can be handled
+if (intent.resolveActivity(packageManager) != null) {
+ startActivity(intent)
+}
+```
+
+## 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
+
+```bash
+# Use adb reverse to connect app to local proxy
+adb reverse tcp:8888 tcp:8888
+
+# App should connect to localhost:8888
+# Proxy (mitmproxy, Charles, Burp Suite) runs on port 8888
+```
+
+### View Network Stats
+
+```bash
+adb shell dumpsys netstats
+```
+
+## Common Issues
+
+### "adb: device offline"
+
+```bash
+adb kill-server
+adb start-server
+# Reconnect device
+```
+
+### "adb: device unauthorized"
+
+- Check device screen for authorization prompt
+- Accept "Always allow from this computer"
+- If no prompt:
+ ```bash
+ adb kill-server
+ rm ~/.android/adbkey*
+ adb start-server
+ ```
+
+### "Logcat shows nothing"
+
+```bash
+# Clear buffer
+adb logcat -c
+
+# Check buffer size
+adb logcat -g
+
+# Increase buffer size
+adb logcat -G 16M
+```
+
+### App not showing in process list
+
+```bash
+# Check if app is running
+adb shell "ps -A | grep com.example.myapp"
+
+# Start app if not running
+adb shell am start -n com.example.myapp/.MainActivity
+```
+
+## Debug Helpers
+
+### Monitor script
+
+Create `monitor-app.sh`:
+```bash
+#!/usr/bin/env bash
+set -euo pipefail
+
+APP_PACKAGE="com.example.myapp"
+LOG_TAG="MyApp"
+
+# Clear logs
+adb logcat -c
+
+# Monitor logs with color
+adb logcat | grep --color=always -E "$APP_PACKAGE|$LOG_TAG|FATAL|ERROR"
+```
+
+### Install and monitor script
+
+Create `debug-install.sh`:
+```bash
+#!/usr/bin/env bash
+set -euo pipefail
+
+APK="app/build/outputs/apk/debug/app-debug.apk"
+PACKAGE="com.example.myapp"
+ACTIVITY=".MainActivity"
+
+echo "Installing $APK..."
+adb install -r "$APK"
+
+echo "Starting app..."
+adb shell am start -n "$PACKAGE/$ACTIVITY"
+
+echo "Monitoring logs (Ctrl+C to stop)..."
+adb logcat -c
+adb logcat | grep "$PACKAGE"
+```
+
+## Next Steps
+
+- **Run tests** - Use the **Test** workflow
+- **Profile performance** - Advanced profiling techniques
+- **Publish app** - Use the **Publish** workflow
+
+## 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)
dots/.config/claude/skills/Android/workflows/Emulator.md
@@ -0,0 +1,471 @@
+# 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
@@ -0,0 +1,670 @@
+# 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
@@ -0,0 +1,485 @@
+# 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
@@ -0,0 +1,507 @@
+# 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
@@ -0,0 +1,517 @@
+# 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
@@ -0,0 +1,492 @@
+# 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
@@ -0,0 +1,304 @@
+# Setup Workflow
+
+Configure Android development environment including SDK, NDK, command-line tools, and necessary environment variables.
+
+## Installation Options
+
+### Option 1: Android Studio (Recommended for beginners)
+
+**Pros**: GUI, automatic SDK management, emulator, IDE
+**Cons**: Large download (~1GB), more resources
+
+**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 2: Command-line Tools (Recommended for this setup)
+
+**Pros**: Lightweight, scriptable, no GUI
+**Cons**: Manual configuration, no IDE
+
+**Steps:**
+
+#### 1. Download Command-line Tools
+
+```bash
+# Create SDK directory
+mkdir -p ~/Android/Sdk
+cd ~/Android/Sdk
+
+# Download latest cmdline-tools (Linux)
+wget https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip
+
+# Or for Mac
+# wget https://dl.google.com/android/repository/commandlinetools-mac-11076708_latest.zip
+
+# Extract
+unzip commandlinetools-linux-*_latest.zip
+rm commandlinetools-linux-*_latest.zip
+
+# Move to correct location
+mkdir -p cmdline-tools/latest
+mv cmdline-tools/* cmdline-tools/latest/ 2>/dev/null || true
+```
+
+#### 2. Set Environment Variables
+
+Add to `~/.bashrc` or `~/.zshrc`:
+
+```bash
+# Android SDK
+export ANDROID_HOME=$HOME/Android/Sdk
+export PATH=$PATH:$ANDROID_HOME/cmdline-tools/latest/bin
+export PATH=$PATH:$ANDROID_HOME/platform-tools
+export PATH=$PATH:$ANDROID_HOME/emulator
+```
+
+Apply changes:
+```bash
+source ~/.bashrc # or ~/.zshrc
+```
+
+#### 3. Accept Licenses
+
+```bash
+sdkmanager --licenses
+```
+
+Type `y` and press Enter for each license.
+
+#### 4. Install SDK Components
+
+```bash
+# Platform tools (adb, fastboot)
+sdkmanager "platform-tools"
+
+# Latest Android platform (API 34 as of 2025)
+sdkmanager "platforms;android-34"
+
+# Build tools
+sdkmanager "build-tools;34.0.0"
+
+# NDK (required for gomobile)
+sdkmanager "ndk;26.1.10909125"
+
+# Emulator (optional)
+sdkmanager "emulator"
+
+# System image for emulator (optional)
+sdkmanager "system-images;android-34;google_apis;x86_64"
+```
+
+#### 5. Verify Installation
+
+```bash
+# Check SDK manager
+sdkmanager --list
+
+# Check adb
+adb version
+
+# Check environment
+echo $ANDROID_HOME
+ls $ANDROID_HOME
+```
+
+### Option 3: NixOS / Nix Package Manager
+
+See the **Nix** workflow for NixOS-specific setup.
+
+## Post-Installation
+
+### Install Gomobile
+
+```bash
+go install golang.org/x/mobile/cmd/gomobile@latest
+gomobile init
+```
+
+This downloads additional toolchains for cross-compilation.
+
+### Configure Gradle (if using Android projects)
+
+Create `~/.gradle/gradle.properties`:
+
+```properties
+# Use more memory for Gradle
+org.gradle.jvmargs=-Xmx4096m
+
+# Enable Gradle daemon
+org.gradle.daemon=true
+
+# Enable parallel execution
+org.gradle.parallel=true
+
+# Enable build cache
+org.gradle.caching=true
+
+# Use AndroidX
+android.useAndroidX=true
+android.enableJetifier=true
+```
+
+### Setup Device for Development
+
+#### Enable Developer Options on Android Device
+
+1. Go to **Settings** → **About phone**
+2. Tap **Build number** 7 times
+3. Go back to **Settings** → **System** → **Developer options**
+4. Enable **USB debugging**
+
+#### Connect Device
+
+```bash
+# Connect via USB
+adb devices
+
+# Should show:
+# List of devices attached
+# 1234567890ABCDEF device
+
+# If shows "unauthorized", check phone for authorization prompt
+```
+
+#### Connect via WiFi (optional)
+
+```bash
+# First connect via USB
+adb tcpip 5555
+
+# Find device IP (Settings → About → Status → IP address)
+# Then connect wirelessly
+adb connect 192.168.1.100:5555
+
+# Disconnect USB cable
+# Verify wireless connection
+adb devices
+```
+
+## Directory Structure
+
+After setup, your SDK should look like:
+
+```
+~/Android/Sdk/
+├── build-tools/
+│ └── 34.0.0/
+│ ├── aapt
+│ ├── aapt2
+│ ├── apksigner
+│ └── ...
+├── cmdline-tools/
+│ └── latest/
+│ └── bin/
+│ ├── sdkmanager
+│ ├── avdmanager
+│ └── ...
+├── emulator/
+│ ├── emulator
+│ └── ...
+├── ndk/
+│ └── 26.1.10909125/
+├── platform-tools/
+│ ├── adb
+│ ├── fastboot
+│ └── ...
+├── platforms/
+│ └── android-34/
+└── system-images/
+ └── android-34/
+```
+
+## Troubleshooting
+
+### sdkmanager: command not found
+
+```bash
+# Check ANDROID_HOME
+echo $ANDROID_HOME
+
+# Check PATH
+echo $PATH | grep Android
+
+# Reload shell configuration
+source ~/.bashrc
+```
+
+### adb: command not found
+
+```bash
+# Install platform-tools
+sdkmanager "platform-tools"
+
+# Add to PATH
+export PATH=$PATH:$ANDROID_HOME/platform-tools
+```
+
+### gomobile init fails
+
+```bash
+# Make sure NDK is installed
+sdkmanager "ndk;26.1.10909125"
+
+# Check ANDROID_HOME
+echo $ANDROID_HOME
+
+# Retry
+gomobile init -v
+```
+
+### Device not detected
+
+```bash
+# Check USB debugging is enabled
+adb devices
+
+# Restart adb
+adb kill-server
+adb start-server
+
+# Check USB connection/cable
+# Try different USB port
+```
+
+## Updating Components
+
+### Update SDK components
+
+```bash
+# List installed and available packages
+sdkmanager --list
+
+# Update all installed packages
+sdkmanager --update
+
+# Install specific version
+sdkmanager "platforms;android-35"
+sdkmanager "build-tools;35.0.0"
+```
+
+### Update Gomobile
+
+```bash
+go install golang.org/x/mobile/cmd/gomobile@latest
+gomobile init
+```
+
+## Next Steps
+
+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
+
+## Resources
+
+- [Android Command-line Tools](https://developer.android.com/studio/command-line)
+- [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)
dots/.config/claude/skills/Android/workflows/Test.md
@@ -0,0 +1,536 @@
+# 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
@@ -0,0 +1,582 @@
+---
+name: Android
+description: Android app development with gomobile, Gradle, and modern tooling. USE WHEN building Android apps, working with gomobile bindings, managing Android SDK/NDK, running emulators, debugging with adb, or publishing to Play Store.
+---
+
+# Android Development
+
+Expert guidance on Android application development with first-class support for Go integration via gomobile, modern Gradle build system, SDK/NDK management, and deployment workflows.
+
+## Purpose
+
+Guide Android development following official best practices, with specialized support for:
+- **Gomobile integration**: Building Go libraries (AAR) and apps for Android
+- **Modern Gradle**: Kotlin DSL, version catalogs, and build optimization
+- **SDK management**: Android SDK, NDK, and command-line tools
+- **Development workflows**: Build, debug, test, and deploy Android apps
+- **Nix integration**: Using Android tools in NixOS environments
+
+### Context Detection
+
+**This skill activates when:**
+- Current directory contains `build.gradle`, `build.gradle.kts`, or `AndroidManifest.xml`
+- Git repository has Android project structure (`app/`, `gradle/`)
+- User mentions gomobile, Android SDK, adb, or Android development
+- Working with `.aar`, `.apk`, or `.aab` files
+- Commands like `gradle`, `adb`, `gomobile`, or `sdkmanager` are mentioned
+
+## Workflow Routing
+
+**When executing a workflow, output this notification directly:**
+
+```
+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` |
+
+## Core Principles
+
+1. **Gomobile for Go integration**: Use `gomobile bind` for libraries, `gomobile build` for standalone apps
+2. **Modern Gradle**: Prefer Kotlin DSL (`.gradle.kts`) over Groovy
+3. **Version catalogs**: Centralize dependency versions in `libs.versions.toml`
+4. **Multi-architecture**: Support arm64-v8a, armeabi-v7a, x86_64, x86
+5. **Type safety**: Handle Go ↔ Java/Kotlin type conversions carefully
+6. **SDK tools**: Use `sdkmanager` and `avdmanager` for command-line management
+7. **Reproducible builds**: Lock dependency versions and use consistent tooling
+
+## Android Project Structure
+
+### Standard Android Project
+
+```
+myapp/
+├── app/
+│ ├── src/
+│ │ ├── main/
+│ │ │ ├── java/com/example/myapp/
+│ │ │ │ └── MainActivity.kt
+│ │ │ ├── res/
+│ │ │ │ ├── layout/
+│ │ │ │ ├── values/
+│ │ │ │ └── drawable/
+│ │ │ └── AndroidManifest.xml
+│ │ ├── test/ # Unit tests
+│ │ └── androidTest/ # Instrumentation tests
+│ ├── build.gradle.kts
+│ └── proguard-rules.pro
+├── gradle/
+│ └── libs.versions.toml # Version catalog
+├── build.gradle.kts # Root build file
+├── settings.gradle.kts # Project settings
+├── gradle.properties
+└── local.properties # SDK location (not in git)
+```
+
+### Android Project with Gomobile
+
+```
+myapp/
+├── app/
+│ ├── libs/
+│ │ └── mylib.aar # Generated by gomobile bind
+│ ├── src/main/
+│ │ ├── java/
+│ │ └── AndroidManifest.xml
+│ └── build.gradle.kts
+├── golib/ # Go library source
+│ ├── mylib.go
+│ └── go.mod
+├── build-aar.sh # Script to build AAR with gomobile
+└── gradle/
+```
+
+## Gomobile Overview
+
+### What is Gomobile?
+
+Gomobile enables Go code to run on Android (and iOS). Two main modes:
+
+1. **gomobile bind**: Create native library (AAR) from Go package
+ - Go code becomes Android library
+ - Called from Java/Kotlin
+ - Best for integrating Go into existing Android apps
+
+2. **gomobile build**: Create standalone Android app
+ - Entire app written in Go
+ - Uses gomobile app package
+ - Limited Android UI capabilities
+
+### Type Mapping (Go ↔ Java/Kotlin)
+
+| Go Type | Java Type | Kotlin Type | Notes |
+|---------|-----------|-------------|-------|
+| `bool` | `boolean` | `Boolean` | |
+| `int`, `int32` | `int` | `Int` | 32-bit |
+| `int64` | `long` | `Long` | 64-bit |
+| `float32` | `float` | `Float` | |
+| `float64` | `double` | `Double` | |
+| `string` | `String` | `String` | |
+| `[]byte` | `byte[]` | `ByteArray` | |
+| `error` | `Exception` | `Exception` | Becomes checked exception |
+| `interface` | `interface` | `interface` | Must have exported methods |
+| `struct` | `class` | `class` | Exported fields become getters/setters |
+
+### Gomobile Limitations
+
+**What works:**
+- Exported functions and methods
+- Basic types (int, string, float, bool)
+- Slices of basic types
+- Interfaces with exported methods
+- Structs with exported fields
+- Callbacks via interfaces
+
+**What doesn't work:**
+- Generic types (Go 1.18+ generics)
+- Maps (use structs or custom types)
+- Channels (use callbacks instead)
+- Unexported types/functions
+- Complex nested types
+- Variadic functions
+
+## Android SDK Management
+
+### SDK Structure
+
+```
+$ANDROID_HOME/
+├── build-tools/
+│ └── 34.0.0/ # Build tools version
+├── cmdline-tools/
+│ └── latest/
+│ └── bin/
+│ ├── sdkmanager
+│ └── avdmanager
+├── emulator/ # Android Emulator
+├── ndk/
+│ └── 26.1.10909125/ # NDK version
+├── platform-tools/ # adb, fastboot
+└── platforms/
+ └── android-34/ # API level 34
+```
+
+### Essential Tools
+
+- **sdkmanager**: Install and update SDK packages
+- **avdmanager**: Create and manage Android Virtual Devices (AVDs)
+- **adb**: Android Debug Bridge (device communication)
+- **emulator**: Run Android Virtual Devices
+- **gradle**: Build system
+
+## Gradle Configuration
+
+### Version Catalog (`gradle/libs.versions.toml`)
+
+```toml
+[versions]
+agp = "8.3.0" # Android Gradle Plugin
+kotlin = "1.9.22"
+compileSdk = "34"
+minSdk = "24"
+targetSdk = "34"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.12.0" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version = "1.6.1" }
+material = { group = "com.google.android.material", name = "material", version = "1.11.0" }
+
+[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.androidx.appcompat)
+ implementation(libs.material)
+
+ // Add gomobile AAR
+ implementation(files("libs/mylib.aar"))
+}
+```
+
+## Testing
+
+### Test Types
+
+1. **Local Unit Tests** (`src/test/`)
+ - Run on JVM
+ - Fast, no Android device needed
+ - Use JUnit, Mockito
+
+2. **Instrumentation Tests** (`src/androidTest/`)
+ - Run on Android device/emulator
+ - Test Android framework APIs
+ - Use AndroidJUnit4, Espresso
+
+3. **UI Tests**
+ - Test user interface
+ - Use Espresso, UI Automator
+ - Run on device/emulator
+
+### Example Unit Test
+
+```kotlin
+// src/test/java/com/example/myapp/CalculatorTest.kt
+import org.junit.Test
+import org.junit.Assert.*
+
+class CalculatorTest {
+ @Test
+ fun addition_isCorrect() {
+ val calculator = Calculator()
+ assertEquals(4, calculator.add(2, 2))
+ }
+}
+```
+
+### Example Instrumentation Test
+
+```kotlin
+// src/androidTest/java/com/example/myapp/MainActivityTest.kt
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.ext.junit.rules.ActivityScenarioRule
+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 testButtonClick() {
+ // Test implementation
+ }
+}
+```
+
+## Signing and Publishing
+
+### Debug Signing
+
+Android Studio automatically creates debug keystore:
+- Location: `~/.android/debug.keystore`
+- Password: `android`
+- Alias: `androiddebugkey`
+
+### Release Signing
+
+Create release keystore:
+```bash
+keytool -genkey -v \
+ -keystore release.keystore \
+ -alias myapp \
+ -keyalg RSA \
+ -keysize 2048 \
+ -validity 10000
+```
+
+Configure in `app/build.gradle.kts`:
+```kotlin
+android {
+ signingConfigs {
+ create("release") {
+ storeFile = file("../release.keystore")
+ storePassword = System.getenv("KEYSTORE_PASSWORD")
+ keyAlias = "myapp"
+ keyPassword = System.getenv("KEY_PASSWORD")
+ }
+ }
+
+ buildTypes {
+ release {
+ signingConfig = signingConfigs.getByName("release")
+ }
+ }
+}
+```
+
+### Build Types
+
+- **APK**: Android Package (direct install)
+ ```bash
+ ./gradlew assembleRelease
+ # Output: app/build/outputs/apk/release/app-release.apk
+ ```
+
+- **AAB**: Android App Bundle (Play Store)
+ ```bash
+ ./gradlew bundleRelease
+ # Output: app/build/outputs/bundle/release/app-release.aab
+ ```
+
+## Common Commands
+
+### Gradle
+
+```bash
+# Build debug APK
+./gradlew assembleDebug
+
+# Build release AAB
+./gradlew bundleRelease
+
+# Install on connected device
+./gradlew installDebug
+
+# Clean build
+./gradlew clean
+
+# List tasks
+./gradlew tasks
+
+# Check dependencies
+./gradlew dependencies
+```
+
+### ADB (Android Debug Bridge)
+
+```bash
+# List devices
+adb devices
+
+# Install APK
+adb install app-debug.apk
+
+# Uninstall app
+adb uninstall com.example.myapp
+
+# View logs
+adb logcat
+
+# View logs for specific app
+adb logcat | grep com.example.myapp
+
+# Clear app data
+adb shell pm clear com.example.myapp
+
+# Take screenshot
+adb shell screencap /sdcard/screen.png
+adb pull /sdcard/screen.png
+
+# Get device properties
+adb shell getprop
+
+# Start activity
+adb shell am start -n com.example.myapp/.MainActivity
+```
+
+### Gomobile
+
+```bash
+# Install gomobile
+go install golang.org/x/mobile/cmd/gomobile@latest
+gomobile init
+
+# Build AAR for Android
+gomobile bind -target=android -o mylib.aar ./golib
+
+# Build AAR for specific architectures
+gomobile bind -target=android/arm64,android/amd64 -o mylib.aar ./golib
+
+# Build standalone Android app
+gomobile build -target=android ./cmd/myapp
+
+# Build with custom app ID
+gomobile build -target=android -appid=com.example.myapp ./cmd/myapp
+```
+
+## Nix Integration
+
+This repository uses NixOS. Android SDK can be configured via:
+
+### Using nixpkgs Android SDK
+
+```nix
+{ pkgs, ... }:
+{
+ home.packages = with pkgs; [
+ android-studio
+ android-tools # adb, fastboot
+ ];
+
+ # Or use androidenv for more control
+ android-sdk = pkgs.androidenv.composeAndroidPackages {
+ platformVersions = [ "34" ];
+ buildToolsVersions = [ "34.0.0" ];
+ includeNDK = true;
+ ndkVersion = "26.1.10909125";
+ includeEmulator = true;
+ };
+}
+```
+
+See `workflows/Nix.md` for detailed Nix integration.
+
+## Best Practices
+
+### General
+
+- Use Kotlin over Java for new code
+- Follow Material Design guidelines
+- Support multiple screen sizes and orientations
+- Handle configuration changes properly
+- Use Android Architecture Components (ViewModel, LiveData, Room)
+- Implement proper error handling
+- Add crash reporting (Firebase Crashlytics, Sentry)
+
+### Gomobile Specific
+
+- Keep Go API simple and flat
+- Avoid complex nested types
+- Use interfaces for callbacks
+- Document type conversions
+- Test Go code separately from Android integration
+- Consider performance implications of crossing Go/Java boundary
+- Use goroutines carefully (they don't map to Android threads)
+
+### Security
+
+- Never commit keystores to version control
+- Use environment variables for secrets
+- Enable ProGuard/R8 for release builds
+- Validate all user inputs
+- Use HTTPS for network communication
+- Store sensitive data in Android Keystore
+- Follow OWASP Mobile Security guidelines
+
+### Performance
+
+- Use build variants for different configurations
+- Enable code shrinking and obfuscation
+- Optimize images and resources
+- Use vector drawables when possible
+- Profile with Android Profiler
+- Monitor memory usage
+- Implement proper caching strategies
+
+## Resources
+
+### Official Documentation
+
+- [Android Developers](https://developer.android.com/)
+- [Gomobile Documentation](https://pkg.go.dev/golang.org/x/mobile/cmd/gomobile)
+- [Gradle Documentation](https://docs.gradle.org/)
+- [Android Studio](https://developer.android.com/studio)
+
+### Gomobile Resources
+
+- [Gomobile Wiki](https://github.com/golang/go/wiki/Mobile)
+- [Gomobile Examples](https://github.com/golang/mobile/tree/master/example)
+- [Go on Android](https://godoc.org/golang.org/x/mobile/app)
+
+### Tools
+
+- [Android SDK Command-Line Tools](https://developer.android.com/studio/command-line)
+- [ADB Documentation](https://developer.android.com/studio/command-line/adb)
+- [Gradle Build Tool](https://gradle.org/)
+
+### Best Practices
+
+- [Android Best Practices](https://developer.android.com/topic/best-practices)
+- [Kotlin Style Guide](https://developer.android.com/kotlin/style-guide)
+- [Material Design](https://material.io/design)
+
+## Examples
+
+**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
+→ Checks gomobile installation
+→ Generates AAR with `gomobile bind`
+→ Shows how to add AAR to build.gradle.kts
+→ Provides example of calling Go code from Kotlin
+```
+
+**Example 2: Debug Android app crash**
+```
+User: "My app crashes when I click the submit button. How do I debug this?"
+→ Invokes Debug workflow
+→ Shows how to use `adb logcat` to view crash logs
+→ Helps filter logs for the specific app
+→ Explains stack trace interpretation
+→ Suggests debugging strategies (breakpoints, logging)
+```
+
+**Example 3: Setup Android development on NixOS**
+```
+User: "How do I set up Android development on my NixOS machine?"
+→ Invokes Nix workflow
+→ Shows nixpkgs Android SDK configuration
+→ Explains how to compose Android packages with androidenv
+→ Configures environment variables (ANDROID_HOME, PATH)
+→ Verifies setup with adb and gradle commands
+```
+
+---
+
+**Philosophy**: Android development should embrace modern tooling and practices. Gomobile provides a powerful bridge between Go's excellent concurrency and networking capabilities and Android's vast ecosystem. Use the right tool for each layer: Go for backend logic and performance-critical code, Kotlin for UI and Android framework integration.