๐Ÿ”Š Capacitor Volume Control Plugin

Advanced volume control with native Android and iOS implementations

โญ GitHub ๐Ÿ“ฆ npm
Get Started Now โ†’

Device Support & Platform Compatibility

๐Ÿ“ฑ Supported Devices

๐Ÿค–

Android Devices

Full Support

  • Android 6.0+ (API 23+)
  • All phone manufacturers
  • Tablets and foldables
  • Internal & SD card storage
  • All media types supported
๐ŸŽ

iOS Devices

Coming Soon

  • iPhone iOS 12+
  • iPad iPadOS 13+
  • Photos & Videos access
  • Music library integration
  • Expected Q2 2025
๐ŸŒ

Web Platform

Development Only

  • Chrome, Firefox, Safari
  • Mock data for testing
  • No real file access
  • Development fallback
  • Not for production
๐Ÿ“ฑ iOS Support Coming Soon! We're actively working on iOS support with full Photos framework integration.

๐Ÿ† Android Version Compatibility

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

๐Ÿ’ Support This Project

Help me improve this plugin and build better tools for the community!

๐Ÿค

GitHub Sponsors

Support through GitHub's official sponsorship program:

๐Ÿ’ก Recommended: GitHub Sponsors offers the most transparent and developer-friendly way to support open source projects.
โ‚ฟ

Cryptocurrency Support

Support via crypto donations across multiple networks:

  • Bitcoin (BTC):
    bc1q2k0ftm2fgst22kzj683e8gpau3spfa23ttkg26
  • USDT (Ethereum):
    0xd6f4d8733c8C23e7bEC8Aeba37F4b3D2e93172d1
  • USDT (BNB Chain):
    0xd6f4d8733c8C23e7bEC8Aeba37F4b3D2e93172d1
  • USDT (TRON/TRC20):
    TXVy781mQ2tCuQ1BrattXWueUHp1wB5fwt
  • USDT (Solana):
    GZ8jmSUUzc4dQF7Cthj2atomvpBZWqccR81N9DL4o1Be
  • USDT (TON):
    UQAthXSNIlauj3SrzpDAU4VYxgEVV3niOSmeTPCtMBKGfEAE
๐Ÿ’ป Why Support? Your contributions help me:
  • Upgrade to better development hardware
  • Improve my workspace and productivity
  • Dedicate more time to open source projects
  • Add iOS support and new features faster
  • Provide better documentation and examples

๐Ÿค Other Ways to Help

โญ Star the Project

Give us a star on GitHub to show your support!

๐Ÿ› Report Issues

Help improve the plugin by reporting bugs and suggesting features.

๐Ÿ“– Improve Docs

Contribute to documentation, examples, and tutorials.

๐Ÿ’ฌ Spread the Word

Share the plugin with other developers who might find it useful.

What This Plugin Does

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.

๐Ÿ“š Quick Links:
โญ View on GitHub ๐Ÿ“ฆ npm Package โ€ข npm install @odion-cloud/capacitor-volume-control
๐Ÿ”Š

Volume Level Control

Get and set volume levels for different audio streams with precise 0.0-1.0 range control.

๐Ÿ‘‚

Volume Change Monitoring

Watch for volume changes in real-time with hardware button detection and callbacks.

๐Ÿ“ฑ

Platform-Specific Features

Android: Suppress volume indicator. iOS: Disable system volume handler.

โšก

Fast & Reliable

Uses native Android AudioManager and iOS AVAudioSession for optimal performance.

Works with all platforms! Full support for Android 6.0+ and iOS 13.0+ with native implementations.
๐Ÿ”Š Real-time Volume Control! Monitor volume changes and control audio levels with hardware button support.

Quick Example

// 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);
});

Getting Started

1Install the Plugin

npm install @odion-cloud/capacitor-volume-control
npx cap sync

2Android Setup (Optional)

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

3iOS Setup (Optional)

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>
That's it! The plugin works out of the box. Permissions are optional and only needed for advanced features.

How to Use

1. Import the Plugin

import { VolumeControl } from '@odion-cloud/capacitor-volume-control';

2. Get Current Volume

// 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'
});

3. Set Volume Level

