UNPKG

@odion-cloud/capacitor-volume-control

Version:

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

721 lines (557 loc) โ€ข 20 kB
# 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. **Published on npm** ยท [@odion-cloud/capacitor-volume-control](https://www.npmjs.com/package/@odion-cloud/capacitor-volume-control) ยท **Supports Capacitor 5, 6, 7, and 8** ## 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 callback parameter in `watchVolume(options, callback)` is no longer supported. - Use the event listener pattern `addListener('volumeButtonPressed', callback)` instead. - The callback receives `{ direction: 'up' | 'down' }`. --- ## Installation ```bash npm install @odion-cloud/capacitor-volume-control npx cap sync ``` ### โœ… No MainActivity Integration Required! This plugin uses `@capacitor-community/volume-buttons` for hardware button detection, which means **no MainActivity code changes needed**. Just install and use! ## Setup 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 ``` ## Usage Examples ### Basic Volume Control ```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 }); ``` ### Volume Watching ```typescript // First, add the event listener await VolumeControl.addListener('volumeButtonPressed', (event) => { console.log('Volume button pressed:', event.direction); // 'up' or 'down' }); // Then start watching await VolumeControl.watchVolume({ disableSystemVolumeHandler: true, // iOS only suppressVolumeIndicator: true, // Android only }); // Stop watching when done await VolumeControl.clearWatch(); ``` ### Advanced Usage ```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 { // Add listener first await VolumeControl.addListener('volumeButtonPressed', this.handleVolumeChange.bind(this)); // Then start watching await VolumeControl.watchVolume({ disableSystemVolumeHandler: true, suppressVolumeIndicator: true }); 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 ```typescript // Add listener await VolumeControl.addListener('volumeButtonPressed', (event) => { console.log('Volume button pressed:', event.direction); }); // Suppress volume indicator on Android await VolumeControl.watchVolume({ suppressVolumeIndicator: true }); // Control different volume types await VolumeControl.setVolumeLevel({ value: 0.7, type: VolumeType.NOTIFICATION }); ``` ### iOS Specific ```typescript // Add listener await VolumeControl.addListener('volumeButtonPressed', (event) => { console.log('Volume button pressed:', event.direction); }); // Disable system volume handler on iOS await VolumeControl.watchVolume({ disableSystemVolumeHandler: true }); // Control voice call volume await VolumeControl.setVolumeLevel({ value: 0.9, type: VolumeType.VOICE_CALL }); ``` ## Error Handling ```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({}); await VolumeControl.watchVolume({}); // This will fail } catch (error) { if (error.message.includes('already been watched')) { console.error('Volume watching is already active'); } } ``` ## React Hook Example ```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 { // Add listener first await VolumeControl.addListener('volumeButtonPressed', (event) => { console.log('Volume button pressed:', event.direction); // You may want to update UI or state here }); // Then start watching 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(); 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 ```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 { // Add listener first await VolumeControl.addListener('volumeButtonPressed', (event) => { console.log('Volume button pressed:', event.direction); }); // Then 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(); 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 ```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 { // Add listener first await VolumeControl.addListener('volumeButtonPressed', (event) => { console.log('Volume button pressed:', event.direction); // You may want to update with actual logic }); // Then 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(); 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: ```bash npm start ``` Build for production: ```bash npm run build ``` Test on device: ```bash npx cap run android npx cap run ios ``` ## Platform Support ### ๐Ÿ“ฆ Capacitor & npm - **Capacitor**: Compatible with Capacitor **5**, **6**, **7**, and **8**. - **npm**: Package is published to [npm](https://www.npmjs.com/package/@odion-cloud/capacitor-volume-control); releases are pushed to GitHub and then published to npm. ### ๐Ÿ“ฑ 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. ```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) ``` #### `watchVolume(options)` Start watching for volume button presses. ```typescript watchVolume({ disableSystemVolumeHandler?: boolean; // iOS: disable system UI suppressVolumeIndicator?: boolean; // Android: hide volume UI }): Promise<void> ``` #### `addListener(eventName, callback)` Listen for volume button press events. ```typescript addListener( 'volumeButtonPressed', callback: (event: { direction: 'up' | 'down' }) => void ): Promise<PluginListenerHandle> // Event data: { direction: 'up' | 'down' } ``` #### `clearWatch()` Stop watching for volume changes. ```typescript clearWatch(): Promise<void> ``` #### `isWatching()` Check if volume watching is currently active. ```typescript 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 with `removeAllListeners()` 2. **Add listener before watching**: Call `addListener()` before `watchVolume()` to ensure events are captured 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. ## Troubleshooting ### Android: Kotlin Version Incompatibility If you encounter the error: ``` Module was compiled with an incompatible version of Kotlin. The binary version of its metadata is 1.9.0, expected version is 1.7.1 ``` **Solution**: Update your project's Kotlin version in `android/build.gradle`: ```groovy buildscript { ext.kotlin_version = '1.9.10' // Update from older version dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } ``` This plugin requires Kotlin 1.9.0+ and Android Gradle Plugin 7.4.2+. ### iOS: Pod Name Mismatch If you're using the scoped npm package name and encounter pod installation issues: **Symptom**: Pod name in Podfile (`OdionCloudCapacitorVolumeControl`) doesn't match the podspec file name (`CapacitorVolumeControl`) **Solution**: Reference the package with an alias in `package.json`: ```json { "dependencies": { "capacitor-volume-control": "npm:@odion-cloud/capacitor-volume-control@^1.0.13" } } ``` Then run: ```bash npm install npx cap sync ios ``` ### Other Common Issues **Volume watching not working on emulators/simulators** - Volume watching requires physical hardware buttons - Test on real devices for volume button detection **Permission denied errors** - Ensure you've added the required permissions to your AndroidManifest.xml or Info.plist - Request permissions at runtime before calling volume control methods **Build errors after updating** - Clean your build folders: ```bash cd android && ./gradlew clean # or cd ios && rm -rf Pods && pod install ``` - Invalidate caches in Android Studio: File โ†’ Invalidate Caches โ†’ Restart ## Changelog See [CHANGELOG.md](https://github.com/odion-cloud/capacitor-volume-control/blob/main/CHANGELOG.md) for a list of changes and version history.