Enhance Makefile, README, and setup script for improved development workflow

- Updated Makefile to include new setup commands for Android SDK and emulator management.
- Revised README.md to reflect changes in setup instructions and command usage.
- Introduced setup.sh script for one-time environment setup, including SDK installation and configuration.
- Modified CrklAccessibilityService to implement a floating action button instead of a full-screen overlay.
- Enhanced OverlayView to support toggling between button and overlay modes for gesture capture.
- Adjusted accessibility service configuration to disable touch exploration mode for better user experience.
This commit is contained in:
ilia 2025-10-21 12:55:39 -04:00
parent c351858749
commit 1d0c1cfb8a
6 changed files with 462 additions and 115 deletions

View File

@ -1,5 +1,5 @@
# Crkl - Physical Phone Development # 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 ANDROID_HOME ?= $(HOME)/android-sdk
ADB := $(ANDROID_HOME)/platform-tools/adb ADB := $(ANDROID_HOME)/platform-tools/adb
@ -7,10 +7,22 @@ GRADLEW := ./gradlew
APK := app/build/outputs/apk/debug/app-debug.apk APK := app/build/outputs/apk/debug/app-debug.apk
help: ## Show available commands help: ## Show available commands
@echo "Crkl - Physical Phone Commands" @echo "Crkl - Android Development Commands"
@echo "" @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 build - Build APK"
@echo " make install - Install to phone" @echo " make install - Install to device/emulator"
@echo " make run - Launch app" @echo " make run - Launch app"
@echo " make logs - Watch app logs" @echo " make logs - Watch app logs"
@echo " make stop - Stop app" @echo " make stop - Stop app"
@ -18,28 +30,99 @@ help: ## Show available commands
@echo " make uninstall - Remove app" @echo " make uninstall - Remove app"
@echo "" @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 build: ## Build APK
@echo "Building Crkl..." @echo "Building Crkl..."
@$(GRADLEW) assembleDebug @$(GRADLEW) assembleDebug
install: build ## Install to phone install: build ## Install to phone
@echo "Installing 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" @echo "✓ Installed"
run: ## Launch app run: ## Launch app
@echo "Launching Crkl..." @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 logs: ## Watch app logs
@echo "Watching logs (Ctrl+C to stop)..." @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 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 clean: ## Clean build
@$(GRADLEW) clean @$(GRADLEW) clean
uninstall: ## Remove app 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

View File

@ -10,14 +10,21 @@ Privacy-first Android AI assistant that lets users circle or touch any element o
## Quick Start ## 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. See [PHONE_SETUP.md](PHONE_SETUP.md) for detailed setup instructions.
### 2. Build and Install ### 3. Build and Install
```bash ```bash
make build # Build APK make check-device # Check if device is connected
make install # Install to phone make build # Build APK
make run # Launch app make install # Install to device
make run # Launch app
``` ```
### 3. Enable Accessibility Service ### 3. Enable Accessibility Service
@ -34,9 +41,17 @@ Touch your phone screen anywhere - you'll see touch detection logs and visual fe
## Commands ## 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 ```bash
make build # Build APK make build # Build APK
make install # Install to phone make install # Install to device
make run # Launch app make run # Launch app
make logs # Watch app logs make logs # Watch app logs
make stop # Stop app make stop # Stop app
@ -44,6 +59,11 @@ make clean # Clean build
make uninstall # Remove app make uninstall # Remove app
``` ```
### Help
```bash
make help # Show all available commands
```
## Project Structure ## Project Structure
``` ```

View File

