Detect ring/silent switch position change

2020-02-26 07:19发布

问题:

I'm working on an app for which I would like to:

  1. respect the ring/silent switch when playing audio, and

  2. display an icon indicating that sound is muted when the ring/silent switch is set to silent.

Requirement 1 is easy: I'm using AVAudioSessionSoloAmbient as my app's audio session category, so that my audio session will mute itself when the ring/silent switch is off.

Requirement 2 seems considerably harder, because I need some sort of callback, notification, or KVO that will allow me to monitor the position of the switch, but Apple has made it clear that it is unwilling to offer an officially exposed way of doing this. That said, if I can find a nonintrusive way to monitor the switch's position, even one that is technically prohibited (like, say, an internal NSNotification), I would be willing to run it by Apple.

Further, I would prefer not to implement some of the polling solutions I've found elsewhere. See the Related Questions section for an example.


What I've Learned (aka What Doesn't Work)

  • In iOS versions 4 and 5, at least, there was a trick that could be used to get the switch's position by watching the route property of the current audio session. Besides being deprecated by the AVAudioSession class, I can confirm that this trick is no longer an option. The current route, both as reported by the C functions comprising the deprecated Audio Session API and the current AVAudioSession class does not change when the ring/silent switch is toggled.

  • AVSystemController is an internal class that seems to have a lot of promise. Invoking - (BOOL)toggleActiveCategoryMuted on the sharedAVSystemController does indeed mute my app's audio. Further, the shared singleton posts an AVSystemController_SystemVolumeDidChangeNotification notification when the system volume is changed via the volume buttons. Unfortunately, this notification is not posted in response to changes to the ring/silent switch (though this dubiously attributed source says it should).

  • As far as I can tell, there are no NSNotifications posted by any object in response to ring/silent switch position changes. I came to this conclusion after adding myself as an observer to all notifications in the default center:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotification:) name:nil object:nil];
    

    and then toggling the ring/silent switch. Nothing.

  • The AVSystemController class has a promising method with the signature:

    - (BOOL)getActiveCategoryMuted:(BOOL*)arg1;
    

    However, there are two problems with this:

    1. Neither the return value nor the BOOL pointed to by arg1 ever seem to change in response to toggling the ring/silent switch.
    2. Because of the method signature, this method is not (so far as I understand) a candidate for KVO.

  • I suspect that some object sends some other object(s) a GSEventRef when the mute switch is changed, because I see the following in the declaration for event types:

    kGSEventRingerOff = 1012,
    kGSEventRingerOn = 1013,
    

    However, I'm pretty sure I can't intercept those messages, and even if I could, that would be a bit more than "a little" intrusive.


Why I Believe This Is Possible

Put simply: the Instagram app exhibits essentially this behavior. When you watch a video, it respects the ring/silent switch's setting, but displays an icon when the switch is off. The icon disappears and reappears so immediately after moving the switch that I assume it must be event-based, not polling.


Related Questions

  • This question dates back to iOS 4, and uses the methods that I mentioned in my first bullet above.

  • This question is very similar to the one above.

  • This question is (much) more current, asking about iOS 7. However, because I'm willing to accept a minimally intrusive breaking of the private-API rules, I would contend that this is a different question from my own.

  • This answer suggests using a polling method that I would strongly prefer to avoid.