Thank you for contributing! This guide ensures your work aligns with the project's safety, security, and quality standards.
This app runs as an overlay on top of every other app on the infotainment screen. Incorrectly capturing, blocking, or misinterpreting swipe gestures can:
- Block the driver's access to navigation, phone, or safety-critical functions
- Cause the app in the foreground to miss intended input
- Create security vulnerabilities where malicious apps hijack the overlay
Every PR touching gesture detection or overlay rendering must be tested on the actual vehicle.
- The accessibility service stays minimal.
AccServicereadsTYPE_WINDOW_STATE_CHANGEDonly (learns foreground package), never window content.canRetrieveWindowContent="false"inres/xml/accessibility_service_config.xmlis non-negotiable. - The app holds no vehicle privileges. No
android.car.*, nosharedUserId, no IPC to EVProfile. Vehicle work → EVProfile. Automation → EVTasker. - The overlay must not eat touches it doesn't own. Swipe strips are minimal and pass touches through outside the gesture area. Never block input the driver intended for the foreground app.
- Stable stays offline. No
INTERNETpermission in stable flavor. All network code insrc/unstable/behindBuildConfig.OTA_ENABLED. - Be transparent about testing. Say what you verified and what you didn't. "Passes CI, emulator OK, not tested on vehicle" is a good PR note.
# Check before opening a PR
mise run check # permission gate + lint + unit tests
# Or without mise:
bash .github/security/check-permissions.sh
./gradlew testStableDebugUnitTest testUnstableDebugUnitTest lintStableDebug
# Deploy to emulator (auto-grants overlay + accessibility)
mise run run- Unit Tests: Gesture coordinates, config persistence, permissions
- Emulator Tests: Swipes from all edges, overlay responsiveness, no false positives
- Vehicle Tests: Real touch input, concurrent apps, different firmware versions (SWI68, SWI133, etc.)
# On emulator or device:
# 1. Enable overlay (app notification → "Start" or settings)
# 2. Swipe from left edge → should open launcher
# 3. Swipe from right edge → should trigger configured action
# 4. Swipe from top/bottom edges → test if configured
# 5. Open maps + swipe → verify maps receives touches outside strip
# 6. No false positives while using other apps// ✅ GOOD: Minimal accessibility service, gesture detection only
class AccService : AccessibilityService() {
override fun onAccessibilityEvent(event: AccessibilityEvent) {
if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
val packageName = event.packageName?.toString()
handleWindowChange(packageName)
}
}
// No window content reading, no hierarchy access
}
// ✅ GOOD: Overlay passes touches through
val params = WindowManager.LayoutParams(
TYPE_ACCESSIBILITY_OVERLAY, // Minimal overlay type
FLAG_NOT_TOUCHABLE or FLAG_NOT_FOCUSABLE // Pass touches through
)
// ❌ BAD: Reading window content (PROHIBITED)
override fun onAccessibilityEvent(event: AccessibilityEvent) {
val windowContent = event.source?.text // NO! violates security model
}
// ❌ BAD: Blocking touches
val params = WindowManager.LayoutParams(
TYPE_ACCESSIBILITY_OVERLAY,
0 // Not pass-through → blocks driver input!
)// ✅ GOOD: Validate configuration with bounds
fun setStripWidth(width: Int): Boolean {
if (width < MIN_STRIP_PX || width > MAX_STRIP_PX) return false
preferences.edit().putInt("strip_width", width).apply()
return true
}
// ✅ GOOD: Sanitize user strings
fun setGestureLabel(label: String) {
val sanitized = label
.take(MAX_LABEL_LEN)
.filter { it.isLetterOrDigit() || it.isWhitespace() }
preferences.edit().putString("label", sanitized).apply()
}
// ❌ BAD: No bounds checking (can crash or set invalid state)
fun setStripWidth(width: String) {
preferences.edit().putInt("strip_width", width.toInt()).apply()
}
// ❌ BAD: Trusting user input (prompt injection)
fun setGestureLabel(label: String) {
preferences.edit().putString("label", label).apply() // Injection risk!
}- Use
HandlerorCoroutinesfor background gesture processing - Never block UI thread with gesture detection
- Synchronize shared gesture state access
- Before opening: Run
mise run check— must pass - Create a feature branch:
git checkout -b feature/gesture-sensitivity git commit -m "Add: gesture sensitivity slider" - Push and open PR using the PULL_REQUEST_TEMPLATE.md
- Address feedback — push additional commits, no force-push
- Merge when approved — squash if appropriate
- What and Why: Clear problem statement and solution rationale
- Verification: Honest list of what you tested (emulator, vehicle, CI)
- Gesture Testing: If gesture detection changed, detail all edge cases tested
- Overlay/Rendering: If overlay display changed, verify all screens
- Security Checklist: Accessibility config, permissions, input validation
- Claude AI Review: Optional — request AI assistance for code review
The overlay MUST NOT:
- Capture input intended for the foreground app
- Read window content or app hierarchy
- Intercept navigation or phone calls
- Persist across uninstall (data in
SharedPreferencesis cleared by Android)
Verify before PR:
adb shell dumpsys accessibility | grep -i "EVSwipe"
# Expected: canRetrieveWindowContent: false
# EVSwipe accessibility enabledEvery new uses-permission fails CI until added to .github/security/permission-allowlist.txt with justification:
SYSTEM_ALERT_WINDOW → "Draw swipe strip overlay over other apps"
BIND_ACCESSIBILITY_SERVICE → "Detect swipe gestures (TYPE_WINDOW_STATE_CHANGED only)"
Request install packages: Deliberately absent. Unstable updater downloads APK; user taps to install.
All user input must be:
- Type-checked — integer, string length, enum values
- Bounds-checked — screen dimensions, valid ranges
- Sanitized — no special characters, no escape sequences
- Tested — edge cases: 0, negative, max int, empty strings, Unicode
If you want Claude AI to help refine your issue or PR:
- Add a comment:
@Claude review-clarity - Claude will clarify and improve issue wording
- You approve before closure
- In PR template: Check "I request automated code review from Claude AI"
- Provide a refinement prompt:
Claude, please review this gesture detection change for: - Correctness of coordinate bounds checking - Overlay pass-through logic (doesn't block input outside strip) - Security checklist compliance - Input validation edge cases
- ✅ Clarify issue descriptions
- ✅ Review code for security patterns
- ✅ Catch prompt injection and input validation gaps
- ✅ Suggest efficiency improvements
- ✅ Help write tests
- ❌ Test on actual vehicle hardware (humans only)
- ❌ Approve merges (maintainers only)
- ❌ Override safety requirements
Data Usage: Claude may learn from your contributions. By using Claude AI assistance, you agree to GitHub's terms.
Not every useful contribution is code:
- Bug reports — firmware generation, app version, what you did, what happened
- Feature requests — describe the problem before the solution
- Documentation — README, this guide, translations
- Pull requests — see below
- Testing pre-releases — install an
unstablebuild and report what broke - Sponsorship — see below
Maintainer time and AI quota are the scarce resources here, ideas are not. If you have access to Claude Opus or another capable coding model, a finished pull request is worth much more than a feature request: someone still has to design, write, test and verify the request, and that someone has a limited quota too.
A pull request that lands quickly usually carries:
- The problem, in one or two sentences
- The proposed solution, and what you rejected
- The implementation, scoped to one concern
- Tests —
mise run checkpasses - Documentation updated: README, CHANGELOG, this guide where relevant
Generate the change locally with whatever model you have, then read every line yourself before opening the PR. You are the author, not the model. Unreviewed generated code on a path that reaches the vehicle will be sent back.
Maintaining EVSuite costs development time, test hardware and AI usage. If it is useful in your daily driving, consider sponsoring through GitHub Sponsors. Sponsorship covers those costs and gets fixes and features out faster.
EVSwipe/
├── app/
│ ├── src/main/ # Core overlay & gesture detection (stable)
│ ├── src/unstable/ # OTA updater (unstable flavor only)
│ └── res/xml/
│ └── accessibility_service_config.xml # Do NOT widen this!
├── .github/
│ ├── ISSUE_TEMPLATE/ # Bug, feature, security templates
│ ├── security/ # Permission allowlist, security gate
│ └── workflows/ # CI/CD (security.yml, tests.yml, release.yml)
├── SECURITY.md # Vulnerability reporting (private)
├── DISCLAIMER.md # Legal disclaimer
└── CONTRIBUTING.md # This file
- SECURITY.md — Vulnerability reporting & security policy
- DISCLAIMER.md — Legal disclaimer (not affiliated with SAIC/MG Motor)
- README.md — Project overview
- LICENSE.md — License terms
- General questions? Open a Discussion or ask in an issue
- Security issues? See SECURITY.md for private reporting
- Build problems? Check the README or open an issue
Thank you for contributing safely and securely! 🎉