audio: rework bluetooth headset recognition

parent 6ed2bd57
This diff is collapsed.
......@@ -386,7 +386,7 @@
<rect key="frame" x="0.0" y="0.0" width="94" height="198"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
</imageView>
<button opaque="NO" tag="39" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="6uv-FV-mUL" userLabel="routesBluetoothButton">
<button opaque="NO" tag="39" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="6uv-FV-mUL" userLabel="routesBluetoothButton" customClass="UIBluetoothButton">
<rect key="frame" x="0.0" y="0.0" width="94" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Bluetooth"/>
......@@ -428,7 +428,7 @@
<action selector="onRoutesEarpieceClick:" destination="-1" eventType="touchUpInside" id="RqY-KM-K7M"/>
</connections>
</button>
<button opaque="NO" tag="41" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="go9-SC-yzb" userLabel="routesSpeakerButton">
<button opaque="NO" tag="41" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="go9-SC-yzb" userLabel="routesSpeakerButton" customClass="UISpeakerButton">
<rect key="frame" x="0.0" y="132" width="94" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Speaker"/>
......@@ -979,7 +979,7 @@
<rect key="frame" x="0.0" y="0.0" width="84" height="198"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
</imageView>
<button opaque="NO" tag="39" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="uPj-2J-1oA" userLabel="routesBluetoothButton">
<button opaque="NO" tag="39" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="uPj-2J-1oA" userLabel="routesBluetoothButton" customClass="UIBluetoothButton">
<rect key="frame" x="0.0" y="0.0" width="84" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Bluetooth"/>
......@@ -1021,7 +1021,7 @@
<action selector="onRoutesEarpieceClick:" destination="-1" eventType="touchUpInside" id="SlB-O8-JVa"/>
</connections>
</button>
<button opaque="NO" tag="41" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="dsJ-hG-m4c" userLabel="routesSpeakerButton">
<button opaque="NO" tag="41" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="dsJ-hG-m4c" userLabel="routesSpeakerButton" customClass="UISpeakerButton">
<rect key="frame" x="0.0" y="132" width="84" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Speaker"/>
......@@ -1155,7 +1155,9 @@
<button hidden="YES" opaque="NO" tag="54" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="mxV-GD-O3N" userLabel="routesButton" customClass="UIToggleButton">
<rect key="frame" x="166" y="0.0" width="84" height="63"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Route"/>
<accessibility key="accessibilityConfiguration" label="Route">
<bool key="isElement" value="YES"/>
</accessibility>
<inset key="titleEdgeInsets" minX="0.0" minY="38" maxX="0.0" maxY="0.0"/>
<state key="normal" image="routes_default.png">
<color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
......
......@@ -428,7 +428,7 @@
<action selector="onRoutesEarpieceClick:" destination="-1" eventType="touchUpInside" id="RqY-KM-K7M"/>
</connections>
</button>
<button opaque="NO" tag="41" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="go9-SC-yzb" userLabel="routesSpeakerButton">
<button opaque="NO" tag="41" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="go9-SC-yzb" userLabel="routesSpeakerButton" customClass="UISpeakerButton">
<rect key="frame" x="0.0" y="132" width="100" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Speaker"/>
......@@ -1021,7 +1021,7 @@
<action selector="onRoutesEarpieceClick:" destination="-1" eventType="touchUpInside" id="SlB-O8-JVa"/>
</connections>
</button>
<button opaque="NO" tag="41" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="dsJ-hG-m4c" userLabel="routesSpeakerButton">
<button opaque="NO" tag="41" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" adjustsImageWhenHighlighted="NO" adjustsImageWhenDisabled="NO" lineBreakMode="middleTruncation" id="dsJ-hG-m4c" userLabel="routesSpeakerButton" customClass="UISpeakerButton">
<rect key="frame" x="0.0" y="132" width="100" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES"/>
<accessibility key="accessibilityConfiguration" label="Speaker"/>
......
......@@ -28,7 +28,13 @@
}
@property(weak, nonatomic) IBOutlet UIRoundedImageView *avatarImage;
@property(weak, nonatomic) IBOutlet UILabel *nameLabel;
@property(weak, nonatomic) IBOutlet UISpeakerButton *speakerButton;
@property(weak, nonatomic) IBOutlet UILabel *addressLabel;
@property(weak, nonatomic) IBOutlet UIToggleButton *routesButton;
@property(weak, nonatomic) IBOutlet UIView *routesView;
@property(weak, nonatomic) IBOutlet UIBluetoothButton *routesBluetoothButton;
@property(weak, nonatomic) IBOutlet UIButton *routesEarpieceButton;
@property(weak, nonatomic) IBOutlet UISpeakerButton *routesSpeakerButton;
- (IBAction)onDeclineClick:(id)sender;
......
......@@ -48,6 +48,11 @@ static UICompositeViewDescription *compositeDescription = nil;
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(bluetoothAvailabilityUpdateEvent:)
name:kLinphoneBluetoothAvailabilityUpdate
object:nil];
LinphoneCall *call = linphone_core_get_current_call([LinphoneManager getLc]);
if (!call) {
if (![PhoneMainView.instance popCurrentView]) {
......@@ -61,6 +66,37 @@ static UICompositeViewDescription *compositeDescription = nil;
ms_free(uri);
[_avatarImage setImage:[FastAddressBook imageForAddress:addr thumbnail:NO] bordered:YES withRoundedRadius:YES];
}
[self hideSpeaker:LinphoneManager.instance.bluetoothAvailable];
}
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[NSNotificationCenter.defaultCenter removeObserver:self];
}
- (IBAction)onRoutesBluetoothClick:(id)sender {
[self hideRoutes:TRUE animated:TRUE];
[[LinphoneManager instance] setBluetoothEnabled:TRUE];
}
- (IBAction)onRoutesEarpieceClick:(id)sender {
[self hideRoutes:TRUE animated:TRUE];
[[LinphoneManager instance] setSpeakerEnabled:FALSE];
[[LinphoneManager instance] setBluetoothEnabled:FALSE];
}
- (IBAction)onRoutesSpeakerClick:(id)sender {
[self hideRoutes:TRUE animated:TRUE];
[[LinphoneManager instance] setSpeakerEnabled:TRUE];
}
- (IBAction)onRoutesClick:(id)sender {
if ([_routesView isHidden]) {
[self hideRoutes:FALSE animated:[[LinphoneManager instance] lpConfigBoolForKey:@"animations_preference"]];
} else {
[self hideRoutes:TRUE animated:[[LinphoneManager instance] lpConfigBoolForKey:@"animations_preference"]];
}
}
- (IBAction)onDeclineClick:(id)sender {
......@@ -68,6 +104,46 @@ static UICompositeViewDescription *compositeDescription = nil;
if (call) {
linphone_core_terminate_call([LinphoneManager getLc], call);
}
[PhoneMainView.instance popCurrentView];
if (![PhoneMainView.instance popCurrentView]) {
[PhoneMainView.instance changeCurrentView:DialerView.compositeViewDescription];
}
}
- (void)hideRoutes:(BOOL)hidden animated:(BOOL)animated {
if (IPAD)
return;
if (hidden) {
[_routesButton setOff];
} else {
[_routesButton setOn];
}
_routesBluetoothButton.selected = LinphoneManager.instance.bluetoothEnabled;
_routesSpeakerButton.selected = LinphoneManager.instance.speakerEnabled;
_routesEarpieceButton.selected = !_routesBluetoothButton.selected && !_routesSpeakerButton.selected;
if (hidden != _routesView.hidden) {
// if (animated) {
// [self hideAnimation:hidden forView:_routesView completion:nil];
// } else {
[_routesView setHidden:hidden];
// }
}
}
- (void)hideSpeaker:(BOOL)hidden {
if (IPAD)
return;
_speakerButton.hidden = hidden;
_routesButton.hidden = !hidden;
}
#pragma mark - Event Functions
- (void)bluetoothAvailabilityUpdateEvent:(NSNotification *)notif {
bool available = [[notif.userInfo objectForKey:@"available"] intValue];
[self hideSpeaker:available];
}
@end
This diff is collapsed.
......@@ -47,11 +47,6 @@
#define LINPHONE_LOGS_MAX_ENTRY 5000
static void audioRouteChangeListenerCallback(void *inUserData, // 1
AudioSessionPropertyID inPropertyID, // 2
UInt32 inPropertyValueSize, // 3
const void *inPropertyValue // 4
);
static LinphoneCore *theLinphoneCore = nil;
static LinphoneManager *theLinphoneManager = nil;
......@@ -256,11 +251,10 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre
- (id)init {
if ((self = [super init])) {
AudioSessionInitialize(NULL, NULL, NULL, NULL);
OSStatus lStatus = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback, (__bridge void *)(self));
if (lStatus) {
LOGE(@"cannot register route change handler [%ld]", lStatus);
}
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(audioRouteChangeListenerCallback2:)
name:AVAudioSessionRouteChangeNotification
object:nil];
NSString *path = [[NSBundle mainBundle] pathForResource:@"msg" ofType:@"wav"];
self.messagePlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL URLWithString:path] error:nil];
......@@ -298,15 +292,7 @@ struct codec_name_pref_table codec_pref_table[] = {{"speex", 8000, "speex_8k_pre
}
- (void)dealloc {
OSStatus lStatus = AudioSessionRemovePropertyListenerWithUserData(
kAudioSessionProperty_AudioRouteChange, audioRouteChangeListenerCallback, (__bridge void *)(self));
if (lStatus) {
LOGE(@"cannot un register route change handler [%ld]", lStatus);
}
[[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:kLinphoneGlobalStateUpdate];
[[NSNotificationCenter defaultCenter] removeObserver:self forKeyPath:kLinphoneConfiguringStateUpdate];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)silentPushFailed:(NSTimer *)timer {
......@@ -713,7 +699,9 @@ static void linphone_iphone_display_status(struct _LinphoneCore *lc, const char
if (linphone_core_get_calls_nb(theLinphoneCore) == 0) {
[self setSpeakerEnabled:FALSE];
[self removeCTCallCenterCb];
bluetoothAvailable = FALSE;
// disable this because I don't find anygood reason for it: bluetoothAvailable = FALSE;
// furthermore it introduces a bug when calling multiple times since route may not be
// reconfigured between cause leading to bluetooth being disabled while it should not
bluetoothEnabled = FALSE;
/*IOS specific*/
linphone_core_start_dtmf_stream(theLinphoneCore);
......@@ -1800,69 +1788,76 @@ static int comp_call_state_paused(const LinphoneCall *call, const void *param) {
OSStatus lStatus = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &lNewRouteSize, &lNewRoute);
if (!lStatus && lNewRouteSize > 0) {
NSString *route = (__bridge NSString *)lNewRoute;
notallow = [route isEqualToString:@"Headset"] || [route isEqualToString:@"Headphone"] ||
[route isEqualToString:@"HeadphonesAndMicrophone"] || [route isEqualToString:@"HeadsetInOut"] ||
[route isEqualToString:@"Lineout"] || IPAD;
notallow = [route containsString:@"Heads"] || [route isEqualToString:@"Lineout"] || IPAD;
CFRelease(lNewRoute);
}
return !notallow;
}
static void audioRouteChangeListenerCallback(void *inUserData, // 1
AudioSessionPropertyID inPropertyID, // 2
UInt32 inPropertyValueSize, // 3
const void *inPropertyValue // 4
) {
if (inPropertyID != kAudioSessionProperty_AudioRouteChange)
return; // 5
LinphoneManager *lm = (__bridge LinphoneManager *)inUserData;
- (void)audioRouteChangeListenerCallback2:(NSNotification *)notif {
// there is at least one bug when you disconnect an audio bluetooth headset
// since we only get notification of route having changed, we cannot tell if that is due to:
// -bluetooth headset disconnected or
// -user wanted to use earpiece
// the only thing we can assume is that when we lost a device, it must be a bluetooth one (strong hypothesis though)
if ([[notif.userInfo valueForKey:AVAudioSessionRouteChangeReasonKey] integerValue] ==
AVAudioSessionRouteChangeReasonOldDeviceUnavailable) {
bluetoothAvailable = NO;
}
bool speakerEnabled = false;
CFStringRef lNewRoute = CFSTR("Unknown");
UInt32 lNewRouteSize = sizeof(lNewRoute);
OSStatus lStatus = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &lNewRouteSize, &lNewRoute);
if (!lStatus && lNewRouteSize > 0) {
NSString *route = (__bridge NSString *)lNewRoute;
CFStringRef newRoute = CFSTR("Unknown");
UInt32 newRouteSize = sizeof(newRoute);
OSStatus status = AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &newRouteSize, &newRoute);
if (!status && newRouteSize > 0) {
NSString *route = (__bridge NSString *)newRoute;
LOGI(@"Current audio route is [%s]", [route UTF8String]);
speakerEnabled = [route isEqualToString:@"Speaker"] || [route isEqualToString:@"SpeakerAndMicrophone"];
if (!IPAD && [route isEqualToString:@"HeadsetBT"] && !speakerEnabled) {
lm.bluetoothEnabled = TRUE;
lm.bluetoothAvailable = TRUE;
NSDictionary *dict = [NSDictionary
dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:lm.bluetoothAvailable], @"available", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kLinphoneBluetoothAvailabilityUpdate
object:lm
userInfo:dict];
bluetoothAvailable = TRUE;
bluetoothEnabled = TRUE;
} else {
lm.bluetoothEnabled = FALSE;
bluetoothEnabled = FALSE;
}
CFRelease(lNewRoute);
}
if (speakerEnabled != lm.speakerEnabled) { // Reforce value
lm.speakerEnabled = lm.speakerEnabled;
NSDictionary *dict =
[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:bluetoothAvailable], @"available", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kLinphoneBluetoothAvailabilityUpdate
object:self
userInfo:dict];
CFRelease(newRoute);
}
}
- (void)setSpeakerEnabled:(BOOL)enable {
OSStatus ret;
speakerEnabled = enable;
UInt32 override = kAudioSessionUnspecifiedError;
if (enable && [self allowSpeaker]) {
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_Speaker;
AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride),
&audioRouteOverride);
bluetoothEnabled = FALSE;
} else {
UInt32 audioRouteOverride = kAudioSessionOverrideAudioRoute_None;
AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(audioRouteOverride),
&audioRouteOverride);
if (!enable && bluetoothAvailable) {
UInt32 bluetoothInputOverride = bluetoothEnabled;
ret = AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
sizeof(bluetoothInputOverride), &bluetoothInputOverride);
// if setting bluetooth failed, it must be because the device is not available
// anymore (disconnected), so deactivate bluetooth.
if (ret != kAudioSessionNoError) {
bluetoothAvailable = bluetoothEnabled = FALSE;
}
}
if (bluetoothAvailable) {
UInt32 bluetoothInputOverride = bluetoothEnabled;
AudioSessionSetProperty(kAudioSessionProperty_OverrideCategoryEnableBluetoothInput,
sizeof(bluetoothInputOverride), &bluetoothInputOverride);
if (override != kAudioSessionNoError) {
if (enable && [self allowSpeaker]) {
override = kAudioSessionOverrideAudioRoute_Speaker;
ret = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(override), &override);
bluetoothEnabled = FALSE;
} else {
override = kAudioSessionOverrideAudioRoute_None;
ret = AudioSessionSetProperty(kAudioSessionProperty_OverrideAudioRoute, sizeof(override), &override);
}
}
if (ret != kAudioSessionNoError) {
LOGE(@"Failed to change audio route: err %d", ret);
}
}
......
......@@ -41,7 +41,7 @@
<action selector="onHistoryClick:" destination="-1" eventType="touchUpInside" id="22"/>
</connections>
</button>
<button opaque="NO" tag="3" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="8" userLabel="contactsButton" customClass="UIIconButton">
<button opaque="NO" tag="3" contentMode="scaleToFill" selected="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" id="8" userLabel="contactsButton" customClass="UIIconButton">
<rect key="frame" x="90" y="0.0" width="90" height="66"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" widthSizable="YES" flexibleMaxX="YES" flexibleMinY="YES" heightSizable="YES" flexibleMaxY="YES"/>
<accessibility key="accessibilityConfiguration" label="Contacts"/>
......
......@@ -39,36 +39,13 @@ static void audioRouteChangeListenerCallback(void *inUserData, // 1
[button update];
}
- (void)initUISpeakerButton {
INIT_WITH_COMMON {
AudioSessionInitialize(NULL, NULL, NULL, NULL);
OSStatus lStatus = AudioSessionAddPropertyListener(kAudioSessionProperty_AudioRouteChange,
audioRouteChangeListenerCallback, (__bridge void *)(self));
if (lStatus) {
LOGE(@"cannot register route change handler [%ld]", lStatus);
}
}
- (id)init {
self = [super init];
if (self) {
[self initUISpeakerButton];
}
return self;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self initUISpeakerButton];
}
return self;
}
- (id)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (self) {
[self initUISpeakerButton];
}
return self;
}
......
......@@ -9,3 +9,6 @@
#import "Log.h"
#import "Utils.h"
#import "UISpeakerButton.h"
#import "UIBluetoothButton.h"
linphone @ 60708a53
Subproject commit 218db02a1ab4590d31032b4cbce25b5c34d508e7
Subproject commit 60708a53ab17ccb5084f3ffcbc288edbb5bd3633
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment