npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

capacitor-volume-controller

v1.0.13

Published

Capacitor plugin for advanced volume control with native Android and iOS implementations

Readme

Capacitor Volume Control Plugin

A Capacitor plugin for advanced volume control with native Android and iOS implementations. This plugin provides comprehensive volume management capabilities including volume level control, volume change monitoring, and platform-specific features.

Features

  • 🔊 Volume Level Control: Get and set volume levels for different audio streams
  • 👂 Volume Change Monitoring: Watch for volume changes in real-time
  • 📱 Platform-Specific Features:
    • Android: Suppress volume indicator, control different volume types
    • iOS: Disable system volume handler, voice call volume control
  • 🎯 Type Safety: Full TypeScript support with comprehensive type definitions
  • 🔧 Easy Integration: Simple API that works seamlessly with Capacitor apps

⚠️ Migration Notice

v2.0+ Breaking Change:

  • The event listener pattern (addListener('volumeChanged', ...)) and event.level are no longer supported.
  • Use only watchVolume(options, callback) for hardware button events. The callback receives { direction: 'up' | 'down' }.

Installation

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

Setup

  1. Install dependencies:
npm install
  1. Add the plugin:
npm install @odion-cloud/capacitor-volume-control
  1. Sync with native platforms:
npx cap sync

Usage Examples

Basic Volume Control

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

// Get current volume
const volume = await VolumeControl.getVolumeLevel();
console.log('Current volume:', volume.value);

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

Volume Watching

// Use watchVolume with callback
await VolumeControl.watchVolume({
  disableSystemVolumeHandler: true, // iOS only
  suppressVolumeIndicator: true,    // Android only
}, (event) => {
  console.log('Volume button pressed:', event.direction);
});

// Stop watching
await VolumeControl.clearWatch();

Advanced Usage

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

class VolumeService {
  private isWatching = false;

  async initializeVolume() {
    try {
      // Get current music volume
      const musicVolume = await VolumeControl.getVolumeLevel({
        type: VolumeType.MUSIC
      });
      
      console.log('Music volume:', musicVolume.value);
      
      // Set system volume
      await VolumeControl.setVolumeLevel({
        value: 0.8,
        type: VolumeType.SYSTEM
      });
      
    } catch (error) {
      console.error('Volume initialization error:', error);
    }
  }

  async startWatching() {
    if (this.isWatching) return;

    try {
      await VolumeControl.watchVolume({
        disableSystemVolumeHandler: true,
        suppressVolumeIndicator: true
      }, this.handleVolumeChange.bind(this));
      
      this.isWatching = true;
      console.log('Started volume watching');
      
    } catch (error) {
      console.error('Volume watching error:', error);
    }
  }

  async stopWatching() {
    try {
      await VolumeControl.clearWatch();
      this.isWatching = false;
      console.log('Stopped volume watching');
      
    } catch (error) {
      console.error('Stop watching error:', error);
    }
  }

  private handleVolumeChange(event: { direction: 'up' | 'down' }) {
    console.log(`Volume ${event.direction}`);
    // Custom volume handling logic
  }

  async getWatchStatus() {
    const status = await VolumeControl.isWatching();
    return status.value;
  }
}

// Usage
const volumeService = new VolumeService();

// Initialize
await volumeService.initializeVolume();

// Start watching
await volumeService.startWatching();

// Check status
const isWatching = await volumeService.getWatchStatus();
console.log('Is watching:', isWatching);

// Stop watching
await volumeService.stopWatching();

Platform-Specific Examples

Android Specific

// Suppress volume indicator on Android
await VolumeControl.watchVolume({
  suppressVolumeIndicator: true
}, (event) => {
  console.log('Volume button pressed:', event.direction);
});

// Control different volume types
await VolumeControl.setVolumeLevel({
  value: 0.7,
  type: VolumeType.NOTIFICATION
});

iOS Specific

// Disable system volume handler on iOS
await VolumeControl.watchVolume({
  disableSystemVolumeHandler: true
}, (event) => {
  console.log('Volume button pressed:', event.direction);
});

// Control voice call volume
await VolumeControl.setVolumeLevel({
  value: 0.9,
  type: VolumeType.VOICE_CALL
});

Error Handling

try {
  await VolumeControl.setVolumeLevel({ value: 1.5 });
} catch (error) {
  if (error.message.includes('between 0.0 and 1.0')) {
    console.error('Invalid volume value');
  } else {
    console.error('Unexpected error:', error);
  }
}

