@odion-cloud/capacitor-volume-control
Version:
Capacitor plugin for advanced volume control with native Android and iOS implementations
608 lines (477 loc) • 16.8 kB
Markdown
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.
- 🔊 **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
---
**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' }`.
---
```bash
npm install @odion-cloud/capacitor-volume-control
npx cap sync
```
1. Install dependencies:
```bash
npm install
```
2. Add the plugin:
```bash
npm install @odion-cloud/capacitor-volume-control
```
3. Sync with native platforms:
```bash
npx cap sync
```
```typescript
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 });
```
```typescript
// 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();
```
```typescript
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();
```
```typescript
// 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
});
```
```typescript
// 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
});
```
```typescript
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');
}
}
```
```typescript
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
};
}
```
```typescript
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
};
}
```
```typescript
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;
}
}
}
```
Run the example:
```bash
npm start
```
Build for production:
```bash
npm run build
```
Test on device:
```bash
npx cap run android
npx cap run ios
```
| 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 | 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 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 |
Get the current volume level for a specific audio stream.
```typescript
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.
```typescript
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.
```typescript
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' }
```
Stop watching for volume changes.
```typescript
clearWatch(): Promise<void>
```
#### `isWatching()`
Check if volume watching is currently active.
```typescript
isWatching(): Promise<{ value: boolean }>
```
| 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:
- **[GitHub Sponsors](https://github.com/sponsors/odion-cloud)** - Most transparent and developer-friendly way to support
### ₿ 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](https://github.com/odion-cloud/capacitor-volume-control) 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](https://github.com/odion-cloud/capacitor-volume-control/blob/main/CONTRIBUTING.md) for details.
## License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/odion-cloud/capacitor-volume-control/blob/main/LICENSE) file for details.
## Changelog
See [CHANGELOG.md](https://github.com/odion-cloud/capacitor-volume-control/blob/main/CHANGELOG.md) for a list of changes and version history.