A simple Android audio recording app built with Jetpack Compose that supports pause/resume functionality, call detection, and visual playback feedback.
- True Pause/Resume: Puses and resume recording to the same file (no segments)
- Lock Screen Recording Status: Live recording timer and controls visible on lock screen
- Interactive Notification Controls: Pause/resume/stop recording directly from notification bar
- Call Detection: Automatic pause notifications during phone calls
- Silent Audio Detection: Warns when no audio input is detected for 10+ seconds
- Audio Device Switching: Automatic switching between Bluetooth, wired, and device microphones
- Storage Management: Real-time storage monitoring with automatic recording stop when storage is low
- Visual Feedback: Highlights currently playing recordings
- Single File Output: All pauses/resumes save to one continuous file
- Permission Management: Handles microphone, phone state, Bluetooth, and notification permissions
Massive Development Investment (8+ hours of intensive work):
- Android Process Death Analysis: Studied Android app lifecycle, low memory killer, and process death scenarios
- Recovery Pattern Research: Investigated Service persistence, chunk-based recording, and state restoration approaches
- Architecture Planning: Designed comprehensive chunk-based system with 30-second segments for recovery resilience
- Technology Stack Selection: Chose Room database + WorkManager + custom persistence layer
Files Created (All Now Deleted):
database/
βββ RecordingDatabase.kt - Room database with migration support
βββ entity/
β βββ RecordingEntities.kt - 3 comprehensive data models
βββ dao/
βββ RecordingDao.kt - 3 DAO interfaces with complex queries
What We Built:
- RecordingSessionEntity: Complete session state (status, timing, pause reasons, audio source)
- AudioChunkEntity: Individual 30-second chunk tracking (file paths, completion status, merge flags)
- RecoveryTaskEntity: Background task management (finalization, merging, cleanup with retry logic)
- Complex Relationships: Foreign keys, indices, and cascade deletion rules
- Migration Framework: Version control and schema evolution planning
SessionPersistenceManager.kt (864 lines of code - deleted):
- Session Lifecycle: Create, update, pause, resume, complete session management
- Chunk Tracking: Add chunks, finalize incomplete chunks, validate file integrity
- Recovery Tasks: Create and manage background recovery operations with retry mechanisms
- State Restoration: Query and restore active sessions after process death
- Cleanup Logic: Automated cleanup of old sessions and completed tasks
- Error Handling: Comprehensive exception handling and logging throughout
RecordingRecoveryWorker.kt (472 lines of code - deleted):
- WorkManager Integration: OneTimeWorkRequest with constraints and backoff policies
- Task Processing: Handle chunk finalization, merging, and cleanup operations
- Chunk Finalization: Recover incomplete chunks after process interruption
- Audio Merging: Merge multiple 30-second chunks into single final recording
- File Management: Handle file validation, size calculation, and cleanup
- Error Recovery: Retry logic with exponential backoff for failed operations
RecorderApplication.kt & Dependencies:
- Application Class: WorkManager configuration and initialization
- Build Configuration: Room, WorkManager, kapt dependencies and annotation processing
- Manifest Updates: Application name, WorkManager permissions, service declarations
- Initialization Logic: Database setup, pending task recovery, and cleanup scheduling
Problem: MediaRecorder cannot be restored after process death
- MediaRecorder maintains internal native state that's lost on process termination
- No Android API exists to serialize/deserialize MediaRecorder state
- Starting new MediaRecorder creates entirely new recording session
- Impact: Recovery system became theoretically impossible with current Android APIs
Problem: Audio chunk merging proved more complex than anticipated
- M4A container format has intricate header structures and metadata
- Simple file concatenation produces unplayable files
- MediaMuxer requires complex track extraction and format conversion
- FFmpeg integration would add massive dependency overhead
- Impact: Even if chunks could be recovered, merging them reliably failed
Problem: Incomplete chunks after process death often corrupted
- MediaRecorder doesn't flush data immediately during recording
- Process termination can leave partially written, unplayable files
- File headers may be incomplete or missing entirely
- Impact: Recovery system would often recover corrupted/unusable audio
Problem: Process death scenarios impossible to reliably test
- Android's process killer behavior varies by device and Android version
- Forced process termination differs from natural low-memory kills
- Timing of process death affects file corruption patterns unpredictably
- Impact: Cannot guarantee recovery system works in real scenarios
After 8+ hours of intensive development, we faced reality:
- MediaRecorder Limitation: Core Android API cannot be recovered after process death
- File Format Complexity: Audio merging requires expertise beyond project scope
- Reliability Concerns: System may fail when most needed (during actual process death)
- Maintenance Overhead: Complex codebase for edge case scenarios
- User Experience: Simple app restart often better than complex recovery attempts
Removed in cleanup (1 hour of deletion):
# Deleted entire directories
Remove-Item database/ -Recurse -Force # 3 files, 400+ lines
Remove-Item session/ -Recurse -Force # 1 file, 864 lines
Remove-Item worker/ -Recurse -Force # 1 file, 472 lines
Remove-Item RecorderApplication.kt -Force # 1 file, 150+ lines
# Total: 6 files, ~1900+ lines of carefully crafted codeBuild Configuration Cleanup:
- Removed Room dependencies (
room-runtime,room-ktx,room-compiler) - Removed WorkManager dependency (
work-runtime-ktx) - Removed kapt plugin and annotation processing setup
- Cleaned duplicate dependencies (was listing same libraries twice)
- Removed RecorderApplication reference from AndroidManifest.xml
What We Learned About Android Development:
- MediaRecorder Limitations: Some Android APIs have fundamental constraints that cannot be overcome
- Audio Format Complexity: Media container formats require specialized knowledge and tools
- Process Death Rarity: With proper foreground service, process death during recording is extremely rare
- Simplicity Value: Users prefer apps that restart cleanly over complex recovery mechanisms
- Development Economics: 8+ hours of complex implementation vs. 30 seconds of app restart
What We Learned About Software Architecture:
- Research First: Should have investigated MediaRecorder limitations before building entire system
- Prototype Core Dependencies: Test critical technical assumptions early in development
- Incremental Validation: Build and test smallest viable pieces before full implementation
- Know When to Quit: Sometimes the best code is the code you delete
Final Status: Complete failure, but valuable learning experience that led to cleaner, more maintainable architecture focused on core functionality. The foreground service approach provides 99.9% of the benefits with 1% of the complexity.
Main activity containing the Compose UI for recording and playback controls.
onCreate()- Requests permissions and sets up UIAudioRecorderApp()- Main UI composable with recording controls and listRecordingCard()- Individual recording item with play/pause functionalityformatDuration()- Converts seconds to human-readable duration format
Core business logic for audio recording with true pause/resume and call detection.
toggleRecord()- Starts/stops recording based on current statetogglePause()- Pauses/resumes active recording in same fileplay()- Plays selected recording with visual highlightingstopPlayback()- Stops audio playback and clears playing statestartRecording()- Begins new recording session with single filepauseRecording()- Pauses recording using MediaRecorder.pause() (no file save)resumeRecording()- Resumes recording to same file using MediaRecorder.resume()stopRecording()- Ends recording and saves single complete filesetupPhoneStateListener()- Configures call detection for notificationshandleCallStateChange()- Shows/hides notifications during phone calls
MediaRecorder wrapper for audio recording functionality with pause/resume support and silence detection.
start()- Begins recording to specified file with silence monitoringstop()- Stops recording and releases resourcespause()- Pauses recording (Android N+) without stopping fileresume()- Resumes recording (Android N+) to same fileswitchAudioSource()- Changes audio input source during recordingsetSilenceDetectionCallback()- Sets callback for silence detection events
MediaPlayer wrapper for audio playback functionality.
playFile()- Plays audio file with automatic resource managementstop()- Stops playback and releases resourcessetOnCompletionListener()- Sets callback for when playback finishes
Foreground service that displays live recording status on lock screen with interactive controls.
startForeground()- Creates persistent notification with timer and pause/stop buttonsupdateNotification()- Updates notification with current recording status and elapsed timeonStartCommand()- Handles notification button presses (pause/resume/stop actions)syncWithViewModelState()- Synchronizes service status with ViewModel recording statecreateNotification()- Builds stopwatch-style notification for lock screen visibility- Uses
CATEGORY_STOPWATCHandIMPORTANCE_HIGHfor optimal lock screen display
Manages notifications for recording events including call detection, audio source changes, storage warnings, and silence detection.
showRecordingPausedNotification()- Displays call interruption notificationshowAudioSourceChangeNotification()- Shows when audio input device changesshowLowStorageNotification()- Warns about insufficient storage during recordingshowSilenceWarningNotification()- Alerts when no audio is detected for 10+ secondshideRecordingPausedNotification()- Removes pause notificationcreateNotificationChannel()- Sets up notification channels for Android 8+
Monitors audio levels during recording to detect periods of silence.
startMonitoring()- Begins audio level monitoring using AudioRecordstopMonitoring()- Stops silence detection and releases resourcescalculateRMS()- Calculates audio level from raw audio samplesgetCurrentSilenceDuration()- Returns current consecutive silence durationresetSilenceCounter()- Manually resets silence detection counter
Manages audio input device detection and automatic switching between microphone sources.
startMonitoring()- Monitors for audio device connection/disconnection eventsgetCurrentDevice()- Returns currently active audio input deviceforceDeviceDetection()- Manually triggers device detection scan- Audio device types: Bluetooth headset, wired headset, USB headset, device microphone
Monitors device storage and manages recording based on available space.
hasEnoughStorageToStart()- Checks if sufficient storage exists before recordingstartStorageMonitoring()- Monitors storage levels during recordinggetStorageInfo()- Returns detailed storage statisticsgetEstimatedRecordingTimeMinutes()- Calculates estimated recording time based on available storage- Storage thresholds: 50MB minimum to start, 100MB warning level, 25MB critical stop level
Utility class for managing notification permissions and guiding users for optimal lock screen experience.
areNotificationsEnabled()- Checks if app notifications are enabledcanShowOnLockScreen()- Determines if notifications can appear on lock screengetNotificationSettingsIntent()- Creates intent to open notification settings for the appgetLockScreenSettingsIntent()- Creates intent to open device lock screen settingsgetPermissionMessage()- Provides user-friendly permission status messages
- Start Recording - Creates a new M4A file and begins audio capture
- Pause (Optional) - Uses MediaRecorder.pause() to temporarily stop recording
- Resume (Optional) - Uses MediaRecorder.resume() to continue to same file
- Stop Recording - Finalizes and saves the complete audio file
- App detects incoming/active phone calls using TelephonyManager
- Shows notification when recording is paused due to call
- User manually controls pause/resume using app buttons
- Notification automatically disappears when call ends
- Continuously monitors audio input levels during recording using AudioRecord
- Detects when audio falls below silence threshold (500 amplitude) for 10+ seconds
- Shows "No audio detected - Check microphone" notification warning
- Helps identify microphone issues, muted devices, or positioning problems
- Automatically resets when audio input is detected again
- Automatically detects Bluetooth headset connections/disconnections
- Supports wired headsets, USB headsets, and device microphone
- Shows notifications when audio source changes during recording
- Attempts to switch MediaRecorder audio source when device changes
- Optimizes audio source selection based on connected device type
- Checks available storage before starting recording (requires 50MB minimum)
- Monitors storage levels continuously during recording
- Shows warning notification when storage drops below 100MB
- Automatically stops recording when storage reaches critical level (25MB)
- Estimates remaining recording time based on available storage and audio bitrate
- Stopped: No active recording session
- Recording: Actively capturing audio to file
- Paused: Recording temporarily stopped (file remains open)
Recordings are saved to app external files directory as:
meeting_YYYYMMDD_HHMMSS.m4a- Complete recording file (including paused periods)
- Android N (API 24+) - For true pause/resume functionality
- Android O (API 26+) - Recommended for optimal notification channels and lock screen display
- Permissions:
RECORD_AUDIO- Required for audio recording and silence detectionFOREGROUND_SERVICE- Required for persistent recording notificationsFOREGROUND_SERVICE_MICROPHONE- Required for microphone access in foreground service (Android 14+)POST_NOTIFICATIONS- Required for lock screen notifications (Android 13+)READ_PHONE_STATE- Optional for call detection notificationsBLUETOOTH_CONNECT- Optional for Bluetooth headset detection (Android 12+)MODIFY_AUDIO_SETTINGS- Optional for audio device management
- Grant Permissions - Allow microphone, phone, Bluetooth, and notification access when prompted
- Enable Lock Screen Notifications - For full lock screen functionality, ensure notifications are enabled in Settings
- Check Storage - Ensure device has at least 50MB free storage for recording
- Start Recording - Tap the record button to begin (live status appears on lock screen)
- Lock Screen Control - When recording, you can:
- View live timer updates on lock screen
- Pause/resume recording using notification buttons
- Stop recording directly from notification
- Monitor Notifications - App will notify you of:
- Phone call interruptions (auto-pause)
- Audio device changes (Bluetooth/wired headset connect/disconnect)
- Storage warnings (when space runs low)
- Silent audio detection (no input for 10+ seconds)
- In-App Control - Use main app for full control and monitoring
- Playback - Tap any saved recording to play with visual highlighting
- Live Timer Display: Shows real-time recording duration on lock screen (updates every second)
- Interactive Controls: Pause/Resume and Stop buttons work directly from notification
- Status Indicators: Clear display of recording state ("Recording", "Paused", "Paused - Call")
- Stopwatch-Style Design: Uses Android's stopwatch notification pattern for consistency
- High Priority Display: Notification configured for optimal lock screen visibility
- Call Detection: Shows when recording is paused due to phone calls
- Audio Source Changes: Alerts when switching between microphones/headsets
- Storage Warnings: Warns when storage is running low during recording
- Silent Audio Detection: "No audio detected - Check microphone" after 10s silence
- Permission Guidance: Prompts user to enable notifications for lock screen display
- Smart Intent Handling: Notifications bring existing app to foreground (no duplicate instances)