try {
  await VolumeControl.watchVolume({}, callback);
  await VolumeControl.watchVolume({}, callback); // This will fail
} catch (error) {
  if (error.message.includes('already been watched')) {
    console.error('Volume watching is already active');
  }
}

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();
    };
  }, []);

  const startWatching = async () => {
    if (isWatching) return;

    try {
      await VolumeControl.watchVolume({
        disableSystemVolumeHandler: true,
        suppressVolumeIndicator: true
      }, (event) => {
        // You may want to update UI or state here
        console.log('Volume button pressed:', event.direction);
      });
      setIsWatching(true);
    } catch (error) {
      console.error('Failed to start watching:', error);
    }
  };

  const stopWatching = async () => {
    try {
      await VolumeControl.clearWatch();
      setIsWatching(false);
    } catch (error) {
      console.error('Failed to stop watching:', error);
    }
  };

  const setVolumeLevel = async (value: number) => {
    try {
      await VolumeControl.setVolumeLevel({ value });
      setVolume(value);
    } catch (error) {
      console.error('Failed to set volume:', error);
    }
  };

  return {
    volume,
    isWatching,
    startWatching,
    stopWatching,
    setVolumeLevel
  };
}

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

  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 {
      await VolumeControl.watchVolume({
        disableSystemVolumeHandler: true,
        suppressVolumeIndicator: true
      }, (event) => {
        console.log('Volume button pressed:', event.direction);
      });
      isWatching.value = true;
    } catch (error) {
      console.error('Failed to start watching:', error);
    }
  };

  const stopWatching = async () => {
    try {
      await VolumeControl.clearWatch();
      isWatching.value = false;
    } catch (error) {
      console.error('Failed to stop watching:', error);
    }
  };

  const setVolumeLevel = async (value: number) => {
    try {
      await VolumeControl.setVolumeLevel({ value });
      volume.value = value;
    } catch (error) {
      console.error('Failed to set volume:', error);
    }
  };

  return {
    volume,
    isWatching,
    startWatching,
    stopWatching,
    setVolumeLevel
  };
}

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

  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 {
      await VolumeControl.watchVolume({
        disableSystemVolumeHandler: true,
        suppressVolumeIndicator: true
      }, (event) => {
        this.volumeSubject.next(0); // You may want to update with actual logic
        console.log('Volume button pressed:', event.direction);
      });
      this.isWatchingSubject.next(true);
    } catch (error) {
      console.error('Failed to start watching:', error);
      throw error;
    }
  }

  async stopWatching(): Promise<void> {
    try {
      await VolumeControl.clearWatch();
      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;
    }
  }
}

Testing

Run the example:

npm start

Build for production:

npm run build

Test on device:

npx cap run android
npx cap run ios

Platform Support

📱 Supported Devices

| Platform | Support Level | Minimum Version | Features | |----------|---------------|-----------------|----------| | Android | ✅ Full Support | Android 6.0+ (API 23+) | All volume types, hardware buttons, real-time monitoring | | iOS | ✅ Full Support | iOS 13.0+ | Volume control, hardware buttons, audio session management | | Web | ⚠️ Development Only | All modern browsers | Mock implementation for testing |

🏆 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 |

🎯 Volume Types Support

| Volume Type | Android | iOS | Description | |-------------|---------|-----|-------------| | VolumeType.MUSIC | ✅ | ✅ | Music, videos, games, and other media | | VolumeType.SYSTEM | ✅ | ❌ | System sounds and notifications | | VolumeType.RING | ✅ | ❌ | Phone ringtone volume | | VolumeType.NOTIFICATION | ✅ | ❌ | Notification sounds | | VolumeType.ALARM | ✅ | ❌ | Alarm clock volume | | VolumeType.VOICE_CALL | ✅ | ✅ | Voice call volume | | VolumeType.DTMF | ✅ | ❌ | DTMF tones |

API Reference

Methods

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: (event: { direction: 'up' | 'down' }) => void): Promise<string>

// Callback receives: { direction: 'up' | 'down' }

clearWatch()

Stop watching for volume changes.

clearWatch(): Promise<void>

isWatching()

Check if volume watching is currently active.

isWatching(): Promise<{ value: boolean }>

Configuration Options

| Option | Platform | Description | |--------|----------|-------------| | suppressVolumeIndicator | Android | Hide system volume UI when changing volume | | disableSystemVolumeHandler | iOS | Disable system volume UI and intercept hardware buttons | | type | Both | Specify volume type (MUSIC, SYSTEM, RING, etc.) | | value | Both | Volume level between 0.0 and 1.0 |

Error Handling

Common errors and their solutions:

| Error Message | Cause | Solution | |---------------|-------|----------| | Volume value must be between 0.0 and 1.0 | Invalid volume level | Ensure volume is between 0.0 and 1.0 | | Volume buttons has already been watched | Multiple watch calls | Call clearWatch() before starting new watch | | Volume slider not available | iOS setup issue | Check audio session configuration | | Failed to get volume level | Permission or system error | Verify permissions and device compatibility | | Volume observer registration failed | Android system issue | Restart app or check system permissions | | Audio session setup failed | iOS audio session issue | Check audio session category and options |

Best Practices

  1. Always clean up listeners: Remove event listeners when components unmount
  2. Use the callback for volume events: Use the callback in watchVolume() for hardware button events
  3. Handle errors gracefully: Wrap volume operations in try-catch blocks
  4. Check watch status: Use isWatching() to avoid duplicate watch calls
  5. Test on real devices: Volume watching requires physical hardware

Support This Project

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

🤝 GitHub Sponsors

Support through GitHub's official sponsorship program:

₿ Cryptocurrency Support

Support via crypto donations across multiple networks:

| Network | Address | |---------|---------| | 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 workspace and productivity
  • Dedicate more time to open source projects
  • Add 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

Contributing

We welcome contributions! Please see our Contributing Guide for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

See CHANGELOG.md for a list of changes and version history.