// Set volume to 50%
await VolumeControl.setVolumeLevel({ value: 0.5 });

// Set specific volume type
await VolumeControl.setVolumeLevel({
  value: 0.8,
  type: 'system'
});

4. Watch Volume Changes

// 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);
Remember: Volume values must be between 0.0 and 1.0. The plugin will throw an error for invalid values.

Real-World Examples

๐ŸŽต Music Player App

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;
  }
}
Output:
Volume up: 0.75
Volume down: 0.6
Current volume: 0.6

๐Ÿ”Š Audio Mixer App

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
    };
  }
}
Output:
Music volume: 0.5
Notification volume: 0.3
System volume: 0.7

๐ŸŽฎ Game Volume Control

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 });
  }
}
Output:
Game muted: Volume set to 0
Game unmuted: Volume restored to 0.5
Volume change detected: 0.6

๐Ÿ“ฑ React Hook Example

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
  };
}
Output:
Initial volume: 0.5
Volume watching started
Volume changed: 0.7
Volume set to: 0.8

๐ŸŽฏ Vue Composition API Example

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
  };
}
Output:
Volume initialized: 0.5
Volume watching started
Volume changed: up 0.6
Volume set to: 0.8

๐Ÿ”ง Angular Service Example

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;
    }
  }
}
Output:
Service initialized: volume 0.5
Volume subscription active
Volume changed: down 0.4
Volume set via service: 0.7

Complete API Reference

getVolumeLevel(options?)

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)
setVolumeLevel(options)

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)
watchVolume(options, callback)

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' }
addListener(eventName, callback)

Add listener for volume change events (recommended for frameworks).

addListener(eventName: 'volumeChanged', callback: VolumeChangeCallback): Promise<PluginListenerHandle>

// Returns: PluginListenerHandle with remove() method
removeAllListeners(eventName?)

Remove all listeners for an event.

removeAllListeners(eventName?: string): Promise<void>
clearWatch()

Stop watching for volume changes.

clearWatch(): Promise<void>
isWatching()

Check if volume watching is currently active.

isWatching(): Promise<WatchStatusResult>

// Returns: { value: boolean }

Volume Types

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

Configuration Options

Android Options

  • suppressVolumeIndicator - Hide system volume UI
  • type - Control specific volume streams
  • Hardware button detection
  • Real-time volume monitoring

iOS Options

  • disableSystemVolumeHandler - Disable system UI
  • type - Control voice call volume
  • Volume button interception
  • Audio session management

Cross-Platform

  • value - Volume level (0.0-1.0)
  • type - Volume type selection
  • Error handling
  • TypeScript support

Event Handling

  • direction - 'up' or 'down'
  • level - New volume level
  • Real-time callbacks
  • Multiple listeners support

Error Handling

Common Errors:
  • Volume value must be between 0.0 and 1.0 - Invalid volume level
  • Volume watching is already active - Multiple watch calls
  • Volume slider not available - iOS setup issue
  • Failed to get volume level - Permission or system error
  • Volume observer registration failed - Android system issue
  • Audio session setup failed - iOS audio session issue

Best Practices

๐Ÿงน Clean Up Listeners

Always remove event listeners when components unmount to prevent memory leaks.

// In React useEffect cleanup
return () => {
  VolumeControl.clearWatch();
  VolumeControl.removeAllListeners();
};

๐ŸŽฏ Use Event Listeners

Prefer addListener() over callback in watchVolume() for better framework integration.

// Recommended for frameworks
await VolumeControl.addListener('volumeChanged', callback);
await VolumeControl.watchVolume({}, () => {});

๐Ÿ›ก๏ธ Handle Errors Gracefully

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');
  }
}

๐Ÿ” Check Watch Status

Use isWatching() to avoid duplicate watch calls and manage state properly.

const status = await VolumeControl.isWatching();
if (!status.value) {
  await VolumeControl.watchVolume({}, callback);
}

๐Ÿ“ฑ Test on Real Devices

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

โšก Performance Tips

Avoid frequent volume changes and batch operations when possible.

// Debounce volume changes
const debouncedSetVolume = debounce(
  (value) => VolumeControl.setVolumeLevel({ value }), 
  100
);