/
VolumeButtonHandler.cs
195 lines (167 loc) · 5.59 KB
/
VolumeButtonHandler.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
using System;
using AudioToolbox;
using MediaPlayer;
using UIKit;
using AVFoundation;
using CoreFoundation;
using Foundation;
namespace RemoteBooth
{
public class VolumeButtonHandler : NSObject
{
private float _initialVolume;
private bool _initialized = false;
private MPVolumeView _volumeView;
private AVAudioSession _session;
private bool _isActive;
private const float maxVolume = 0.95f;
private const float minVolume = 0.05f;
private const string VOLUMECHANGE_KEYPATH = "outputVolume";
private static readonly VolumeButtonHandler _instance = new VolumeButtonHandler();
/// <summary>
/// Will be raised when any volume button was clicked
/// </summary>
public event EventHandler ButtonClicked;
/// <summary>
/// Get the singleton instance.
/// </summary>
/// <value>The instance.</value>
public static VolumeButtonHandler Instance {
get { return _instance; }
}
/// <summary>
/// Private constructor to ensure the singleton
/// </summary>
private VolumeButtonHandler ()
{
}
/// <summary>
/// Initializes the handler
/// </summary>
public void Start()
{
if (_initialized)
return;
NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.WillResignActiveNotification, onApplicationDidChangeActive);
NSNotificationCenter.DefaultCenter.AddObserver(UIApplication.DidBecomeActiveNotification, onApplicationDidChangeActive);
initAudioSession();
disableVolumeHUD();
// Wait for the volume view to be ready before setting the volume to avoid showing the HUD
DispatchQueue.DefaultGlobalQueue.DispatchAfter(new DispatchTime( (Int64)0.1f*1000000),() => {setInitialVolume();});
_initialized = true;
_isActive = true;
}
/// <summary>
/// Deinitializes the handler
/// </summary>
public void Stop()
{
if (!_initialized)
return;
_session.RemoveObserver (this, VOLUMECHANGE_KEYPATH);
NSNotificationCenter.DefaultCenter.RemoveObserver (this);
_initialized = false;
_isActive = false;
}
#region private methods
/// <summary>
/// Sets the system volume.
/// </summary>
/// <param name="value">The new volume value</param>
private void setSystemVolume(float value)
{
MPMusicPlayerController.ApplicationMusicPlayer.Volume = value;
}
/// <summary>
/// Disables the volume HUD.
/// </summary>
/// <remarks>>Creates a new MPVolumeView and adds it to the mainwindow in the "off-screen" area. Adding this view suppresses showing the volume HUD</remarks>
private void disableVolumeHUD()
{
_volumeView = new MPVolumeView (new CoreGraphics.CGRect (float.MaxValue, float.MaxValue, 0, 0));
UIApplication.SharedApplication.Windows [0].AddSubview (_volumeView);
}
/// <summary>
/// Will be called when the system volume has been chanfed
/// </summary>
/// <param name="newValue">New value.</param>
private void OnVolumeChanged(float newValue)
{
if (_initialized && _isActive) {
if (newValue == _initialVolume) {
// Resetting volume, skip blocks
return;
}
if (ButtonClicked != null)
ButtonClicked (this, EventArgs.Empty);
// Reset volume
setSystemVolume(_initialVolume);
}
}
/// <summary>
/// Inits the AVAudioSession.
/// </summary>
private void initAudioSession()
{
_session = AVAudioSession.SharedInstance();
var errorOrNull = _session.SetCategory (AVAudioSessionCategory.Ambient);
if (errorOrNull != null) {
throw new Exception (errorOrNull.Description);
}
errorOrNull = _session.SetActive (true);
if (errorOrNull != null) {
throw new Exception (errorOrNull.Description);
}
_session.AddObserver (this, VOLUMECHANGE_KEYPATH, NSKeyValueObservingOptions.New, IntPtr.Zero);
// Audio session is interrupted when you send the app to the background,
// and needs to be set to active again when it goes to app goes back to the foreground
NSNotificationCenter.DefaultCenter.AddObserver(AVAudioSession.InterruptionNotification, onAudioSessionInterrupted);
}
public override void ObserveValue (NSString keyPath, NSObject ofObject, NSDictionary change, IntPtr context)
{
if (keyPath == VOLUMECHANGE_KEYPATH) {
var volume = (float)(change ["new"] as NSNumber);
OnVolumeChanged (volume);
} else
base.ObserveValue (keyPath, ofObject, change, context);
}
/// <summary>
/// Will be called for AVAudioSession.InterruptionNotification
/// </summary>
/// <param name="notification">Notification.</param>
private void onAudioSessionInterrupted(NSNotification notification)
{
NSDictionary changeInfo = notification.UserInfo;
Int32 interuptionType = (Int32)(changeInfo["AVAudioSessionInterruptionTypeKey"] as NSNumber);
if (interuptionType == 0) { //AVAudioSession.InterruptionTypeEnded:
var errorOrNull = _session.SetActive (true);
if (errorOrNull != null) {
throw new Exception (errorOrNull.Description);
}
}
}
/// <summary>
/// Will be called for UIApplication.WillResignActiveNotification or UIApplication.DidBecomeActiveNotification
/// </summary>
/// <param name="notification">Notification.</param>
private void onApplicationDidChangeActive(NSNotification notification)
{
_isActive = notification.Name == UIApplication.DidBecomeActiveNotification;
}
/// <summary>
/// retrieves and initializes the current volume
/// </summary>
private void setInitialVolume()
{
_initialVolume = _session.OutputVolume;
if (_initialVolume > maxVolume) {
_initialVolume = maxVolume;
setSystemVolume (_initialVolume);
} else if (_initialVolume < minVolume) {
_initialVolume = minVolume;
setSystemVolume (_initialVolume);
}
}
#endregion
}
}