How do I read Bluetooth headset name? - ios

I want to read the bluetooth headset name.
I managed to rout the sound trough the headset and to get the current route for the kAudioSessionProperty_AudioRoute.
I was not able to find any documentation how to read the headset name. Any link, advice or code is welcome.

You can't read the name of the device as it is written in Settings/Bluetooth.
However, you can access to the name written in the device (which sometimes is different, I've seen this case) :
EASessionController = [EADSessionController sharedController];
_accessoryList = [[NSMutableArray alloc] initWithArray:[[EAAccessoryManager sharedAccessoryManager] connectedAccessories]];
for (EAAccessory *key in _accessoryList)
{
NSLog(#"Name : %#", key.name);
}
More information at EAAccessory class.

Related

Unable to cast music to Chromecast

I'm trying to establish a simple audio stream to a Chromecast device. This is just a POC for me to familiarize myself with the API. What I want to do is load up the user's library, select a song, and have it cast over. I've been following the integration guide quite closely but to no avail.
Please find the full project on Github here.
An overview of my code, AppDelegate.m:
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
GCKCastOptions *options = [GCKCastOptions.alloc initWithReceiverApplicationID:kGCKMediaDefaultReceiverApplicationID];
[GCKCastContext setSharedInstanceWithOptions:options];
[GCKCastContext.sharedInstance.sessionManager addListener:self];
}
UITableViewDelegate:
- (void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath
{
MPMediaItem *mediaItem = MusicManager.sharedManager[index];
NSString *path = [MPMediaLibrary.defaultMediaLibrary pathForAssetURL:mediaItem.assetURL];
GCKMediaMetadata *metadata = [GCKMediaMetadata.alloc initWithMetadataType:GCKMediaMetadataTypeMusicTrack];
[metadata setString:mediaItem.title forKey:kGCKMetadataKeyTitle];
[metadata setString:mediaItem.artist forKey:kGCKMetadataKeyArtist];
[metadata setString:mediaItem.albumTitle forKey:kGCKMetadataKeyAlbumTitle];
GCKMediaInformation *mediaInfo = [GCKMediaInformation.alloc initWithContentID:path
streamType:GCKMediaStreamTypeBuffered
contentType:[self fileMIMEType:path]
metadata:metadata
streamDuration:mediaItem.playbackDuration
customData:nil];
[GCKCastContext.sharedInstance.sessionManager.currentCastSession.remoteMediaClient loadMedia:mediaInfo autoplay:YES];
}
I initiate the casting session by tapping the GCKCastButton and Start Session, my TV shows the Cast logo, then when I tap on a specific song, my TV briefly shows the metadata (i.e. song title, artist name, etc.), and then reverts back to the Chromecast logo. On the device, if I remain on the screen presented by GCKCastButton, I can also see the details of the song that is supposed to be currently casting, but quickly changes to "No Media Selected" after a few seconds.
I've checked the file path, the MIME type, everything is correct and playable. I've even tried bundling a short MP3 and trying to cast that, but to no avail.
Can't help but feel like I'm missing something here, the integration guide doesn't really give much more info.
Any insight is appreciated. Thanks!
It seems that I was under the wrong impression that the cast library initiates a stream from the provided file itself. But instead it required an actual URL for the Chromecast device to perform a GET operation on.
I imagine this will be solved by running a server locally on the iOS device.

Trying to Write An Auto Answer App to Test iPhone

I'm trying to write an app to auto answer an iphone when there are incoming calls.
The app can be launched by the user and run in the background.
I'm trying to use the IOS Private APIs to do this, but no success.
This is for testing purpose only and won't be submitted to the app store so using private APIs shouldn't be a problem. The phones aren't jailbroken though.
This is what I have in my AppDelegate.m file (didFinishLaunchingWithOptions):
id callCenterId = [TUCallCenter sharedInstance];
NSLog(#"callCenterId: %#", callCenterId);
TUCallCenter *object = [[TUCallCenter alloc] init];
NSLog(#"object: %#", object);
[object handleCallModelStateChanged: ^{
NSLog(#"this is an incoming call");
}];
When I run the app, I do see some outputs in the debug panel:
2016-02-01 10:32:38.849 AutoAnswer[312:63056] callCenterId: <TUCallCenter: 0x134642b40>
2016-02-01 10:32:38.851 AutoAnswer[312:63056] object: <TUCallCenter: 0x1346496c0>
But nothing is shown when I dial the phone when the app is in the foreground. I can't even get that to work, so forget about making it work in the background.
Any help would be appreciated.

Will en0 always be Wi-Fi on an iOS device?

I’m trying to get the IP address of the wifi interface on iOS devices using getifaddrs() and searching for the en0 name. This is working fine, but it assumes that en0 is always Wi-Fi. Is there some way to ensure that the interface I’m querying is specifically Wi-Fi for future proofing?
I use the following to find the wifi information by looking for a device which has a BSSID for the wifi station. It will also have a SSID.
The following class method returns the wifi info dictionary only. It is a simple extension to also return the device label (ifname variable). It is usually en0, as you have noted, but this would work as a way to make sure you pick up the wifi if it was not en0 in the future.
+ (NSDictionary *)getWifiInfo {
NSDictionary *ret=nil;
// Get the supported interfaces.
NSArray *ifs = (__bridge_transfer id)CNCopySupportedInterfaces();
// Find one with a BSSID and return that. This should be the wifi.
for (NSString *ifnam in ifs) {
NSDictionary *info = (__bridge_transfer id)CNCopyCurrentNetworkInfo((__bridge CFStringRef)ifnam);
NSLog(#"Network device found = %#", info.description);
if (info[(__bridge NSString *)kCNNetworkInfoKeyBSSID]) {
// Return this as it should be the right one given it has a MAC address for the station
ret=info;
}
}
NSLog(#"Exit: returning wifi info = %#", ret.description);
return ret;
}
Once you have the device label based on BSSID, you can then use getifaddrs to get its IP, gateway etc knowing it is the wifi device.
NB This returns nil in the iOS simulator as there is no wifi detected, so for testing you need some workaround code to meet your needs.
A hacky option is to find the IP address with an associated MAC of 02:00:00:00:00:00. iOS hides the MAC address from coders and it has this value.

Objective-C HealthKit identify if source is from Apple iPhone or Apple Watch

I have an app where I am trying to integrate the HealthKit and pull steps related data aggregated by day using the HKStatisticsCollectionQuery. Requirement is to pull steps data specific to only iPhone and Apple Watch devices separately (no de-duplication) which have contributed to the health app.
The HKSource class only exposes the following properties:
name - Cannot be used as the user can change this to anything from just 'XXXX iPhone'
bundleIdentifier - Provides us the UUID for the device (unique per device, so different for every iPhone/Watch), and it looks like com.apple.health.UUID, here's what the Apple documentation says : "For apps, this property holds the app’s bundle identifier. For supported Bluetooth LE devices, this property holds a UUID for the device."
I am able to pull all sources (using a HKSourceQuery) which have the bundleIdentifier prefix of 'com.apple.health', but am unable to deduce which is an Apple iPhone versus which is an Apple iWatch.
Has anybody faced a similar situation before, and is there any other way to identify which source is an iPhone or Apple Watch?
Any help would be great!.Thanks!
Not the best solution but, I have figured out a way to distinguish between the watch and the phone using the following process:
I noticed that all step data coming from the iPhone/Watch have the following bundleIdentifier format:
com.apple.health.DeviceUUID
Note that manually entered data into the Health app has a bundle identifier of com.apple.Health (with a capital 'H').
So, first thing, get the device name for the phone using:
NSString *deviceName = [[UIDevice currentDevice] name];
Next, fetch all the sources for which there is a prefix match of 'com.apple.health' in the bundleIdentifier. This should give you the iPhone and the Apple watch as the valid sources and ignore the manual entries and all other apps.
Next, check if the name of the device is the same in the source, then its your iPhone, the other source should be your Apple Watch.
Here's a sample source query for fetching the sources :
- (void)fetchSources
{
NSString *deviceName = [[UIDevice currentDevice] name];
NSMutableArray *dataSources = [[NSMutableArray alloc] init];
HKQuantityType *stepsCount = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKSourceQuery *sourceQuery = [[HKSourceQuery alloc] initWithSampleType:stepsCount
samplePredicate:nil
completionHandler:^(HKSourceQuery *query, NSSet *sources, NSError *error)
{
for (HKSource *source in sources)
{
if ([source.bundleIdentifier hasPrefix:sourceIdentifier])
{
if ([source.name isEqualToString:deviceName])
// Iphone
else
// Apple Watch
[dataSources addObject:source];
}
}
}];
[self.healthStore executeQuery:sourceQuery];
}
You can now create a predicate with each source for your data pull using the NSPredicate class:
NSPredicate *sourcesPredicate = [HKQuery predicateForObjectsFromSource:source];
Note that my first thought was to match the UUID, but when I generate a UUID using the NSUUID class, it does not match with the one present in the bundle identifier in the pulled sources.
Also, you can change the name of the phone to whatever you want, it will automatically update in the Health app as well.
As I said, not the best solution but works for me, and it's the only way I could find to do this. Please let me know if you were able to find a better solution. Thanks.

Detect attached audio devices iOS

I'm trying to figure out how to detect which if any audio devices are connected on iphone/ipad/ipod. I know all about the audio route calls and route change callbacks but these don't tell me anything about what's attached. They only report where the audio is currently routing. I need to know, for instance, if headphones and/or bluetooth are still attached while audio is routed through the speakers. Or, for instance, if a user plugs in the headset while using bluetooth then decides to disconnect bluetooth, I need to know that the bluetooth is disconnected even as audio is still routing through headphones.
Unfortunately, as of iOS11, it seems there's no API to reliably get the list of the output devices that are currently attached - as soon as the current route changes, you only see 1 device (currently routed) via AVAudioSession's currentRoute.outputs, even though multiple devices may still be attached.
However, for the input, and that includes Bluetooth devices with HFP profile, if the proper Audio Session mode is used (AVAudioSessionModeVoiceChat or AVAudioSessionModeVideoChat for example), one can get the list of the available input via AVAudioSession's availableInputs, and those inputs are listed there even when that device is not an active route - this is very useful when a user is doing a manual override via MPVolumeView from Bluetooth to the speaker, for example, and since HFP is a 2-way IO (has both input and output), you can judge whether output HFP Bluetooth is still available by looking at the inputs.
BOOL isBtInputAvailable = NO;
NSArray *inputs = [[AVAudioSession sharedInstance] availableInputs];
for (AVAudioSessionPortDescription* port in inputs) {
if ([port.portType isEqualToString:AVAudioSessionPortBluetoothHFP]) {
isBtInputAvailable = YES;
break;
}
}
In case of iOS 5 you should use:
CFStringRef newRoute;
size = sizeof(CFStringRef);
XThrowIfError(AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &size, &newRoute), "couldn't get new audio route");
if (newRoute)
{
CFShow(newRoute);
if (CFStringCompare(newRoute, CFSTR("HeadsetInOut"), NULL) == kCFCompareEqualTo) // headset plugged in
{
colorLevels[0] = .3;
colorLevels[5] = .5;
}
else if (CFStringCompare(newRoute, CFSTR("SpeakerAndMicrophone"), NULL) == kCFCompareEqualTo)
}
You can get from AudioSession properties a list of InputSources and OutputDestinations.
Check out these Session Properties:
kAudioSessionProperty_InputSources
kAudioSessionProperty_OutputDestinations
And to query the details of each, you can use:
kAudioSessionProperty_InputSource
kAudioSessionProperty_OutputDestination

Resources