diff --git a/Makefile b/Makefile index ab56b0d..ca4f4f2 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # Crkl - Physical Phone Development -.PHONY: help build install run stop logs clean uninstall +.PHONY: help setup-sdk build install run stop logs clean uninstall devices check-device ANDROID_HOME ?= $(HOME)/android-sdk ADB := $(ANDROID_HOME)/platform-tools/adb @@ -7,10 +7,22 @@ GRADLEW := ./gradlew APK := app/build/outputs/apk/debug/app-debug.apk help: ## Show available commands - @echo "Crkl - Physical Phone Commands" + @echo "Crkl - Android Development Commands" @echo "" + @echo "Setup Commands:" + @echo " make setup-sdk - Install Android SDK and tools" + @echo " make setup-emulator - Create and start Android emulator" + @echo " make check-device - Check if device/emulator is connected" + @echo " make devices - List connected devices/emulators" + @echo "" + @echo "Emulator Commands:" + @echo " make emulator - Start Android emulator" + @echo " make emulator-stop - Stop emulator" + @echo " make emulator-list - List available emulators" + @echo "" + @echo "Development Commands:" @echo " make build - Build APK" - @echo " make install - Install to phone" + @echo " make install - Install to device/emulator" @echo " make run - Launch app" @echo " make logs - Watch app logs" @echo " make stop - Stop app" @@ -18,28 +30,99 @@ help: ## Show available commands @echo " make uninstall - Remove app" @echo "" +setup-sdk: ## Install Android SDK and tools + @echo "Setting up Android SDK..." + @mkdir -p $(ANDROID_HOME) + @cd $(ANDROID_HOME) && \ + if [ ! -f cmdline-tools/latest/bin/sdkmanager ]; then \ + echo "Downloading Android SDK command-line tools..."; \ + wget -q https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip; \ + unzip -q commandlinetools-linux-11076708_latest.zip; \ + mkdir -p cmdline-tools/latest; \ + mv cmdline-tools/* cmdline-tools/latest/ 2>/dev/null || true; \ + rm commandlinetools-linux-11076708_latest.zip; \ + fi + @echo "Accepting SDK licenses..." + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$$PATH && \ + yes | sdkmanager --licenses > /dev/null 2>&1 + @echo "Installing platform tools and build tools..." + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$$PATH && \ + sdkmanager "platform-tools" "platforms;android-34" "build-tools;34.0.0" > /dev/null 2>&1 + @echo "✓ Android SDK setup complete!" + @echo "Add to your ~/.bashrc or ~/.zshrc:" + @echo " export ANDROID_HOME=$(ANDROID_HOME)" + @echo " export PATH=\$$ANDROID_HOME/platform-tools:\$$PATH" + +check-device: ## Check if device is connected + @echo "Checking for connected devices..." + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/platform-tools:$$PATH && \ + adb devices + +devices: check-device ## List connected devices (alias for check-device) + +setup-emulator: ## Create and start Android emulator + @echo "Setting up Android emulator..." + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$$PATH && \ + sdkmanager "system-images;android-34;google_apis;x86_64" > /dev/null 2>&1 + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$$PATH && \ + avdmanager create avd -n "CrklEmulator" -k "system-images;android-34;google_apis;x86_64" -d "pixel_7" --force + @echo "✓ Emulator created. Run 'make emulator' to start it." + +emulator: ## Start Android emulator + @echo "Starting Android emulator with 4GB RAM..." + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/emulator:$(ANDROID_HOME)/platform-tools:$$PATH && \ + emulator -avd CrklEmulator -memory 4096 -cores 4 -no-audio -gpu swiftshader_indirect -no-metrics & + +emulator-stop: ## Stop emulator + @echo "Stopping emulator..." + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/platform-tools:$$PATH && \ + adb emu kill + +emulator-list: ## List available emulators + @echo "Available emulators:" + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/cmdline-tools/latest/bin:$(ANDROID_HOME)/platform-tools:$$PATH && \ + avdmanager list avd + build: ## Build APK @echo "Building Crkl..." @$(GRADLEW) assembleDebug install: build ## Install to phone @echo "Installing to phone..." - @$(ADB) install -r $(APK) + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/platform-tools:$$PATH && \ + $(ADB) install -r $(APK) @echo "✓ Installed" run: ## Launch app @echo "Launching Crkl..." - @$(ADB) shell am start -n com.example.crkl/.MainActivity + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/platform-tools:$$PATH && \ + $(ADB) shell am start -n com.example.crkl/.MainActivity logs: ## Watch app logs @echo "Watching logs (Ctrl+C to stop)..." - @$(ADB) logcat -s CrklAccessibilityService:D OverlayView:D AndroidRuntime:E + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/platform-tools:$$PATH && \ + $(ADB) logcat -s CrklAccessibilityService:D OverlayView:D AndroidRuntime:E stop: ## Stop app - @$(ADB) shell am force-stop com.example.crkl + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/platform-tools:$$PATH && \ + $(ADB) shell am force-stop com.example.crkl clean: ## Clean build @$(GRADLEW) clean uninstall: ## Remove app - @$(ADB) uninstall com.example.crkl + @export ANDROID_HOME=$(ANDROID_HOME) && \ + export PATH=$(ANDROID_HOME)/platform-tools:$$PATH && \ + $(ADB) uninstall com.example.crkl diff --git a/README.md b/README.md index 568e83e..359fc3f 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,21 @@ Privacy-first Android AI assistant that lets users circle or touch any element o ## Quick Start -### 1. Connect Your Android Phone +### 1. Setup Development Environment +```bash +# One-time setup (installs Android SDK and tools) +./setup.sh +``` + +### 2. Connect Your Android Device See [PHONE_SETUP.md](PHONE_SETUP.md) for detailed setup instructions. -### 2. Build and Install +### 3. Build and Install ```bash -make build # Build APK -make install # Install to phone -make run # Launch app +make check-device # Check if device is connected +make build # Build APK +make install # Install to device +make run # Launch app ``` ### 3. Enable Accessibility Service @@ -34,9 +41,17 @@ Touch your phone screen anywhere - you'll see touch detection logs and visual fe ## Commands +### Setup Commands +```bash +./setup.sh # One-time setup (installs Android SDK) +make setup-sdk # Install Android SDK and tools +make check-device # Check if device is connected +``` + +### Development Commands ```bash make build # Build APK -make install # Install to phone +make install # Install to device make run # Launch app make logs # Watch app logs make stop # Stop app @@ -44,6 +59,11 @@ make clean # Clean build make uninstall # Remove app ``` +### Help +```bash +make help # Show all available commands +``` + ## Project Structure ``` diff --git a/app/src/main/kotlin/com/example/crkl/accessibility/CrklAccessibilityService.kt b/app/src/main/kotlin/com/example/crkl/accessibility/CrklAccessibilityService.kt index 1e24bf2..3a935ae 100644 --- a/app/src/main/kotlin/com/example/crkl/accessibility/CrklAccessibilityService.kt +++ b/app/src/main/kotlin/com/example/crkl/accessibility/CrklAccessibilityService.kt @@ -1,12 +1,9 @@ package com.example.crkl.accessibility import android.accessibilityservice.AccessibilityService -import android.accessibilityservice.GestureDescription import android.graphics.PixelFormat import android.util.Log import android.view.Gravity -import android.view.MotionEvent -import android.view.View import android.view.WindowManager import android.view.accessibility.AccessibilityEvent import kotlinx.coroutines.CoroutineScope @@ -17,10 +14,12 @@ import kotlinx.coroutines.cancel /** * Crkl Accessibility Service * - * This service creates a system-wide overlay that captures user gestures + * This service monitors accessibility events to detect user interactions * and provides AI-powered assistance based on selected screen content. * * Privacy: All processing is local. No data leaves the device. + * + * Note: No overlay is created to avoid blocking touch events. */ class CrklAccessibilityService : AccessibilityService() { @@ -33,36 +32,90 @@ class CrklAccessibilityService : AccessibilityService() { super.onServiceConnected() Log.d(TAG, "Crkl Accessibility Service connected") - // Initialize overlay - setupOverlay() + // Create floating action button for Crkl activation + setupFloatingButton() + Log.d(TAG, "Service ready - floating button created") } - private fun setupOverlay() { + private fun setupFloatingButton() { try { windowManager = getSystemService(WINDOW_SERVICE) as WindowManager - // Create overlay view - overlayView = OverlayView(this) + // Create overlay view with callback for mode changes + overlayView = OverlayView(this) { isOverlayMode -> + updateOverlayMode(isOverlayMode) + } - // Configure window layout parameters for accessibility overlay + // Configure window layout parameters for floating button val params = WindowManager.LayoutParams( - WindowManager.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or - WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or - WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH, + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT ) - params.gravity = Gravity.TOP or Gravity.START + // Position in bottom right corner + params.gravity = Gravity.BOTTOM or Gravity.END + params.x = 50 + params.y = 100 - // Add overlay to window manager + // Add floating button to window manager windowManager?.addView(overlayView, params) - Log.d(TAG, "Overlay created successfully") + Log.d(TAG, "Floating button created successfully") } catch (e: Exception) { - Log.e(TAG, "Error creating overlay", e) + Log.e(TAG, "Error creating floating button", e) + } + } + + private fun updateOverlayMode(isOverlayMode: Boolean) { + try { + overlayView?.let { view -> + windowManager?.let { wm -> + // Remove current view + wm.removeView(view) + + // Create new parameters based on mode + val params = if (isOverlayMode) { + // Full screen overlay parameters + WindowManager.LayoutParams( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, + PixelFormat.TRANSLUCENT + ).apply { + gravity = Gravity.TOP or Gravity.LEFT + x = 0 + y = 0 + } + } else { + // Floating button parameters + WindowManager.LayoutParams( + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY, + WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or + WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, + PixelFormat.TRANSLUCENT + ).apply { + gravity = Gravity.BOTTOM or Gravity.END + x = 50 + y = 100 + } + } + + // Add view with new parameters + wm.addView(view, params) + Log.d(TAG, "Overlay mode updated: ${if (isOverlayMode) "full-screen" else "floating button"}") + } + } + } catch (e: Exception) { + Log.e(TAG, "Error updating overlay mode", e) } } @@ -84,7 +137,7 @@ class CrklAccessibilityService : AccessibilityService() { super.onDestroy() Log.d(TAG, "Service destroyed") - // Clean up overlay + // Clean up floating button overlayView?.let { windowManager?.removeView(it) overlayView = null diff --git a/app/src/main/kotlin/com/example/crkl/accessibility/OverlayView.kt b/app/src/main/kotlin/com/example/crkl/accessibility/OverlayView.kt index 031315b..de0280d 100644 --- a/app/src/main/kotlin/com/example/crkl/accessibility/OverlayView.kt +++ b/app/src/main/kotlin/com/example/crkl/accessibility/OverlayView.kt @@ -6,111 +6,245 @@ import android.graphics.Color import android.graphics.Paint import android.graphics.PointF import android.util.Log +import android.view.Gravity +import android.view.View.MeasureSpec import android.view.MotionEvent import android.view.View +import android.widget.Toast +import kotlin.math.sqrt /** - * Overlay View for gesture capture + * Crkl Overlay View * - * This view is displayed over all other apps and captures user touch gestures. - * It draws a visual indicator when the user circles or touches the screen. - * - * Phase 1 (POC): Simple tap detection and visual feedback - * Phase 2: Circle gesture recognition - * Phase 3: Region extraction and content analysis + * This view provides a floating action button that toggles to a full-screen overlay + * for capturing touch gestures and circle selections. It reports selection bounds + * for content analysis. */ -class OverlayView(context: Context) : View(context) { +class OverlayView( + context: Context, + private val onModeChanged: (Boolean) -> Unit = {} +) : View(context) { private val TAG = "OverlayView" - // Paint for drawing touch indicators - private val circlePaint = Paint().apply { - color = Color.parseColor("#8003DAC6") // Semi-transparent secondary color - style = Paint.Style.STROKE - strokeWidth = 8f - isAntiAlias = true - } - - private val fillPaint = Paint().apply { - color = Color.parseColor("#2003DAC6") // Very transparent fill + // Button properties + private val buttonSize = 80f + private var buttonColor = Color.BLUE + private val buttonPaint = Paint().apply { + color = buttonColor style = Paint.Style.FILL isAntiAlias = true } - // Touch tracking - private var touchPoint: PointF? = null - private var isActive = false - private val touchRadius = 80f + // Overlay mode properties + private var isOverlayMode = false + private val overlayPaint = Paint().apply { + color = Color.argb(128, 0, 0, 0) // Semi-transparent black + style = Paint.Style.FILL + } + + private val instructionPaint = Paint().apply { + color = Color.WHITE + textSize = 48f + textAlign = Paint.Align.CENTER + isAntiAlias = true + } + + // Drawing properties + private var isDrawing = false + private var touchPath = mutableListOf() + private val pathPaint = Paint().apply { + color = Color.YELLOW + strokeWidth = 8f + style = Paint.Style.STROKE + isAntiAlias = true + } + + // Selection bounds tracking + private var selectionBounds: android.graphics.RectF? = null + private val boundsPaint = Paint().apply { + color = Color.GREEN + strokeWidth = 4f + style = Paint.Style.STROKE + isAntiAlias = true + } + + private val textPaint = Paint().apply { + color = Color.WHITE + textSize = 32f + textAlign = Paint.Align.CENTER + isAntiAlias = true + } init { - // Make view transparent setBackgroundColor(Color.TRANSPARENT) - Log.d(TAG, "OverlayView initialized") + isClickable = true + isFocusable = true + isFocusableInTouchMode = true + + setOnClickListener { + Log.d(TAG, "Crkl floating button clicked!") + toggleOverlayMode() + } + + Log.d(TAG, "Simple floating button initialized") + } + + private fun toggleOverlayMode() { + isOverlayMode = !isOverlayMode + if (isOverlayMode) { + Log.d(TAG, "Overlay mode activated - draw a circle to analyze content") + showToast("Draw a circle to analyze content!") + } else { + Log.d(TAG, "Overlay mode deactivated") + showToast("Overlay mode disabled") + touchPath.clear() + selectionBounds = null + } + onModeChanged(isOverlayMode) + invalidate() + } + + private fun showToast(message: String) { + val toast = Toast.makeText(context, message, Toast.LENGTH_LONG) + toast.setGravity(Gravity.TOP or Gravity.CENTER_HORIZONTAL, 0, 200) + toast.show() + Log.d(TAG, "Toast shown: $message") } override fun onTouchEvent(event: MotionEvent?): Boolean { - event ?: return false - - when (event.action) { - MotionEvent.ACTION_DOWN -> { - // User started touching - touchPoint = PointF(event.x, event.y) - isActive = true - invalidate() // Request redraw - Log.d(TAG, "Touch down at (${event.x}, ${event.y})") - } - - MotionEvent.ACTION_MOVE -> { - // User is dragging (future: track path for circle gesture) - touchPoint = PointF(event.x, event.y) - invalidate() - } - - MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { - // Touch ended - Log.d(TAG, "Touch up at (${event.x}, ${event.y})") - - // For POC: just show we detected a tap - Log.d(TAG, "POC: Tap detected! Future: analyze content at this location") - - // Clear after a moment - postDelayed({ - isActive = false - touchPoint = null - invalidate() - }, 500) - } + if (!isOverlayMode) { + // In button mode, only handle clicks on the button itself + return super.onTouchEvent(event) } - // Return false to allow touch events to pass through to underlying apps - // In production, we'll want smarter handling based on gesture state - return false + // In overlay mode, handle drawing + event?.let { motionEvent -> + when (motionEvent.action) { + MotionEvent.ACTION_DOWN -> { + isDrawing = true + touchPath.clear() + touchPath.add(PointF(motionEvent.x, motionEvent.y)) + Log.d(TAG, "Drawing started at (${motionEvent.x}, ${motionEvent.y})") + } + MotionEvent.ACTION_MOVE -> { + if (isDrawing) { + touchPath.add(PointF(motionEvent.x, motionEvent.y)) + } + } + MotionEvent.ACTION_UP -> { + isDrawing = false + Log.d(TAG, "Drawing ended. Path points: ${touchPath.size}") + + if (isCircleGesture()) { + Log.d(TAG, "Circle detected!") + calculateSelectionBounds() + showToast("Circle detected! Analyzing content...") + reportSelectionBounds() + } + } + } + invalidate() + } + return true // Consume touch events in overlay mode + } + + private fun isCircleGesture(): Boolean { + if (touchPath.size < 10) return false + + val firstPoint = touchPath.first() + val lastPoint = touchPath.last() + val dx = (lastPoint.x - firstPoint.x).toDouble() + val dy = (lastPoint.y - firstPoint.y).toDouble() + val distance = sqrt(dx * dx + dy * dy) + + return distance < 100f // Circle is closed if start and end are close + } + + private fun calculateSelectionBounds() { + if (touchPath.isEmpty()) return + + var minX = Float.MAX_VALUE + var maxX = Float.MIN_VALUE + var minY = Float.MAX_VALUE + var maxY = Float.MIN_VALUE + + for (point in touchPath) { + minX = minOf(minX, point.x) + maxX = maxOf(maxX, point.x) + minY = minOf(minY, point.y) + maxY = maxOf(maxY, point.y) + } + + selectionBounds = android.graphics.RectF(minX, minY, maxX, maxY) + Log.d(TAG, "Selection bounds calculated: $selectionBounds") + } + + private fun reportSelectionBounds() { + selectionBounds?.let { bounds -> + Log.d(TAG, "Selection bounds reported: left=${bounds.left}, top=${bounds.top}, right=${bounds.right}, bottom=${bounds.bottom}") + Log.d(TAG, "Selection area: ${bounds.width()} x ${bounds.height()}") + // TODO: In future, this will trigger content analysis at these bounds + } } override fun onDraw(canvas: Canvas) { super.onDraw(canvas) - // Only draw when user is actively touching - if (isActive && touchPoint != null) { - val point = touchPoint!! - - // Draw circle at touch point - canvas.drawCircle(point.x, point.y, touchRadius, fillPaint) - canvas.drawCircle(point.x, point.y, touchRadius, circlePaint) - - // Draw crosshair for precision - val crosshairSize = 20f - canvas.drawLine( - point.x - crosshairSize, point.y, - point.x + crosshairSize, point.y, - circlePaint - ) - canvas.drawLine( - point.x, point.y - crosshairSize, - point.x, point.y + crosshairSize, - circlePaint - ) + if (isOverlayMode) { + drawOverlayMode(canvas) + } else { + drawButtonMode(canvas) } } -} - + + private fun drawButtonMode(canvas: Canvas) { + // Draw button background + val centerX = width / 2f + val centerY = height / 2f + canvas.drawCircle(centerX, centerY, buttonSize / 2, buttonPaint) + + // Draw "C" text + val textY = centerY + (textPaint.textSize / 3) + canvas.drawText("C", centerX, textY, textPaint) + } + + private fun drawOverlayMode(canvas: Canvas) { + // Draw semi-transparent overlay + canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), overlayPaint) + + // Draw instructions + canvas.drawText("Draw a circle to analyze content", width / 2f, 200f, instructionPaint) + canvas.drawText("Tap 'C' button again to exit", width / 2f, height - 200f, instructionPaint) + + // Draw the drawing path + if (touchPath.size > 1) { + for (i in 1 until touchPath.size) { + val prev = touchPath[i - 1] + val curr = touchPath[i] + canvas.drawLine(prev.x, prev.y, curr.x, curr.y, pathPaint) + } + } + + // Draw selection bounds if available + selectionBounds?.let { bounds -> + canvas.drawRect(bounds, boundsPaint) + // Draw bounds info + val boundsText = "Selection: ${bounds.width().toInt()}x${bounds.height().toInt()}" + canvas.drawText(boundsText, bounds.centerX(), bounds.top - 20f, textPaint) + } + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + if (isOverlayMode) { + // In overlay mode, use full screen + setMeasuredDimension( + MeasureSpec.getSize(widthMeasureSpec), + MeasureSpec.getSize(heightMeasureSpec) + ) + } else { + // In button mode, use fixed button size + setMeasuredDimension(buttonSize.toInt(), buttonSize.toInt()) + } + } +} \ No newline at end of file diff --git a/app/src/main/res/xml/accessibility_service_config.xml b/app/src/main/res/xml/accessibility_service_config.xml index af47588..ab85c86 100644 --- a/app/src/main/res/xml/accessibility_service_config.xml +++ b/app/src/main/res/xml/accessibility_service_config.xml @@ -2,9 +2,9 @@ diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..c648143 --- /dev/null +++ b/setup.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# Crkl Setup Script +# This script sets up the Android SDK and environment for Crkl development + +set -e + +echo "🚀 Setting up Crkl development environment..." + +# Check if we're in the right directory +if [ ! -f "Makefile" ]; then + echo "❌ Error: Please run this script from the Crkl project root directory" + exit 1 +fi + +# Setup Android SDK +echo "📱 Setting up Android SDK..." +make setup-sdk + +# Add environment variables to shell profile +SHELL_PROFILE="" +if [ -f "$HOME/.zshrc" ]; then + SHELL_PROFILE="$HOME/.zshrc" +elif [ -f "$HOME/.bashrc" ]; then + SHELL_PROFILE="$HOME/.bashrc" +fi + +if [ -n "$SHELL_PROFILE" ]; then + echo "🔧 Adding environment variables to $SHELL_PROFILE..." + + # Check if already added + if ! grep -q "ANDROID_HOME" "$SHELL_PROFILE"; then + echo "" >> "$SHELL_PROFILE" + echo "# Crkl Android SDK" >> "$SHELL_PROFILE" + echo "export ANDROID_HOME=\$HOME/android-sdk" >> "$SHELL_PROFILE" + echo "export PATH=\$ANDROID_HOME/platform-tools:\$PATH" >> "$SHELL_PROFILE" + echo "✓ Environment variables added to $SHELL_PROFILE" + else + echo "✓ Environment variables already present in $SHELL_PROFILE" + fi +fi + +# Build the project +echo "🔨 Building Crkl APK..." +make build + +echo "" +echo "✅ Setup complete!" +echo "" +echo "Next steps:" +echo "1. Connect your Android tablet/phone via USB" +echo "2. Enable Developer Options and USB Debugging on your device" +echo "3. Run: make check-device" +echo "4. Run: make install" +echo "5. Run: make run" +echo "" +echo "For help, run: make help" \ No newline at end of file