Code Monkey vs. Silverlight Audio Capture

I’ve been working on a new project (Symphony, https://github.com/ermau/Symphony) that is heavily media centric. The general idea is to provide better support to .NET for a whole range of multimedia scenarios, whether they’re recording and playing back audio, decoding a song, or what have you. In order to reach the broadest audience possible, I decided early on to include as much Silverlight support as I possibly could. Part of that supports is bringing Silverlight’s built in microphone support under Symphony’s unified API (which is, to say, wrapping it). My head and desk are no longer on speaking terms, but more to the point I’d love to know (at least in the case of Silverlight) if Microsoft bothers to get people who have experience in the area their assigned to write libraries for.

A lesson I learned the hard way in developing Gablarski was that available audio devices change, a lot. Some enable/disable themselves automatically when detecting a 3.5mm being connected/disconnected, others are USB devices that get plugged in and removed at will, so on and so forth. As any logical person would deduce from this truth, it becomes important to be able to track what device is currently the default, when that default changes, and when a device is removed. So let’s look at Silverlight’s APIs and see what’s available; first we have the CaptureDeviceConfiguration:

public static class CaptureDeviceConfiguration
{
	public static bool AllowedDeviceAccess { get; }

	public static bool RequestDeviceAccess();
	public static ReadOnlyCollection<VideoCaptureDevice> GetAvailableVideoCaptureDevices();
	public static ReadOnlyCollection<AudioCaptureDevice> GetAvailableAudioCaptureDevices();
	public static VideoCaptureDevice GetDefaultVideoCaptureDevice();
	public static AudioCaptureDevice GetDefaultAudioCaptureDevice();
}

Ok, so this gives us a way to get the current default and a list of all available devices. As you’ll notice, however, it does not provide an event notifying you that the default device has changed. I’ve found that the best user experience for voice based applications is to default to the default audio device and to track that device. If I manually set another device, the application will gladly remember what I’ve selected, but I should be given the choice of “default” which follows the current default instead of just the default at selection time. Without a notification to tell the application when this has changed, the developer is left with no good way to provide an excellent user experience. The best I could come up with to work around this is to poll the current default device in order to provide the notification that Symphony’s API demands. Having the notification at all is a positive outcome, but having to balance between how much delay between polls and how many extra cycles we’re wasting checking is undesirable. Here’s what I came up with:

public class SilverlightAudioCaptureDeviceProvider
{
	public SilverlightAudioCaptureDeviceProvider()
	{
		if (!CaptureDeviceConfiguration.AllowedDeviceAccess)
			CaptureDeviceConfiguration.RequestDeviceAccess();

		UpdateDevices();
		this.pollTimer.Interval = TimeSpan.FromMilliseconds (250);
		this.pollTimer.Tick += PollTimerOnTick;
		this.pollTimer.Start();
	}

	public event EventHandler DefaultDeviceChanged;

	public AudioCaptureDevice DefaultDevice
	{
		get;
		private set;
	}

	private readonly DispatcherTimer pollTimer = new DispatcherTimer();
	private AudioCaptureDevice lastDefaultDevice;

	private void PollTimerOnTick (object sender, EventArgs eventArgs)
	{
		UpdateDevices();
	}

	private void UpdateDevices()
	{
		AudioCaptureDevice defaultDevice = CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();
		if (DefaultDevice != defaultDevice) // This is actually a bug which I’ll explain later
		{
			DefaultDevice = defaultDevice;
			OnDefaultDeviceChanged (EventArgs.Empty);
		}
	}

	private void OnDefaultDeviceChanged (EventArgs e)
	{
		var changed = DefaultDeviceChanged;
		if (changed != null)
			changed (this, e);
	}
}

(This is not the Symphony API, I’ve reduced it to a decorator off sorts to provide these functions for any Silverlight use for the reader.)

Now that we have default device notifications, we need to check into notifications for devices becoming unavailable (which could be a user manually disabling the device in Windows, unplugging a USB mic, etc.) It’s obviously not on CaptureDeviceConfiguration class, so let’s check the AudioCaptureDevice class:

public class CaptureDevice
	: DependencyObject
{
	public static readonly DependencyProperty FriendlyNameProperty;
	public static readonly DependencyProperty IsDefaultDeviceProperty;

	public string FriendlyName { get; }
	public bool IsDefaultDevice { get; }
}

public sealed class AudioCaptureDevice
	: CaptureDevice
{
	public static readonly DependencyProperty AudioFrameSizeProperty;

	public int AudioFrameSize { get; set; }
	public ReadOnlyCollection<AudioFormat> SupportedFormats { get; }
	public AudioFormat DesiredFormat { get; set; }
}

So there’s no status changed events and there’s no properties to check if the device is even still alive. Let’s step back and take a look at a basic usage of this API. First you new up a CaptureSource:

public sealed class CaptureSource
	: DependencyObject
{
	public static readonly DependencyProperty VideoCaptureDeviceProperty;
	public static readonly DependencyProperty AudioCaptureDeviceProperty;

	public CaptureSource();

	public event EventHandler<ExceptionRoutedEventArgs> CaptureFailed;
	public event EventHandler<CaptureImageCompletedEventArgs> CaptureImageCompleted;

	public VideoCaptureDevice VideoCaptureDevice { get; set; }
	public AudioCaptureDevice AudioCaptureDevice { get; set; }
	public CaptureState State { get; }

	public void Start();
	public void Stop();
	public void CaptureImageAsync();
}

So, we take our audio device and assign CaptureSource.AudioCaptureDevice to it and call start. CaptureFailed looks promising, I would expect it to be raised if the device had disappeared before or after starting the capture. I can’t help but notice that there’s explicit methods for capturing a single image, but there’s no way from right here to get audio samples (or multiple images). In order to actually get the audio samples you then have to create (as there are no built-in implementations) and initialize an AudioSink:

public abstract class AudioSink
{
	public CaptureSource CaptureSource { get; set; }

	protected abstract void OnCaptureStarted();
	protected abstract void OnCaptureStopped();
	protected abstract void OnFormatChange (AudioFormat audioFormat);
	protected abstract void OnSamples ( long sampleTimeInHundredNanoseconds, long sampleDurationInHundredNanoseconds,
					byte[] sampleData);
	~AudioSink();
}

Here are some more alternatives, OnCaptureStopped() might be called if the device goes away during capture. So, I set up a test with all of these components, put breakpoints everywhere, started the capture and disabled a device while it was running. Can you guess which event/callback breakpoint was hit? I asked a few different people that question when I was attempting to do all of this and the answer is so ridiculous that no one guessed it their first try. The answer is NONE of them. If the device you’re capturing with goes away while you’re capturing, you received no notification whatsoever. No event raised, no callback fired, no 0 value arguments to OnSamples, no CaptureSource.State == CaptureState.Failed, no exception, nothing.

As before, this is a pretty critical factor of user experience. The user may not even know that the device just went away. I can just imagine the “Hey, can anyone hear me?” and subsequent “I’ve been talking for 30 minutes and no one could hear me..”. If you’ve used a VoIP application while playing games, you’ve probably done this before with a mute switch on your headset. So, there are two alternatives: We can poll the complete list of devices to see if the device is listed anymore, or we can have a timeout for OnSamples calls (as it does stop being called when the device goes away). Since we already have a poll for the default device, I decided to go with polling.

First, we’ll need a decorator to hold the status and event:

public class AudioCaptureDeviceEx
{
	public AudioCaptureDeviceEx (AudioCaptureDevice device)
	{
		if (device == null)
			throw new ArgumentNullException ("device");

		this.device = device;
	}

	public event EventHandler IsAvailableChanged;

	public bool IsAvailable
	{
		get { return this.isAvailable; }
		set
		{
			if (this.isAvailable == value)
				return;

			this.isAvailable = value;
			var changed = IsAvailableChanged;
			if (changed != null)
				changed (this, EventArgs.Empty);
		}
	}

	// 1:1 AudioCaptureDevice wrappings left out for brevity

	private readonly AudioCaptureDevice device;
	private bool isAvailable = true;
}

We’ll want a list of devices with our additions in the device provider:

private readonly Dictionary<AudioCaptureDevice, AudioCaptureDeviceEx> devices =
	new Dictionary<AudioCaptureDevice, AudioCaptureDeviceEx>();

public IEnumerable<AudioCaptureDeviceEx> AudioDevices
{
	get
	{
		lock (this.devices)
			return this.devices.Values.Where (d => d.IsAvailable).ToList();
	}
}

Now we need to update our polling routine:

private void UpdateDevices()
{
	AudioCaptureDeviceEx currentDefault = null;

	lock (this.devices)
	{
		var newDevices = new HashSet<AudioCaptureDevice> (CaptureDeviceConfiguration.GetAvailableAudioCaptureDevices());
		foreach (var kvp in this.devices)
		{
			if (kvp.Value.IsDefaultDevice)
				currentDefault = kvp.Value;

			if (newDevices.Remove (kvp.Key))
			{
				kvp.Value.IsAvailable = true;
				continue;
			}

			this.devices[kvp.Key].IsAvailable = false;
		}

		foreach (AudioCaptureDevice device in newDevices)
		{
			var d = new AudioCaptureDeviceEx (device);
			if (d.IsDefaultDevice)
				currentDefault = d;

			this.devices.Add (device, d);
		}
	}

	if (DefaultDevice != currentDefault)
	{
		DefaultDevice = currentDefault;
		OnDefaultDeviceChanged (EventArgs.Empty);
	}
}

Seems like it should work, right? Yeah, I thought so too. In my tests (on my machine anyway), I discovered AudioCaptureDevice.IsDefaultDevice always returns false. You’d think there’d be an Assert.IsTrue (CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice().IsDefaultDevice) somewhere in their tests. So, I need to instead go back to querying CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice() and then reconcile the AudioCaptureDeviceEx instances. This is where I was in for another surprise; remember that bug I said I’d tell you about later? AudioCaptureDevice does not implement equality functions and new instances are returned on each of CaptureDeviceConfiguration’s methods, so there’s no other way to compare them but their “FriendlyName” (which is actually cut off, which makes it impossible to display an exact match to what’s in Windows). Comparing by their name will probably work most of the time, it’s definitely not ideal. So now we have:

private class AudioCaptureDeviceEqualityComparer
	: IEqualityComparer<AudioCaptureDevice>
{
	public static readonly AudioCaptureDeviceEqualityComparer Instance
		= new AudioCaptureDeviceEqualityComparer();

	public bool Equals (AudioCaptureDevice x, AudioCaptureDevice y)
	{
		if (x == y)
			return true;
		if (x == null || y == null)
			return false;

		return (x.FriendlyName == y.FriendlyName);
	}

	public int GetHashCode (AudioCaptureDevice obj)
	{
		if (obj == null)
			throw new ArgumentNullException ("obj");

		return obj.FriendlyName.GetHashCode();
	}
}

We’ll need to update the dictionary holding the devices:

private readonly Dictionary<AudioCaptureDevice, AudioCaptureDeviceEx> devices =
	new Dictionary<AudioCaptureDevice, AudioCaptureDeviceEx> (AudioCaptureDeviceEqualityComparer.Instance);

And finally the updated UpdateDevices:

private void UpdateDevices()
{
	AudioCaptureDevice currentDefault = CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();
	AudioCaptureDeviceEx currentDefaultEx = null;

	lock (this.devices)
	{
		var newDevices = new HashSet<AudioCaptureDevice> (CaptureDeviceConfiguration.GetAvailableAudioCaptureDevices(),
							AudioCaptureDeviceEqualityComparer.Instance);
		foreach (var kvp in this.devices)
		{
			if (currentDefault != null && kvp.Value.FriendlyName == currentDefault.FriendlyName)
				currentDefaultEx = kvp.Value;

			if (newDevices.Remove (kvp.Key))
			{
				kvp.Value.IsAvailable = true;
				continue;
			}

			this.devices[kvp.Key].IsAvailable = false;
		}

		foreach (AudioCaptureDevice device in newDevices)
		{
			var d = new AudioCaptureDeviceEx (device);
			if (currentDefault != null && d.FriendlyName == currentDefault.FriendlyName)
				currentDefaultEx = d;

			this.devices.Add (device, d);
		}
	}

	if (DefaultDevice != currentDefaultEx)
	{
		DefaultDevice = currentDefaultEx;
		OnDefaultDeviceChanged (EventArgs.Empty);
	}
}

When I was wrapping AudioCaptureDevice for Symphony’s capture device contract, I discovered another fun bit. Symphony has it’s own definition for audio formats, so I have to keep a map of formats so I can pass the correct one to Silverlight. Ok, no problem, I’ll just call ToDictionary with a little conversion function I wrote, and.. ArgumentException. AudioCaptureDevice.SupportedFormats actually contains duplicates. I realize this is probably just directly reading from whatever API it’s sitting on, but when you’re trying to provide a simple API you should really clean up crap like this.

Let’s review

  • No notifications for default device changed
  • No notifications for devices added / removed
  • AudioCaptureDevice.FriendlyName is fixed width
  • AudioCaptureDevice.IsDefaultDevice just plain broken
  • AudioCaptureDevice isn’t equatable
  • AudioCaptureDevice.SupportedFormats contains duplicates

If someone handed me code like this for such a wide reaching project like Silverlight and told me it was done, I’d fire them. That it actually made it to production baffles me, somewhere a manager completely failed at their job. I’m really not sure how you can go this wrong, but the more I explore Silverlight, the more I come to the conclusion that it’s a hack job and that if it wasn’t for WP7 I really wouldn’t bother wasting my time trying to support it for anything. The Mono crew have unified platform support figured out, how is it that Microsoft with all its resources (or perhaps that’s exactly the reason?) has managed to create such different platforms. I very quickly ran out of votes on Silverlight’s UserVoice page: http://dotnet.uservoice.com/forums/4325-silverlight-feature-suggestions

The complete final code for the device provider is available here: https://gist.github.com/1047203

P.S. If I got this wrong, I’d love to be corrected.

Code Monkey vs. Elderly Preconditioning

For anyone that’s given tech support to their parents, grandparents, or any older customers, one thing stands out: They don’t read what’s in front of them. Although the same could be said for most average computer users, it is my belief that is far more prevalent amongst the older generations. When these older people were growing up, computers weren’t expected household items. More importantly, they were easy to break software-wise.

As a result of this the older users, who’ve never taken the time to learn about their computer, have been preconditioned to never “try it and see”. When TIAS is likely to break possibly everything, it becomes easy to avoid doing anything you’re unsure of. This is especially true for those that don’t have tech support to go to (for those that do, that’d be you). As a result, it’s far less likely that these people will ever try to figure out how to do some things on their own, due to a fear of breaking things.

While it may sound ridiculous to think that uninstalling some simple program may break the whole computer, they have no clue how a computer works. Even as operating systems become more and more reliable, most will remain stuck in this preconditioned fear. Even on platforms like iOS where it’s extremely difficult to break by normal use, this behavior continues.

What can we do to stop this? Much to the dismay to this audience (or maybe not, depending on how much you get asked for support), I believe that platforms like iOS that are heavily locked down are the future of mainstream computing and gaming. We’re already seeing it with the rise of console gaming, and the old (and young) adopting the iPad. That’s not to say full fledged PCs are going anywhere anytime soon, but more and more they’ll be regulated to the office and the power user.

These locked down systems are decidedly easier to use. No, they’re not anywhere near as capable as a normal personal computer today, but that won’t stay the case for long. On top of that fact, the ones having issues with computers are generally those that only need very little from it. Advanced content creation will remain with more capable systems, but mass content consumption and simple content creation is the bulk of the average user’s use for computers and can easily be regulated to less capable systems. These generally less capable systems are even, in some cases, better suited to these tasks to begin with.

In the short term, for those annoyed by support requests, you can refuse to help. Demand they read before they ask, even make them recite what’s on the screen. Do not accept “well I don’t know” or “If I knew, I wouldn’t have to ask you” or even “You can figure it out a lot faster than me” as excuses. It may sound mean, but there’s few other ways to teach them that don’t require a significant portion of your time.

You can help them by teaching them how to find things. When we look for functionality in a program, we know to check context menus and program menus. These things are key to finding things, but right clicking things may not be all that apparent to those who don’t use the computer anywhere near as much as us.

For once, I don’t blame the application developers. Although some fail miserably at creating good user experiences, many are adequate. Despite developers’ best efforts, people don’t read and sadly not every application can be created without needing description or instruction.

Code Monkey vs. iOS4

So having ranted about the hardware, it’s only fitting that I rant about the software on it. Like my article on the hardware, I’m only going to focus on what I think is wrong with it; there’s plenty of articles on the good stuff already.

Multitasking

UI

Apple’s implementation of multitasking, UI-wise, is nearly the worst I could think of. Not only did they fail completely at avoiding a task manager, they ended up implementing one that confuses users even more than a proper one would. On top of this, they broke the cardinal rule: they broke a users understanding of simply closing an app.

Previous to iOS4, pressing the home button was synonymous with closing an app. There was no fear of it continuing to drain the battery, continuing to monitor your location or what have you. This changed slightly with the introduction of push notifications, but the effect was the same, the app was dead and gone. iOS4 has broken this completely, worse than simply changing an expected behavior, they’ve made the old behavior annoyingly difficult to get to. Let’s compare, shall we?

Really closing an app in iOS3-:

  1. Press the home button.

Really closing an app in iOS4:

  1. Press the home button.
  2. Double press the home button.
  3. Tap and hold on an icon.
  4. Press the red minus above the app.

I mean, really? What’s worse is this isn’t even remotely obvious; I’d be willing to bet that most users of iOS4 don’t even know about this. You may be wondering why this is even a problem since most apps don’t multitask anyway and state saving is a useful feature.

When introducing what is effectively just a MRU list, Steve Jobs said “These are all the apps that are running.” Problem is, that’s not even remotely true. It’s just a list if recently used apps, with absolutely no indication of wether they are actually still running or just in a suspended state (if that, since non-iOS4 apps are listed too). Beyond the fact that not actually closing the apps pollutes this terrible ‘multitasking UI’, I’m not even given a choice up front about whether an app should keep running in the background or not.

On the flip side of this, you could say that things like Skype and Pandora are now consistent to their core-feature counterparts. Except the fact that both of these apps use significant bandwidth and in the case of Pandora, more power. You could also say that 9.9 times out of 10, people will not want to actually close Skype while on a phone call, which I’d agree with. In the case of Pandora though, you have to weigh user expectations with consistency. Specifically for Pandora, consistency probably wins out because it’s more convenient than the alternative. However, I feel that core-feature alternatives are the exception and that we can’t even begin to apply this argument to all types of apps.

UI Ideas

The last time I was blogging, I posted an article proposing my solution for the UI of multitasking to avoid a task manager among other things. In it, I proposed that true multitasking be treated like GPS location, that you be asked by the OS whether or not to allow this app to keep draining battery. Having seen Apple’s solution now, I still think it’s a good idea and have some new ideas.

Part of the problem now is that the default behavior is completely up to the app developer who may or may not give you a choice. Sometimes when I exit Colloquy, I actually want to exit and sign out of Colloquy. Sure, Colloquy could likely add a dialog asking me in their task completion thread on whether to keep running or not (which I’d like, actually), but that doesn’t solve it for say Pandora or anything else.

Instead of having explicit closing, what about explicit multitasking? If I tap the home button, it would exit the app as it always has. If I double tap the home button, up would pop the multitasking UI. From there, I could either go to the home screen, or to another running app. Either way, exited through this multitasking UI, the running app would always be allowed to multitask. Options could be setup to allow this from the ‘normal’ app exit, but it would be opt-in.

As for saved state, I’m on the fence. I’d still like to keep a way to explicitly clear it, as is sometimes necessary when apps get confused (I’m looking at you, Trillian.) If it wasn’t for this, I’d want them not in the multitasking list. Sadly the software world is imperfect and I suspect there will always be an app I have to beat with a stick. Non-iOS4 apps should never have shown in the list to begin with, but it won’t really matter for much longer.

Actual possibilities

Many have criticized Apple for not allowing ‘true multitasking’ and instead watering it down to what they saw as important. However, I’m not going to do that, because I actually like the approach Apple has taken. Not only that, but the work to integrate things like Pandora and Skype to look and act like the similar core features of the phone impressed me.

Where Apple fell short though is their belief that Push Notifications was sufficient for all messaging clients. Problem is, not all of us tech informed people have a box running 24/7 they can use as a IRC proxy. Sure, I could probably set one up on my Linode VPS, but I shouldn’t have to. Persistent connections are an important but annoyingly missing piece. Everyone likes to use IRC as the example of this, but let’s use an example more relevant to the every day crowd.

Let’s take some multiplayer game you’re playing with your friend when you get a phone call. Now, a newly compiled single player game would happily have it’s state saved and then restored upon hanging up. Since it’s a multiplayer game however, one of two things just happened: 1. Because the developer took a ton of extra time, the game saved its state on its own and is now trying to reconnect. Or the more likely: 2. You’ve completely lost the connection and have to start the game over. Allowing the game to keep the connection would let it simply pause and then when the call is over it could simply un-pause and continue on it’s merry way with significantly less effort from the developer.

Contacts

I guess at this point it shouldn’t come as a shock to me that Apple seems to have no problem regressing features. Originally I’d planned to complain about the fact that I thought they’d removed custom labels from phone numbers and email addresses. When presented with the new interface that didn’t force me to select one and a preexisting label to the left, I assumed there was no way to change it. As it turns out however, it’s been moved to a menu that appears when you tap that label in edit mode.

Now, if I’d never used iOS 4 before, I’d probably have tried tapping on the label to change it. It seems like a pretty obvious spot to get to that menu on its own. However having been using iOS since 2.0, I was pretty used to it the way it was. When I saw the way I’d been used to was gone, I assumed the feature was gone and I bet I’m not alone.

On its own, the new design is simpler and has significantly less friction. However, as with multitasking, they’ve gone ahead and totally screwed with expected behavior.  I mean, was anyone really upset that adding a phone number or email address took a few seconds longer than it does now?

Email

Threading

Apple, if you’re going to steal/copy/whatever a feature, at least do it right. My biggest hope for email was that I’d get threaded messages from Gmail, where I love them to death. Upon setting up my email account on the new phone, I eagerly went into a 52 email thread and was promptly disappointed. Where are my own messages? Forum threads show my responses, Gmail shows my responses, but Mail does not. “Thread different”, perhaps?

Without seeing my own responses inline, the threading feature is all but useless. The point is to be able to see the entire conversation between parties, and that includes being able to see what you said. Being able to see them together should of helped avoid going into an email and backing out to try to find the previous one, but that hasn’t been remedied at all. Surely that must be an option I can enable, at least, right? Nope.

Worse still is the interface, the back and forth navigation is still just as annoying as it was before. Yes, sure, it’s an improvement because the messages are all together but the beauty of Gmail’s interface is that everything is right there. Even given the small size of the screen, I think that Gmail’s interface is totally doable. Simply pull the email down to reveal the subjects of the other messages, tap to expand. “But wait, that’s as just as many actions as before!” you say. Yes, but it skips the navigation animations that become time consuming quickly when compounded over several emails.

Gmail Archive

I guess at this point I really shouldn’t be surprised, but let us continue on this face-meets-palm quest pointing out where Apple broke user expectations. Previously setting up a Gmail account, delete would (insert surprise joke here) actually trash the email! In iOS4, Apple has added the ‘archive’ feature for Gmail accounts, which is great. What is not great is that it’s now turned on by default. So let me get this straight, Apple introduces a feature (threading) that just affects presentation and has it off by default. When Apple introduces a feature that radically affects where your email goes, however, it’s now on by default even for existing accounts?

Now, granted, at least it’s not the reverse (going from archive to a delete) so at least it’s not more destructive. However, yet once again, Apple has taken existing behavior and decided that you’d rather have this other new behavior. I’m not sure who archives their Gmail messages instead of moving them to trash, but I certainly don’t. Maybe we could have both? Swipe right to delete, left to archive. We’ve already associated swipe right with delete pretty much everywhere, let’s take new completely different behavior and put it somewhere else instead of replacing things.

Annoying Tidbits

  • Why is there a delay in opening a folder?
  • Still no bulk delete in MMS.
  • Safari, maybe I don’t want to go to something in my bookmarks if I don’t have an open page.

Conclusion

Generally speaking, I’m quite happy with iOS4 and do not wish to go back to iOS3. As I said before though, this has just been about the bad.

Apple seems to have no regard for previous user expectations, which I find very disturbing. I don’t have the total sales numbers for iPhones previous to iPhone 4, but this helps put it in perspective. The thing is, when you have something that works and works well, there’s likely not a good enough justification for changing it. It’s only when expectations of users is to be frustrated, or for the feature to not even work, that it’s OK to go ahead and gut the feature.

Code Monkey vs. Antennagate Press Conference

I am honestly appalled at Apple, not that I wasn’t already. So much of their “hard data” is easily defeated by a simple explanation that it makes me wonder if people will be gullible enough to become apologists regurgitating their “hard data” as an excuse. It’s not even an explanation, it’s nothing but downplay with some numbers that sound impressive on their face. I was dissapointed (but not surprised) to hear nothing about the glass being more easily scratchable. I’m more worried about the glass, since there’s no workaround once it scratches, than the reception.

So first off, playing that YouTube video is not cute, it’s arrogant and offensive. While logically correct from a customer’s stand point, “don’t buy one or take it back” is hardly a “we love our customers” approach and certainly not something I want to hear from the product’s company. Customers have voiced a legitimate concern, and Apple has simply shrugged.

“Hard Data”

In the press conference, Jobs liked to keep coming back to sarcastically stating that because of the sheer amount of coverage this issue has received, it must be on a grande scale. After each reiteration, he mentions some very low number as the counter. Here’s the thing about Web 2.0, Steve: All the coverage is people complaining. Post after post, comment after comment, video after video, people are complaining in large numbers. Simply because they do not go through methods you can track, doesn’t mean the outcry isn’t there.

The percentage of iPhone 4 users who have called into AppleCare about the antenna or reception is reported to be 0.55%. For once, I’m glad Job’s took the time to share the current sales numbers with us: 3M iPhone 4s sold. 0.55% of 3M is 16,500 and that’s just the people who’ve bothered to contact support. I’m not sure what land Apple lives in, but 16,500 is a lot of people, especially given that out of the users experiencing a problem, only a fraction of those experiencing it will ever bother to complain about it. Many others, like myself, knew of the issue and were simply awaiting Apple’s public response.

Low return rates is easily explained by the fact that many have been waiting to see what Apple would do. Seeing if they’d eventually give away cases, have some form of recall, or if some software fix would correct the problem. On top of this, you have the 3G users like me who’ve been waiting for a newer faster device and are willing to put up with this to get the rest. “Put up with” is not something you really want your customers associating with your product. Where’s the “hard data” on customer annoyance?

Call drop rates are again easily explained and are just as troubling. Many reviews point to the iPhone 4 not dropping calls in areas their 3G or 3GS previously did. So, if it’s actually not dropping the same calls as before, and the delta is only between 0-1, wouldn’t that imply that dropping calls from this new issue is more than a <1 delta? On top of this, people know about the issue and have begun (well, at least I have) holding their phone in less comfortable manners to ensure that we don’t drop the call. Where’s the “hard data” on hand comfort throughout calls? Admittedly, the lack of case theory may affect the numbers, but all this does is imply to me that a case is required.. which I have never wanted and shouldn’t be forced into.

“Perspective”

“.. but this does put it in perspective.” In your reality distortion field, maybe. The numbers cited sound great, until you actually stop and think about them. Job’s attitude throughout the presentation was dismissive and arrogant, which does put things into perspective.

All this being said, as far as the antenna issue itself goes, the free case solution is a good one. $29.99 was absurd for what the Bumper really was. Kudos to Apple for even going further and including other cases to ensure quantity, although I should probably wait until we see what the other selections are. The refund date, however, should have been extended for those waiting to see what Apple was going to do.

Yes, Apple made it extremely visible both in the bars issue and in where the spot to kill the signal is. However, what I would like to see if there’s any one spot on these other phones that you can kill the signal with a single finger. Somehow, I doubt it, but I certainly can on the iPhone 4 and that is why it’s still a bigger issue than with the other phones.

Code Monkey vs. iPhone 4

I have one and I hate it just as much as I love it. I find that I can actually do what I want in a reasonable amount of time and minimal frustration due to the sheer speed and general responsiveness of the device. Yet I find myself generally less happy with it than I was with my iPhone 3G when I got it. To have come this far, it’s sad to see how far Apple has fallen with this device.

Now, there’s been plenty of coverage on the various issues, many of which I feel completely miss the mark (that’s what we get for enabling everyone to have global soap boxes I guess). Some get close, but over complicate the issues with speculation that bring about more debate than consensus. I only plan to focus on what’s wrong, hardware wise; I’ll write about iOS4 separately.

Comfort

Awkward ways to hold the phone to get a signal aside, this is by far the most uncomfortable iPhone yet. Sure, it looks beautiful, but as a smart phone comfort should be on the top of the list. The iPhone has reached a point where thinner is not better, it doesn’t help consumers at this point and it only makes manufacturing and design that much harder. No one was really crying for a thinner iPhone, because the larger curved back was already comfortable. This new thinner design only makes it harder and more painful to hold on to, having to bring your fingers farther in, bending them even more. Honestly it reminds me greatly of the original NES controllers, turning your hands into painful fixed claws.

To add another to the ‘curved was better’ category, the new sides are just downright painful. Especially when it comes to certain games, the sides actually become painful because the steel band is not the same thickness as the phone. This leaves a nice little crevice on either side of the band that your finger gets pushed into and it doesn’t take very long before this starts to hurt. I suppose I should just ‘avoid holding it that way’, but if we keep adding ways to hold it to the ‘avoid’ list, I’m not going to be able to hold this thing at all.

There’s no doubt that the iPhone 4 looks and feels very solid and precise. The iPhone 3G / 3GS look like toys with their plastic parts in comparison. However, even with their slightly weaker feeling due to the plastic, there’s a comfort and solidity from the full curved back that fits more naturally into my hand. Being larger and filling your hand gives you a greater sense of having a good solid grip on the phone. If I could take the 4′s guts and stick them in a 3G case, I’d do it without hesitation.

Glass

I’ve had my iPhone 3G since the day after launch (2 years), it’s never worn a case and I just toss it in my pocket with my wallet. The plastic back is scratched up like crazy, but I’ve never really cared about that. The glass, however, only has a few minor scratches on the top and bottom, none through the screen. My iPhone 4 has more scratches all over its front glass in the two weeks than I’ve had it (treated exactly the same) than my 3G does. Think about that for a second, more scratches in two weeks than in two years. For what gain, exactly?

My guess is that it has something to do with the fact that they’re laminating the screen and glass together. There’s no doubt even just the lack of a gap between the glass and screen looks significantly better, but at what cost? Is it going to matter if there’s a gap or not when the screen is so covered in scratches that I can’t hardly use the device? Only time will tell, but my spidey sense tells me the answer is ‘no’.

Now, to be fair, the scratches are basically invisible while the screen is on but I wonder how long that will last as they accumulate. I think there’s a definite case for false advertising here; Apple’s own web page says “… the glass is ultradurable and more scratch resistant than ever”. More scratch resistant?! I’d have been perfectly happy if it was as scratch resistant as my 3G, but as it’s been demonstrated all over the web (and in my personal experience), it’s actually significantly worse.

Antenna

I have a hard time expressing just how ridiculous and unfathomable the whole ordeal has been. Speculation abounds, but I lack such guesses because I am completely dumbfounded. Suffice to say, Apple has lost my respect over this issue alone, let alone everything else that’s wrong with this device. I, personally, experience the death grip and it’s no exaggeration and it’s not simply that the bars are wrong. Web page loading can be paused and continued at will repeatedly just by touching and releasing a specific spot on the phone.

There’s been a lot of commenting about this being an issue for left handed people. Here’s the thing, this issue affects data just as much as phone calls. This means that when holding it in my left hand to touch and interact with using my right hand, that wonderful spot of death is firmly cupped in my hand. It’s not specific to just holding it in a way that you’d make a phone call. Please stop saying Apple hates lefties, it’s obvious Apple hates us all equally.

The absurdity of all this is mind boggling, despite the fact that some signal attenuation may happen with any cell phone, including previous iPhones. The simplest solution, even with the current overall design, would be to move the ‘death point’ to the right side. You’re moving it from what’s always going to hit the palm, greatly increasing the chances that it’ll be bridged, to the side where only your fingers are present which would be far less likely to be bridged. During a right handed phone call (at least in my hands), I’m not cupping that gap, my thumb rises over and latches the right side of the phone.

Although Apple claims that there are no real issues beyond what affects a normal cell phone and says that a lot of the confusion stems from a bug in the software, I’m calling bullshit. For me, it doesn’t just degrade the signal, it removes it completely. Data transfers stop dead in their tracks, even simple webpages won’t continue loading. I wish I had a 3GS or something to record it, but I’ve paused web page loads multiple times in the same load just by pressing the ‘death point’. To be fair, I have seen some cases where I had to use more than finger to interrupt it, giving credence to the idea that it is actually just extreme attenuation.

However, other cell phones don’t put the antenna in the best possible place to be blocked/shorted/whatever is going on. They don’t take the most comfortable way to hold the phone and tell you not to just “avoid holding it that way” or to buy a case (for which they sell an insanely overpriced piece of plastic). Besides the cases meant to clip to a belt, who actually bought a case for their cell phone pre-smart phone? Nearly no one, so why is this different?

The response has varied wildly, anywhere from “calm down, buy a case and enjoy your iPhone” all the way to lawsuits. The expectation of us to buy a case just to be able to hold our phone in a comfortable manner and still have it work is just absurd, given what we’re already paying for it. What’s worse has been Apple’s-and Steve Jobs’, if email postings are to be believed-response to the whole ordeal. If this was such a nonissue that was present in old phones, there wouldn’t be a such a sudden and huge outcry over it, so downplaying and dismissing the issue is just an insult to Apple’s customers.

Apple should at least acknowledge that the design of the iPhone 4 does, in fact, make it easier to significantly drop a signal just by holding it. They really should be handing out free bumpers to appease customers, as they’re certainly not worth $29, and even more so would be worth the hit to help curb the large amount of negative press they’re getting.

The Bad

In a time where Android is starting to come under fire, this should of been the perfect time to surge ahead in positive mindshare. Instead, they’ve made several wrong turns with a phone that iPhone 3G customers have been waiting for every since they saw how fast the 3GS was. Now these customers are stuck, sure they could return their phones, but after seeing the beautiful display and just how fast it is, it’s unlikely they’ll do anything of the sort. On top of this, they’ve dismissed and insulted customers without so much as an olive branch.

I’ve always had a hate-love relationship with Apple, but this whole ordeal has left me bitter and hateful of Apple. The iPhone 4 now has, at least among the tech-informed (you know, the guys who recommend devices to the non-tech informed), a negative mindshare. It’s full of problems, and the Android users are sitting back and laughing with their relatively minor issues in comparison.

I will likely end up with a case to protect the antenna and glass, and hopefully round out the back some for more comfort. The fact that I now actually feel that I have to buy a case is, in my opinion, a complete failure on Apple’s part. It’s fragile and uncomfortable, a terrible combination if you want to keep something intact and not piss off your customers.

Code Monkey vs. Asynchronous Lists

I guess the theory is that a list that starts displaying its results immediately will allow the user to get to what they’re looking for quicker. The problem is that most of the time this couldn’t be farther from the truth.

Steam's "Add a Game List"

Steam's "Add a Game list" dialog

When you open this, you already know what you’re looking for. The list is sorted alphabetically, so you start to scroll down to the letter the game starts with. However the list only serves to frustrate because it doesn’t actually load alphabetically, so when you find the letter you’re surprised to find that your game isn’t listed. You’re not given any indication that the list is still loading, so is the game just not going to be listed? Or has the dialog simply not gotten around to loading it yet?

The fun continues when the game finally shows up. Now that you see the game listed you go to select it so you can move on, no need for the list to finish loading, right? Just as you go to click on the item you were looking for, it moves clear out of view since something was just added many items up somewhere you don’t see or care about. Thus begins a game of cat and mouse trying to find and select your game before it moves out of view again. You could just give up in frustration and just wait for the list to load, but since the only indication you have is less reliable than listening for popcorn to finish popping, it’s equally frustrating as the cat and mouse.

Worse still, this design tricks users into trying to find what they’re looking immediately for because there’s items in the list to look through. Our interactions with computers are very action-reaction oriented, we issue a command and wait for the computer to respond. With this list, the computer starts to respond immediately, so we’re prompted to issue our next command prematurely and no one likes being premature.

This experience is the same for the .NET tab of the Add Reference dialog for Visual Studio 2010, as well as that of the “Programs and Features” list of Windows Vista and 7. Someone, please tell me how this is preferable to simply waiting a second or two and then finding your item in a nice alphabetically sorted list. It usually takes no more time, and is significantly less frustrating. At the very least, provide some form of loading indicator so the user knows for sure when the least is complete. Otherwise you’re only pissing off your users and earning your place in my Hall of Shame.