feat(android): Android mobile platform support#2988
feat(android): Android mobile platform support#2988StarbirdTech wants to merge 23 commits intospacedriveapp:mainfrom
Conversation
3be4e89 to
672263f
Compare
- Add @types/react and @types/react-dom overrides to root package.json - Update packages/assets exports for icons, images, svgs, sounds, videos, lottie - Add additional ts-client exports for hooks and generated types - Move react and @tanstack/react-query to devDependencies in ts-client - Optimize metro.config.js watch folders to avoid watching Rust target/ dirs - Resolve React from workspace root (bun hoists it there) - Update bun.lockb Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add packages/assets/types.d.ts for @sd/assets module declarations - icons, images (png/jpg), svgs, videos, sounds, lottie - Add apps/mobile/src/types/assets.d.ts for React Native asset imports - Add apps/mobile/src/types/expo-router.d.ts for unstable native tabs types - Augments expo-router with missing 'name' prop on NativeTabs.Trigger Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…tection Device detection: - Skip sysinfo on mobile platforms (was causing crashes on Android) - Return minimal SystemInfo with architecture and form factor - TODO: Implement with native APIs for richer device info Volume detection: - Add Android volume detection module (core/src/volume/platform/android.rs) - Detect app storage, external storage (/storage/emulated/0), SD cards, USB drives - Use statvfs for capacity queries (Android is Linux-based) - Read device model from /system/build.prop - Check /proc/mounts and /sys/block for removable storage detection Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Library: - Store library ID separately for lock-free access - Avoids potential panic when reading config under contention Location add: - Add safe_canonicalize() that handles Android filesystem edge cases - Falls back to partial canonicalization or raw path if full resolution fails - Add read permission verification before adding location - Add defensive device existence check with race condition documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…error handling Kotlin module (SDMobileCoreModule): - Add pickFolder() using Storage Access Framework (ACTION_OPEN_DOCUMENT_TREE) - Add getPathFromUri() to convert content:// URIs to filesystem paths - Handle primary storage, home directory, and external volumes (SD cards, USB) - Use StorageManager API on Android N+ for reliable path resolution Rust FFI (lib.rs): - Add safe_cstring() to prevent panics from null bytes in strings - Add structured JSON-RPC error responses with error_type and details - Add library ID validation before processing requests - Initialize Android logger for logcat visibility - Wrap JNI callbacks in catch_unwind to prevent panics crossing FFI boundary - Add proper error handling for JavaVM attachment and JNI calls Build: - Auto-detect Android NDK location - Clear conflicting environment variables for cross-compilation - Add androidx.documentfile dependency for DocumentFile API AndroidManifest: - Update storage permissions for Android 13+ (granular media permissions) - Add MANAGE_EXTERNAL_STORAGE for file manager functionality Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…r UI Transport: - Add SpacedriveError class with code, errorType, and details - Add request timeouts (30s default, 2min for long-running operations) - Detect long-running methods (locations.add, libraries.create, jobs.run) - Clean up pending timeouts on destroy() Client hooks: - Add useMobileClient() helper for properly typed mobile client access - Fix type casting between ts-client and mobile client interfaces OverviewScreen: - Implement handleAddStorage() for adding storage locations - Android: Use native SAF folder picker via SDMobileCore.pickFolder() - iOS: Use expo-document-picker - Show success/error alerts with appropriate messaging - Handle permission and access errors gracefully Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…s, and volumes - Add Stack.Screen routes for location, device, and volume detail pages - Implement navigation from Browse screen to detail screens - Add LocationExplorerScreen, DeviceDetailsScreen, and VolumeDetailsScreen - Fix SpaceSwitcher to support space selection state - Improve SettingsGroup children typing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Split tab layout into iOS (NativeTabs) and Android (Tabs) components - iOS uses liquid glass effect with SF Symbols - Android follows Material Design 3 guidelines: - 80dp tab bar height with proper safe area handling - 24dp icons with filled/outline states - Active indicator pill (64x32dp) with animated transitions - Brand-aligned color scheme - Smooth animations using react-native-reanimated: - Pill fade in/out - Icon/label spacing adjustments on focus - 200ms cubic easing for M3 feel Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
These dedicated screens were superseded by the unified /explorer approach. Removed: - Route files: device/[deviceId], location/[locationId], volume/[volumeId] - Screen components: DeviceDetailsScreen, LocationExplorerScreen, VolumeDetailsScreen - Stack.Screen definitions in _layout.tsx Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Replace CSS transition-all with react-native-reanimated for space indicator dots - Add slide_from_right animation for explorer screen navigation - Use consistent 200ms timing across all animations Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add Kotlin static analysis tooling: - detekt v1.23.7 for code quality and complexity checks - ktlint v12.1.2 for code formatting enforcement Includes detekt.yml configuration with rules for: - Complexity thresholds (method length, nesting depth) - Naming conventions - Potential bugs detection - Style consistency Run with: ./gradlew detekt ktlintCheck Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add two new CI jobs for mobile quality gates: 1. android-lint: Runs detekt and ktlint on Kotlin code - Triggers on changes to android/ directories - Uses Java 17 + Bun setup 2. mobile-rust-tests: Runs cargo test on sd-mobile-core - Triggers on changes to mobile core or shared Rust code - Tests FFI layer including safe_cstring and error mapping Both jobs use path filtering to skip when unrelated files change. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Security hardening for Android app data: - Set allowBackup="false" to prevent cloud backup of sensitive data - Add backup_rules.xml excluding databases, prefs, and library data - Add data_extraction_rules.xml for Android 12+ D2D transfer rules This prevents Spacedrive library databases and configuration from being backed up to Google Drive or transferred during device setup, which could expose user file metadata. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Build configuration improvements: Release Signing: - Add signingConfigs.release using environment variables - Required vars: SPACEDRIVE_KEYSTORE_PATH, _PASSWORD, KEY_ALIAS, KEY_PASSWORD - Falls back to debug keystore if not configured - Add SIGNING.md documentation with setup instructions Dynamic Versioning: - versionCode now uses CI build number (GITHUB_RUN_NUMBER) - Falls back to git commit count for local builds - versionName configurable via SPACEDRIVE_VERSION env var This enables proper Play Store releases while allowing development builds without production credentials. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive safety improvements for the Rust FFI layer: Null Pointer Validation: - Add null checks before CStr::from_ptr in initialize_core, handle_core_msg - Return proper error codes/responses instead of undefined behavior Panic Prevention: - Replace .unwrap() on CORE.get() and RUNTIME.get() with match + error - Replace serde_json::to_value().unwrap() with unwrap_or_else - Replace .as_object().unwrap() with safe Option::map pattern Transmute Safety: - Validate callback function pointers are non-zero before transmute - Add null check before Box::from_raw in android_callback Conditional Logging: - Add debug_log!, info_log!, error_log! macros - debug_log compiles to nothing in release builds - Prevents debug output and sensitive data leakage in production Async Timeouts: - Add tokio::time::timeout wrapper to process_daemon_request - 30s default, 120s for long-running methods (locations.add, etc.) - Returns proper JSON-RPC timeout error instead of hanging Unit Tests: - test_safe_cstring_* for null byte handling - test_daemon_error_* for error code mapping Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Thread Safety: - Replace var listeners with AtomicInteger for concurrent access - Replace var registeredWithRust with AtomicBoolean - Replace single pendingFolderPickerPromise with ConcurrentHashMap - Use unique request codes per folder picker call Exception Handling: - Add specific catch blocks (SecurityException, IllegalArgumentException) - Use compareAndSet for atomic flag updates with rollback on failure Permission Checks: - Add hasStoragePermission() for Android 11+ MANAGE_EXTERNAL_STORAGE - Add warnIfNoStoragePermission() to log when permissions missing Path Security: - Add validateAndResolvePath() with canonical path verification - Check each path component for traversal (.. and .) - Verify resolved path stays under expected base directory - Catches symlink-based escape attempts Debug Logging: - Add debugLog() that respects app debuggable flag - Add sanitizePath() that redacts paths in release builds - Initialize debug state from ApplicationInfo flags Deprecation Documentation: - Add @Suppress("DEPRECATION") for startActivityForResult - Document that Expo modules don't yet support ActivityResultContracts Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
transport.ts - Retry Logic: - Add RetryConfig interface with maxRetries, baseDelayMs, backoffMultiplier - Add exponential backoff with jitter (10-20% randomization) - NON_RETRYABLE_ERROR_TYPES list for client errors that shouldn't retry - Split request() into public method with retry and private requestInternal() transport.ts - Health Checks: - Add startHealthCheck() with configurable ping interval - Add onHealthChange() listener for status updates - Add performHealthCheck() for manual checks - Track healthy/unhealthy/unknown status transport.ts - Batch Timeout: - Add batch-level timeout (base + per-request) - Reset batchQueued in finally block for recovery - Reject all batch requests with specific error types on failure subscriptionManager.ts - Cleanup Races: - Add isDestroying flag to prevent cleanup during destruction - Add cleaned flag to SubscriptionEntry to prevent double cleanup - Guard createCleanup with hasRun flag - Defer unsubscribe to next tick with setTimeout - Clear pendingSubscriptions in destroy() SpacedriveClient.ts - Health Integration: - Add startHealthMonitoring() and stopHealthMonitoring() - Add getHealthStatus() and checkHealth() - Add onHealthChange() that also emits 'connection-health' event - Stop health monitoring in destroy() Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Rust FFI: Add safety documentation and allow attributes for pointer dereference functions, remove unused mut - Kotlin: Refactor complex SAF path resolution functions into smaller, focused helpers (parseDocumentId, isRelativePathSafe, resolveViaFallbackMounts, resolveVolumeToPath, etc.) - Kotlin: Add justified suppressions for Expo module API constraints - Detekt: Disable NoTabs rule (project uses tabs for indentation) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Run cargo fmt on files modified in this branch to fix CI formatting check. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add comprehensive handling for Android's MANAGE_EXTERNAL_STORAGE permission: - Add native module functions to check/request storage permission - Add useStoragePermission hook for React Native - Add StoragePermissionBanner component on Overview screen - Add Permissions section in Settings screen - Update Rust LocalBackend to log permission errors (sanitized paths) Without this permission on Android 11+, files are silently skipped during directory listing. Now users see a clear prompt to grant access. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add Android-specific layout with parallax hero that scrolls with content - Hero moves at 0.3x scroll speed with fade effect for depth illusion - Fixed library name header stays pinned at top - MY NETWORK header fades in when scrolled past hero section - Content card overlaps hero with negative margin for slide-over effect - Fix PageIndicator to use reanimated animations instead of CSS transitions - Disable transformOrigin scale animations on Android (not well supported) - Add nestedScrollEnabled to HeroStats for proper horizontal scroll handling - Keep iOS layout unchanged with its complex z-index parallax system Platform-specific layouts needed due to different touch event handling: iOS allows touches through z-index siblings, Android captures all scroll gestures in topmost ScrollView bounds regardless of pointerEvents. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
672263f to
d3424d3
Compare
|
@tembo review again please |
|
@jamiepine I'm working on your request now and will update you when I'm done. |
- Rename release-mobile profile to mobile-dev in build script to match Cargo.toml - Add missing log and android_logger crates for Android build - Fix useNormalizedQuery calls using wrong wireMethod property instead of query - Remove network tab route and screen that kept reappearing from rebases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove hard-coded form_factor for iOS/Android (was misclassifying iPhones as Tablet) - Remove redundant device_exists check that didn't close TOCTOU race - Canonicalize path in execute() to match validation, preventing path mismatch - Improve skipped entries warning to be platform-neutral - Gate Android logger level on debug_assertions to avoid verbose logging in release Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Mobile Feature Config Test ResultsThis comment was written by Claude Code (Opus 4.6) after running the core test suite under the mobile feature configuration to validate that the Android changes don't break existing core functionality. GoalVerify that SetupTo get the test suite compiling, I applied the Results
Tests cover: C string null-byte safety, unicode handling, DaemonError → JSON-RPC error code mapping.
The 3 failures are pre-existing floating-point precision bugs in Integration tests that compiled and ran:
Tests that failed to compile (pre-existing on
ConclusionThe |
|
@jamiepine I tested this branch on both the iOS simulator and my personal phone (Pixel 8a, Android 16). I don't believe I've introduced any changes that cause a regression for iOS. I'm comfortable saying this pr is ready to merge. I'd like to get proper test coverage of the mobile app before claiming it fully "works on Android," but that can be a follow-up PR. Same for polishing the UI to bring it up to par with the iOS version. Also not sure on the state of the Android CI added in this pr, but I don't think i can run that myself (?). |

Adds native Android support to Spacedrive's mobile app. This PR covers Android platform detection in the Rust core, a Kotlin native module with JNI bindings, platform-specific UI with Material Design 3 styling, and production infrastructure (CI, security, signing).
Non-Android-specific changes were extracted into separate PRs to keep this diff focused:
What this adds
Core Rust (Android platform support)
core/src/volume/platform/android.rs— Android volume detection (359 lines)core/src/volume/platform/mod.rs,core/src/volume/detection.rs,core/src/volume/backend/local.rs— Android integrationcore/src/domain/device.rs— skip sysinfo on Android/iOS (SELinux crash fix)core/src/ops/locations/add/action.rs—safe_canonicalize()for Android paths + permission checkscore/src/library/{mod,manager}.rs— lock-free library ID access for mobile reliabilityAndroid build & config
apps/mobile/android/files (manifest, gradle, detekt, signing docs, backup rules)apps/mobile/modules/sd-mobile-core/android/(native Kotlin module, build scripts)apps/mobile/modules/sd-mobile-core/core/src/lib.rs— JNI bindings.github/workflows/ci.yml— Android lint/test CI jobsMobile frontend
apps/mobile/src/app/(drawer)/(tabs)/_layout.tsx— platform-specific tab navigationapps/mobile/src/screens/overview/OverviewScreen.tsx— M3 collapsing header + storage pickerapps/mobile/src/client/— transport, client, subscription improvementsapps/mobile/src/hooks/useStoragePermission.ts+StoragePermissionBanner.tsxiOS side-effects
apps/mobile/ios/Podfile.lock— updated depsapps/mobile/ios/Spacedrive.xcodeproj/project.pbxprojSecurity
Stats
21 commits, 47 files, +4,267 / -679