@ -1,12 +1,9 @@
package com.example.crkl.accessibility package com.example.crkl.accessibility
import android.accessibilityservice.AccessibilityService import android.accessibilityservice.AccessibilityService
import android.accessibilityservice.GestureDescription
import android.graphics.PixelFormat import android.graphics.PixelFormat
import android.util.Log import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager import android.view.WindowManager
import android.view.accessibility.AccessibilityEvent import android.view.accessibility.AccessibilityEvent
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -17,10 +14,12 @@ import kotlinx.coroutines.cancel
/** /**
* Crkl Accessibility Service * 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. * and provides AI-powered assistance based on selected screen content.
* *
* Privacy: All processing is local. No data leaves the device. * Privacy: All processing is local. No data leaves the device.
*
* Note: No overlay is created to avoid blocking touch events.
*/ */
class CrklAccessibilityService : AccessibilityService() { class CrklAccessibilityService : AccessibilityService() {
@ -33,36 +32,90 @@ class CrklAccessibilityService : AccessibilityService() {
super.onServiceConnected() super.onServiceConnected()
Log.d(TAG, "Crkl Accessibility Service connected") Log.d(TAG, "Crkl Accessibility Service connected")
// Initialize overlay // Create floating action button for Crkl activation
setupOverlay() setupFloatingButton()
Log.d(TAG, "Service ready - floating button created")
} }
private fun setupOverlay() { private fun setupFloatingButton() {
try { try {
windowManager = getSystemService(WINDOW_SERVICE) as WindowManager windowManager = getSystemService(WINDOW_SERVICE) as WindowManager
// Create overlay view // Create overlay view with callback for mode changes
overlayView = OverlayView(this) overlayView = OverlayView(this) { isOverlayMode ->
updateOverlayMode(isOverlayMode)
}
// Configure window layout parameters for accessibility overlay // Configure window layout parameters for floating button
val params = WindowManager.LayoutParams( val params = WindowManager.LayoutParams(
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY, WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL or WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT 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) windowManager?.addView(overlayView, params)
Log.d(TAG, "Overlay created successfully") Log.d(TAG, "Floating button created successfully")
} catch (e: Exception) { } 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() super.onDestroy()
Log.d(TAG, "Service destroyed") Log.d(TAG, "Service destroyed")
// Clean up overlay // Clean up floating button
overlayView?.let { overlayView?.let {
windowManager?.removeView(it) windowManager?.removeView(it)
overlayView = null overlayView = null

View File

@ -6,111 +6,245 @@ import android.graphics.Color
import android.graphics.Paint import android.graphics.Paint
import android.graphics.PointF import android.graphics.PointF
import android.util.Log import android.util.Log
import android.view.Gravity
import android.view.View.MeasureSpec
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View 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. * This view provides a floating action button that toggles to a full-screen overlay
* It draws a visual indicator when the user circles or touches the screen. * for capturing touch gestures and circle selections. It reports selection bounds
* * for content analysis.
* Phase 1 (POC): Simple tap detection and visual feedback
* Phase 2: Circle gesture recognition
* Phase 3: Region extraction and content analysis
*/ */
class OverlayView(context: Context) : View(context) { class OverlayView(
context: Context,
private val onModeChanged: (Boolean) -> Unit = {}
) : View(context) {
private val TAG = "OverlayView" private val TAG = "OverlayView"
// Paint for drawing touch indicators // Button properties
private val circlePaint = Paint().apply { private val buttonSize = 80f
color = Color.parseColor("#8003DAC6") // Semi-transparent secondary color private var buttonColor = Color.BLUE
style = Paint.Style.STROKE private val buttonPaint = Paint().apply {
strokeWidth = 8f color = buttonColor
isAntiAlias = true
}
private val fillPaint = Paint().apply {
color = Color.parseColor("#2003DAC6") // Very transparent fill
style = Paint.Style.FILL style = Paint.Style.FILL
isAntiAlias = true isAntiAlias = true
} }
// Touch tracking // Overlay mode properties
private var touchPoint: PointF? = null private var isOverlayMode = false
private var isActive = false private val overlayPaint = Paint().apply {
private val touchRadius = 80f 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<PointF>()
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 { init {
// Make view transparent
setBackgroundColor(Color.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 { override fun onTouchEvent(event: MotionEvent?): Boolean {
event ?: return false if (!isOverlayMode) {
// In button mode, only handle clicks on the button itself
when (event.action) { return super.onTouchEvent(event)
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)
}
} }
// Return false to allow touch events to pass through to underlying apps // In overlay mode, handle drawing
// In production, we'll want smarter handling based on gesture state event?.let { motionEvent ->
return false 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) { override fun onDraw(canvas: Canvas) {
super.onDraw(canvas) super.onDraw(canvas)
// Only draw when user is actively touching if (isOverlayMode) {
if (isActive && touchPoint != null) { drawOverlayMode(canvas)
val point = touchPoint!! } else {
drawButtonMode(canvas)
// 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
)
} }
} }
}
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())
}
}
}

View File

@ -2,9 +2,9 @@
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:description="@string/accessibility_service_description" android:description="@string/accessibility_service_description"
android:accessibilityEventTypes="typeAllMask" android:accessibilityEventTypes="typeAllMask"
android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows|flagRequestTouchExplorationMode" android:accessibilityFlags="flagDefault|flagRetrieveInteractiveWindows"
android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFeedbackType="feedbackGeneric"
android:canRetrieveWindowContent="true" android:canRetrieveWindowContent="true"
android:canRequestTouchExplorationMode="true" android:canRequestTouchExplorationMode="false"
android:notificationTimeout="100" /> android:notificationTimeout="100" />

57
setup.sh Executable file
View File

@ -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"