Phone Number Formatter

When writing apps, you often re-use your code, improving it along the way. One function I’ve used in at least 4 apps (iDialer, web, Phone.com Android app, and another unannounced app) and implemented in 3 languages (Java, Javascript, C) is a phone number formatter.

My requirements for this seemingly simple function:

  1. It must use an easy to configure “dial plan” for formatting.
  2. It must be able to format a “partial” phone number, i.e. one you’re typing in right now, or a complete phone number
  3. It must be able to emulate the behavior of the very excellent iPhone dialer.

Javascript:

To see an older iteration of the function in Javascript, look at my iDialer config page, click on the dialplan “advanced” link.

C:

For an even older iteration, look at my iDialer source code.

Java:

Here is my most current solution, in JAVA:


	/**
	 * A semicolon separated list of formatting strings to format. "N"
	 * represents any single digit. [1-9()-] represents an exact digit. The
	 * underscore "_" represents a SPACE (not an underscore). These rules will
	 * be followed sequentially until one is matched.
	 */
	private static final String DIALPLAN = "+1_NNN_NNN_NNNN;1_(NNN)_NNN-NNNN;NNN-NNNN;(NNN)_NNN-NNNN";

	/**
	 * Formats a phone number according to the rules defined in
	 * {@link #DIALPLAN}.
	 *
	 * @param number
	 *            the non-formatted number
	 * @param isPartialOk
	 *            true if the number passed in "number" are just the first few
	 *            digits of the formatted number. This might be the case on a
	 *            dialpad where the number is being inputted right now
	 * @return
	 */
	public static String formatDialplanNumber(String number, boolean isPartialOk) {
		StringBuilder output = new StringBuilder(32);

		/*
		 * k = index of "number" that we're trying to match d = index of
		 * "DIALPLAN" that we're trying to match cd = character of "DIALPLAN" at
		 * index "d" cn = character of "number" at index "k" ln = length of
		 * "number" match = does it match so far
		 */
		int k = 0;
		boolean match = true;
		int ln = number.length();
		for (int d = 0; d < DIALPLAN.length(); d++) {

			char cd = DIALPLAN.charAt(d);
			char cn = k < ln ? number.charAt(k) : 0;

			// move on to the next rule
			if (cd == ';') {
				if (match && k == ln)
					break;

				k = 0;
				match = true;
				if (output.length() > 0)
					output.delete(0, output.length());
				continue;
			}

			if (!match)
				continue;

			// next character in "number" must match exactly
			if (cd == '+' || cd == '#' || cd == '*' || cd >= '0' && cd <= '9') {
				if (k < ln && cn == cd) {
					output.append(cd);
					k++;
				} else {
					match = false;
				}
			}

			// next character in "number" must be a digit
			else if (cd == 'N') {
				if (k == ln) {
					if (isPartialOk)
						output.append(' ');
					else
						match = false;
				} else if (cn >= '0' && cn <= '9') {
					output.append(cn);
					k++;
				} else {
					match = false;
				}
			}

			// just add space to the output
			else if (cd == '_') {
				output.append(' ');
			}

			// just add the literal character to the output
			else if (cd == '(' || cd == ')' || cd == '-' || cd == '.') {
				output.append(cd);
			}
		}

		if (!match || k != ln) {
			// Didn't match anything... just rely on built-in phone number
			// formatter
			return PhoneNumberUtils.formatNumber(number);
		}

		// Remove non-numbers from the end of the string
		int end = output.length();

		int i = output.indexOf("( ");
		if (i > -1)
			end = i;

		while (end > 0
				&& (output.charAt(end - 1) == ' ' || output.charAt(end - 1) == '-')) {
			end--;
		}

		String formatted = output.substring(0, end);
		return formatted;
	}

Antennagate

Certainly you’ve heard of “Antennagate” by now. It’s all the hype surrounding the fatal flaw in the iPhone 4′s genius new antenna design that causes it to lose quite a bit of signal strength when you hold the phone. I haven’t tried it myself, but I have tried holding a couple of my other phones in a way that would consistently make them lose signal strength.

Interesting fact #1: No matter what I do, I cannot get the iPhone 3GS to lose signal strenth. Even if I completely surround the phone with both of my hands.

Interesting fact #2: I can cause my HTC EVO 4G to lose signal strength consistently, by holding it in a way that I would never actually hold it. Holding it in this position also disables the camera. Good thing there’s a backup camera up front :)