Advanced volume control with native Android and iOS implementations
Get Started Now โFull Support
Coming Soon
Development Only
Android Version | API Level | Support Level | Features |
---|---|---|---|
Android 14+ | API 34+ | โ Full | All features, visual media permissions |
Android 13 | API 33 | โ Full | Granular media permissions |
Android 10-12 | API 29-32 | โ Full | Scoped storage, external volumes |
Android 6-9 | API 23-28 | โ Full | Runtime permissions, SD card access |
Android 5 | API 21-22 | โ ๏ธ Basic | Limited external storage access |
Help me improve this plugin and build better tools for the community!
Support through GitHub's official sponsorship program:
Support via crypto donations across multiple networks:
bc1q2k0ftm2fgst22kzj683e8gpau3spfa23ttkg26
0xd6f4d8733c8C23e7bEC8Aeba37F4b3D2e93172d1
0xd6f4d8733c8C23e7bEC8Aeba37F4b3D2e93172d1
TXVy781mQ2tCuQ1BrattXWueUHp1wB5fwt
GZ8jmSUUzc4dQF7Cthj2atomvpBZWqccR81N9DL4o1Be
UQAthXSNIlauj3SrzpDAU4VYxgEVV3niOSmeTPCtMBKGfEAE
Give us a star on GitHub to show your support!
Help improve the plugin by reporting bugs and suggesting features.
Contribute to documentation, examples, and tutorials.
Share the plugin with other developers who might find it useful.
Advanced volume control for Capacitor apps with native Android and iOS implementations. Perfect for building music players, audio apps, and any application that needs precise volume management.
Get and set volume levels for different audio streams with precise 0.0-1.0 range control.
Watch for volume changes in real-time with hardware button detection and callbacks.
Android: Suppress volume indicator. iOS: Disable system volume handler.
Uses native Android AudioManager and iOS AVAudioSession for optimal performance.
// Get current volume and set it to 50% const currentVolume = await VolumeControl.getVolumeLevel(); console.log('Current volume:', currentVolume.value); await VolumeControl.setVolumeLevel({ value: 0.5 }); // Watch for volume changes await VolumeControl.watchVolume({ suppressVolumeIndicator: true }, (event) => { console.log('Volume changed:', event.direction); });
npm install @odion-cloud/capacitor-volume-control npx cap sync
For advanced volume control features, add to android/app/src/main/AndroidManifest.xml:
<!-- For volume control features --> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
For iOS volume control features, add to ios/App/App/Info.plist:
<key>NSMicrophoneUsageDescription</key> <string>This app needs access to control audio volume</string>
import { VolumeControl } from '@odion-cloud/capacitor-volume-control';
// Get current volume level (0.0 to 1.0) const currentVolume = await VolumeControl.getVolumeLevel(); console.log('Current volume:', currentVolume.value); // Get specific volume type const musicVolume = await VolumeControl.getVolumeLevel({ type: 'music' });
// Set volume to 50% await VolumeControl.setVolumeLevel({ value: 0.5 }); // Set specific volume type await VolumeControl.setVolumeLevel({ value: 0.8, type: 'system' });
// Method 1: Using watchVolume with callback await VolumeControl.watchVolume({ suppressVolumeIndicator: true, // Android: hide volume UI disableSystemVolumeHandler: true // iOS: disable system UI }, (event) => { console.log('Volume changed:', event.direction); }); // Method 2: Using event listeners (recommended for frameworks) const listener = await VolumeControl.addListener('volumeChanged', (event) => { console.log('Volume changed:', event.direction); }); // Start watching (required for both methods) await VolumeControl.watchVolume({ disableSystemVolumeHandler: true, suppressVolumeIndicator: true }, () => {}); // Empty callback if using event listeners // Stop watching await VolumeControl.clearWatch(); // Remove specific listener listener.remove(); // Or remove all listeners await VolumeControl.removeAllListeners(); // Check if watching const isWatching = await VolumeControl.isWatching(); console.log('Is watching:', isWatching.value);
class MusicPlayer { constructor() { this.isWatching = false; } async initialize() { // Get current volume const volume = await VolumeControl.getVolumeLevel(); this.currentVolume = volume.value; // Start watching volume changes await this.startVolumeWatching(); } async startVolumeWatching() { if (this.isWatching) return; await VolumeControl.watchVolume({ suppressVolumeIndicator: true }, (event) => { this.handleVolumeChange(event); }); this.isWatching = true; } handleVolumeChange(event) { this.currentVolume = event.level; console.log(`Volume ${event.direction}: ${event.level}`); // Update UI or trigger other actions this.updateVolumeDisplay(event.level); } async setVolume(level) { await VolumeControl.setVolumeLevel({ value: level }); this.currentVolume = level; } }
class AudioMixer { constructor() { this.volumeLevels = { music: 0.5, notification: 0.3, system: 0.7 }; } async setMusicVolume(level) { await VolumeControl.setVolumeLevel({ value: level, type: 'music' }); this.volumeLevels.music = level; } async setNotificationVolume(level) { await VolumeControl.setVolumeLevel({ value: level, type: 'notification' }); this.volumeLevels.notification = level; } async getCurrentLevels() { const music = await VolumeControl.getVolumeLevel({ type: 'music' }); const notification = await VolumeControl.getVolumeLevel({ type: 'notification' }); const system = await VolumeControl.getVolumeLevel({ type: 'system' }); return { music: music.value, notification: notification.value, system: system.value }; } }
class GameVolumeManager { constructor() { this.isMuted = false; this.previousVolume = 0.5; } async initialize() { // Get current volume const volume = await VolumeControl.getVolumeLevel(); this.previousVolume = volume.value; // Start watching for volume changes await VolumeControl.watchVolume({ disableSystemVolumeHandler: true }, (event) => { this.handleGameVolumeChange(event); }); } handleGameVolumeChange(event) { if (this.isMuted) { // If muted, restore previous volume VolumeControl.setVolumeLevel({ value: this.previousVolume }); } else { // Update previous volume this.previousVolume = event.level; } } async mute() { this.isMuted = true; await VolumeControl.setVolumeLevel({ value: 0 }); } async unmute() { this.isMuted = false; await VolumeControl.setVolumeLevel({ value: this.previousVolume }); } }
import { useEffect, useState } from 'react'; import { VolumeControl } from '@odion-cloud/capacitor-volume-control'; export function useVolumeControl() { const [volume, setVolume] = useState(0.5); const [isWatching, setIsWatching] = useState(false); useEffect(() => { // Get initial volume VolumeControl.getVolumeLevel().then(result => { setVolume(result.value); }); // Cleanup on unmount return () => { VolumeControl.clearWatch(); VolumeControl.removeAllListeners(); }; }, []); const startWatching = async () => { if (isWatching) return; try { // Add event listener first await VolumeControl.addListener('volumeChanged', (event) => { setVolume(event.level); }); // Start watching with empty callback since we're using event listeners await VolumeControl.watchVolume({ disableSystemVolumeHandler: true, suppressVolumeIndicator: true }, () => {}); setIsWatching(true); } catch (error) { console.error('Failed to start watching:', error); } }; const stopWatching = async () => { try { await VolumeControl.clearWatch(); await VolumeControl.removeAllListeners(); setIsWatching(false); } catch (error) { console.error('Failed to stop watching:', error); } }; const setVolumeLevel = async (value) => { try { await VolumeControl.setVolumeLevel({ value }); setVolume(value); } catch (error) { console.error('Failed to set volume:', error); } }; return { volume, isWatching, startWatching, stopWatching, setVolumeLevel }; }
import { ref, onMounted, onUnmounted } from 'vue'; import { VolumeControl } from '@odion-cloud/capacitor-volume-control'; export function useVolumeControl() { const volume = ref(0.5); const isWatching = ref(false); let volumeListener = null; onMounted(async () => { // Get initial volume try { const result = await VolumeControl.getVolumeLevel(); volume.value = result.value; } catch (error) { console.error('Failed to get initial volume:', error); } }); onUnmounted(async () => { await stopWatching(); }); const startWatching = async () => { if (isWatching.value) return; try { // Add event listener volumeListener = await VolumeControl.addListener('volumeChanged', (event) => { volume.value = event.level; console.log('Volume changed:', event.direction); }); // Start watching await VolumeControl.watchVolume({ disableSystemVolumeHandler: true, suppressVolumeIndicator: true }, () => {}); isWatching.value = true; } catch (error) { console.error('Failed to start watching:', error); } }; const stopWatching = async () => { try { await VolumeControl.clearWatch(); if (volumeListener) { volumeListener.remove(); volumeListener = null; } await VolumeControl.removeAllListeners(); isWatching.value = false; } catch (error) { console.error('Failed to stop watching:', error); } }; const setVolumeLevel = async (value) => { try { await VolumeControl.setVolumeLevel({ value }); volume.value = value; } catch (error) { console.error('Failed to set volume:', error); } }; return { volume, isWatching, startWatching, stopWatching, setVolumeLevel }; }
import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs'; import { VolumeControl } from '@odion-cloud/capacitor-volume-control'; @Injectable({ providedIn: 'root' }) export class VolumeService { private volumeSubject = new BehaviorSubject<number>(0.5); private isWatchingSubject = new BehaviorSubject<boolean>(false); private volumeListener = null; public volume$ = this.volumeSubject.asObservable(); public isWatching$ = this.isWatchingSubject.asObservable(); constructor() { this.initializeVolume(); } private async initializeVolume() { try { const result = await VolumeControl.getVolumeLevel(); this.volumeSubject.next(result.value); } catch (error) { console.error('Failed to get initial volume:', error); } } async startWatching(): Promise<void> { if (this.isWatchingSubject.value) return; try { // Add event listener this.volumeListener = await VolumeControl.addListener('volumeChanged', (event) => { this.volumeSubject.next(event.level); console.log('Volume changed:', event.direction); }); // Start watching await VolumeControl.watchVolume({ disableSystemVolumeHandler: true, suppressVolumeIndicator: true }, () => {}); this.isWatchingSubject.next(true); } catch (error) { console.error('Failed to start watching:', error); throw error; } } async stopWatching(): Promise<void> { try { await VolumeControl.clearWatch(); if (this.volumeListener) { this.volumeListener.remove(); this.volumeListener = null; } await VolumeControl.removeAllListeners(); this.isWatchingSubject.next(false); } catch (error) { console.error('Failed to stop watching:', error); throw error; } } async setVolumeLevel(value: number): Promise<void> { try { await VolumeControl.setVolumeLevel({ value }); this.volumeSubject.next(value); } catch (error) { console.error('Failed to set volume:', error); throw error; } } }
Get the current volume level for a specific audio stream.
getVolumeLevel({ type?: VolumeType; // Volume type to get (default: 'music') }): Promise<VolumeResult> // Returns: { value: number } (0.0 to 1.0)
Set the volume level for a specific audio stream.
setVolumeLevel({ value: number; // Volume level (0.0 to 1.0) type?: VolumeType; // Volume type to set (default: 'music') }): Promise<VolumeResult> // Returns: { value: number } (the new volume level)
Start watching for volume changes with hardware button detection.
watchVolume({ disableSystemVolumeHandler?: boolean; // iOS: disable system UI suppressVolumeIndicator?: boolean; // Android: hide volume UI }, callback: VolumeChangeCallback): Promise<void> // Callback receives: { direction: 'up' | 'down' }
Add listener for volume change events (recommended for frameworks).
addListener(eventName: 'volumeChanged', callback: VolumeChangeCallback): Promise<PluginListenerHandle> // Returns: PluginListenerHandle with remove() method
Remove all listeners for an event.
removeAllListeners(eventName?: string): Promise<void>
Stop watching for volume changes.
clearWatch(): Promise<void>
Check if volume watching is currently active.
isWatching(): Promise<WatchStatusResult> // Returns: { value: boolean }
Supported volume types for Android and iOS:
Volume Type | Description | Platform |
---|---|---|
VolumeType.MUSIC |
Music, videos, games, and other media | Android, iOS |
VolumeType.SYSTEM |
System sounds and notifications | Android |
VolumeType.RING |
Phone ringtone volume | Android |
VolumeType.NOTIFICATION |
Notification sounds | Android |
VolumeType.ALARM |
Alarm clock volume | Android |
VolumeType.VOICE_CALL |
Voice call volume | Android, iOS |
VolumeType.DTMF |
DTMF tones | Android |
suppressVolumeIndicator
- Hide system volume UItype
- Control specific volume streamsdisableSystemVolumeHandler
- Disable system UItype
- Control voice call volumevalue
- Volume level (0.0-1.0)type
- Volume type selectiondirection
- 'up' or 'down'level
- New volume levelVolume value must be between 0.0 and 1.0
- Invalid volume levelVolume watching is already active
- Multiple watch callsVolume slider not available
- iOS setup issueFailed to get volume level
- Permission or system errorVolume observer registration failed
- Android system issueAudio session setup failed
- iOS audio session issueAlways remove event listeners when components unmount to prevent memory leaks.
// In React useEffect cleanup return () => { VolumeControl.clearWatch(); VolumeControl.removeAllListeners(); };
Prefer addListener()
over callback in watchVolume()
for better framework integration.
// Recommended for frameworks await VolumeControl.addListener('volumeChanged', callback); await VolumeControl.watchVolume({}, () => {});
Wrap volume operations in try-catch blocks and handle common error scenarios.
try { await VolumeControl.setVolumeLevel({ value: 0.8 }); } catch (error) { if (error.message.includes('between 0.0 and 1.0')) { console.error('Invalid volume value'); } }
Use isWatching()
to avoid duplicate watch calls and manage state properly.
const status = await VolumeControl.isWatching(); if (!status.value) { await VolumeControl.watchVolume({}, callback); }
Volume watching and hardware button detection require physical devices for proper testing.
// Run on device for testing npx cap run android npx cap run ios
Avoid frequent volume changes and batch operations when possible.
// Debounce volume changes const debouncedSetVolume = debounce( (value) => VolumeControl.setVolumeLevel({ value }), 100 );