Skip to content
This repository has been archived by the owner on Nov 10, 2023. It is now read-only.

Handle Media Remote Volume Mute #24

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions JPSVolumeButtonHandler/JPSVolumeButtonHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,16 @@ typedef void (^JPSVolumeButtonBlock)();
// A block to run when the volume down button is pressed
@property (nonatomic, copy) JPSVolumeButtonBlock downBlock;

// A block to run when the volume mute button is pressed
@property (nonatomic, copy) JPSVolumeButtonBlock muteBlock;

// Returns a button handler with the specified up/down volume button blocks
+ (instancetype)volumeButtonHandlerWithUpBlock:(JPSVolumeButtonBlock)upBlock downBlock:(JPSVolumeButtonBlock)downBlock;

// Returns a button handler with the specified volume up/down/mute blocks
// volume mute handling is intended to work with a media remote, and may not function correctly with hardware mute
+ (instancetype)volumeButtonHandlerWithUpBlock:(JPSVolumeButtonBlock)upBlock
downBlock:(JPSVolumeButtonBlock)downBlock
muteBlock:(JPSVolumeButtonBlock)muteBlock;

@end
68 changes: 55 additions & 13 deletions JPSVolumeButtonHandler/JPSVolumeButtonHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ @interface JPSVolumeButtonHandler ()
@property (nonatomic, assign) CGFloat initialVolume;
@property (nonatomic, strong) AVAudioSession * session;
@property (nonatomic, strong) MPVolumeView * volumeView;
@property (nonatomic, strong) UISlider * volumeSlider;
@property (nonatomic, assign) BOOL appIsActive;

@end
Expand All @@ -37,8 +38,12 @@ - (id)init {

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeActive:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationDidChangeActive:) name:UIApplicationDidBecomeActiveNotification object:nil];

[self updateInitialVolumeWithDelay];

// run these operations later
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self setInitialVolume];
[self setupVolumeMuteHandling];
}];
}
return self;
}
Expand All @@ -53,6 +58,7 @@ - (void)dealloc {
}
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self.volumeView removeFromSuperview];
self.volumeSlider = nil;
}

- (void)setupSession {
Expand All @@ -68,7 +74,7 @@ - (void)setupSession {
NSLog(@"%@", error);
return;
}

// Observe outputVolume
[self.session addObserver:self
forKeyPath:sessionVolumeKeyPath
Expand Down Expand Up @@ -109,17 +115,15 @@ - (void)audioSessionInterrupted:(NSNotification*)notification {
- (void)disableVolumeHUD {
self.volumeView = [[MPVolumeView alloc] initWithFrame:CGRectMake(MAXFLOAT, MAXFLOAT, 0, 0)];
[[[[UIApplication sharedApplication] windows] firstObject] addSubview:self.volumeView];

[self.volumeView.subviews enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([obj isKindOfClass:[UISlider class]]) {
self.volumeSlider = obj;
*stop = YES;
}
}];
}

- (void)updateInitialVolumeWithDelay {
// Wait for the volume view to be ready before setting the volume to avoid showing the HUD
double delayInSeconds = 0.1f;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
[self setInitialVolume];
});
}

- (void)setInitialVolume {
self.initialVolume = self.session.outputVolume;
if (self.initialVolume > maxVolume) {
Expand All @@ -131,10 +135,33 @@ - (void)setInitialVolume {
}
}

- (void)setupVolumeMuteHandling {
// Needs to be done after setting initial volume
if (self.volumeSlider && self.muteBlock) {
// if muted, set to initial volume so everything starts in the same state
if (self.volumeSlider.value == 0) {
self.volumeSlider.value = self.initialVolume;
}
// listen for changes
[self.volumeSlider addTarget:self action:@selector(volumeSliderChanged:) forControlEvents:UIControlEventValueChanged];
}
}

- (void)volumeSliderChanged:(id)sender {
if (self.volumeSlider.value == 0.0) {
// mute button hit
// run block and reset volume slider, don't want to stay in mute
if (self.muteBlock) self.muteBlock();
self.volumeSlider.value = self.initialVolume;
}
}

- (void)applicationDidChangeActive:(NSNotification *)notification {
self.appIsActive = [notification.name isEqualToString:UIApplicationDidBecomeActiveNotification];
if (self.appIsActive) {
[self updateInitialVolumeWithDelay];
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[self setInitialVolume];
}];
}
}

Expand All @@ -149,6 +176,21 @@ + (instancetype)volumeButtonHandlerWithUpBlock:(JPSVolumeButtonBlock)upBlock dow
return instance;
}

// Returns a button handler with the specified volume up/down/mute blocks
+ (instancetype)volumeButtonHandlerWithUpBlock:(JPSVolumeButtonBlock)upBlock
downBlock:(JPSVolumeButtonBlock)downBlock
muteBlock:(JPSVolumeButtonBlock)muteBlock
{
JPSVolumeButtonHandler *instance = [[JPSVolumeButtonHandler alloc] init];
if (instance) {
instance.upBlock = upBlock;
instance.downBlock = downBlock;
instance.muteBlock = muteBlock;
}
return instance;
}


#pragma mark - KVO

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
Expand Down