Monday, September 16, 2013

Google Glass Screen Sharing Made Easy (Not)

I needed a simple way to screencast from Glass to my MacBook Pro, and I found it (only after a few hours of searching and tinkering).

Here's the scoop:

1. Install Java for Mac OSX

2. Install the Android SDK
I downloaded both the SDK and the IDE, and ended up putting this in my user directory. I also renamed parent folder "Android" (because I found "adt-bundle-mac-x86_64-20130729" a bit unwieldy). Note that if you rename this parent folder, you'll have to update the SDK directory location in the settings when you launch Eclipse.

3. Update your path to include pointers to the Android Debug Bridge (ADB)
I actually had to create a new file in my user directory called ".bash_profile". (The assorted references I found while searching also talk about a file called ".profile" -- so I went ahead and created both.) Here's what that file / those file should contain:
export PATH="/Users/[YOUR_USER_NAME]/[YOUR_ANDROID_SDK_DIRECTORY]]/sdk/tools/":$PATH
export PATH="/Users/[YOUR_USER_NAME]/[YOUR_ANDROID_SDK_DIRECTORY]]/sdk/platform-tools/":$PATH

4. Turn on debugging on Android phone
Open up your device’s “Settings”,  scroll to the bottom and tap “About phone”, and then tap on the Build Number seven times. Seriously - seven times. Not six, not eight...seven. (All we're missing now is some arcane enchantment or mystic prunes.)
http://developer.android.com/tools/device.html

NOTE: If you don't have an Android device, you can go directly from Glass to the Mac...but that means having to tether yourself via a USB cable during a presentation. To do this, enable debug mode on Glass by going to Settings > Device Info > Turn on Debugging. (Thanks to Ryan Warner for this tip.)

5. Download Android Screencast
I also came across Android Screen Monitor, but I was unable to get that to work. Here's a link in case you, gentle reader, have more success.

6. Connect your phone to your computer
Just plug it into the USB port. You can check to make sure it's connected by running the following command in Terminal:
adb devices

7. Run Android Screencast
Right click on the androidscreencast.jnlp file and select Open With > Java Web Start.

8. Accept  the Security Warning
Yes, you have to take your very life in your hands and run a Java applet from an unknown publisher. Are you feeling lucky? (Or more importantly, have you backup up your machine lately?)


9. Run MyGlass app on Android phone and start screencast

And there you have it. Now where is my cocktail?



Monday, July 1, 2013

A "But" Too Far: Gearing Up to Become a Google Glass Explorer

Good news! I got an invite to join the Google Glass Explorers program.

But I have to pay $1,500 for the privilege. And I have to fly to New York to pick them up and get fitted at Google's Chelsea Market office. And to get full functionality, I need to tether my Glass with an Android phone.

This is already turning out to be a rather expensive endeavor, but I'm still excited and ready to give life on the bleeding edge a try. And I'm planning to blog about my Glass experiences regularly, so watch this space. (And I plan on writing off all Glass related expenses on next year's taxes anyway.)

Me being me, I agonized / thrashed all weekend about the best solution for tethering my Glass. There are three major options:
  1. Use my current iPhone 4 (which I'm still quite happy with, and am invested in Apple ecosystem)
  2. Upgrade to an Android phone (I loose Messages and all my apps, and get locked in to new 2 year contract)
  3. Get a separate Android phone (best of both worlds, but also most expensive)
In the case of the first two options, I'll need to upgrade my AT&T account from the unlimited data plan to the DataPro 5Gb plan that supports tethering. That's an extra $20 bucks a month (for up 5Gb of data...which shouldn't be a problem for me).

But I'm too comfortable and happy with my iPhone to just walk away from iOS at this time (especially with the new goodies coming in iOS7), and I rely heavily on Messages to communicate with family members. But just to be open minded, I did a little analysis and came up with the following arguments / gross generalizations in favor of each platform:

Android
  • Customizable - turn ringer back on after 3 hours, use volume rocker for paging, etc.
  • Google Now - sounds creepy to some, but I'm intrigued
  • Better information sharing between apps
  • Multiple user accounts - I can safely hand off my device to the Geeklings in a pinch
iOS
  • Battery life - not a huge deal, since I generally plug in my phone every night anyway
  • Messenger - synched up between iPhone, iPad, and MacBook Pro
  • Camera - Apple continues to outpace the competition here, but I'm no Ansel Adams
  • Stability of platform / availability of updates
  • Less malware / viruses - a big benefit of the "walled garden"
  • More / better apps - although most successful apps come to Android
  • Existing investment in ecosystem - our household has definitely drunk the Apple Kool-Aid
  • No bloatware / crapware - can be avoided by going with a Nexus device
So I decided to go with option three - a new Android phone..."in for a penny, in for a pound". 

But we're not through yet.

I wanted to avoid carrier and manufacturer bloatware, so was leaning toward a Google Nexus phone. This will get me the latest and greatest Android bits directly from Google as they are released. But the current Nexus 4 -- while it has gotten great reviews -- is almost a year old, and doesn't have all the bells and whistles of the current crop of Android phone champs like the Samsung Galaxy S4 and the HTC One. Google announced the Google Play versions of both the S4 and One at their recent I/O conference, which gets you the hardware with stock Android. But not only am I balking at the price tag ($600 and $650 respectively) for a device that I'm only planning to use for development, they won't start shipping until July 9th.

We have reached our "but" too far.

And since my Glass pickup appointment is for this Saturday, July 6th my decision was finally made. My new Nexus 4 is due to arrive in a couple of days, and I'll post my initial thoughts here soon.

Wednesday, February 20, 2013

Stopwords to Live By


I'm doing some work with SQL Full-Text Search, and I'd forgotten how to display the list of "stopwords" that are currently being ignored when performing a search.

Below is a simple query to get the list of English stopwords on your database. Mine currently includes 154 items, including individual numbers and letters.

What's in your stopwords list?

 SELECT ssw.stopword, slg.name   
 FROM sys.fulltext_system_stopwords AS ssw  
   JOIN sys.fulltext_languages AS slg ON slg.lcid = ssw.language_id  
 WHERE name = 'English'  


Friday, February 8, 2013

RESTful Web.API Services Are So Primitive, Man!


A couple quick and dirty tips from the trenches on how to validate primitive types that map to enums of collections (specifically a list). This has come in handy as I develop a new RESTful Web.API .

First up, Enum.TryParse is now supported by the .NET Framework 4.5. So we can now easily validate a  string to make sure it correctly maps to an existing enum value.

 MyEnum matchingEnumValue;  
 if (!Enum.TryParse(enumValueAsString, true, out matchingEnumValue))  
   throw new ArgumentException(enumValueAsString + " is not a valid enum value.");  

Next up, we want to pass in a delimited string of integer values (e.g. "1|2|3"), and parse that into a collection.

 List<int> integerInList;  
 try  
 {  
   integerInList = integersInString.Split('|').Select(int.Parse).ToList();  
 }  
 catch (Exception ex)  
 {  
   throw new ArgumentException("Could not parse list of integers. " + ex.Message);  
 }  
 if (integerInList == null || integerInList.Count == 0)  
   throw new ArgumentException("Integer list is empty.");  

Easy as bullseye'ing womp rats in my T-16 back home. Share & Enjoy!

Monday, April 23, 2012

Don't Freak Out -- It's Just the Start Screen!


I recently created my first Metro style native Windows 8 prototype applications (in both XAML and JavaScript). Here are my initial impressions of the Windows 8 Consumer Preview and Visual Studio 2011 Beta.

  1. Solid
    I created a VMWare virtual machine both on my Windows 7 box (work) and my MacBook Pro (personal). Both proved to be quite stable...I don't recall getting any lock ups or BSoD (or whatever the Win8 equivalent would be). And remember this is a per-release OS and a beta IDE. Kudos to Microsoft.

  2. Visual Studio is like your favorite concert t-shirt...all washed out, but still rocks
    The official name appears to be "Visual Studio 11", and it works pretty much the same as 2010. The most notable visual difference is that they sucked all the color out of the UI...but that doesn't bother me. It's stable; I immediately knew my way around; and I was able to be productive -- that's all I (and most developers) care about. There's some nice new Metro application templates (for C# and JavaScript projects), and they worked great. I was able to throw a native Metro style prototype app together very quickly.

  3. If you like Windows Phone, you'll like Windows 8
    I own an iPhone4, and live totally within the Apple ecosystem. That being said, I've developed a few Windows Phone apps (both work and personal), and really like what Microsoft has done with the Metro UI. If my phone went all pear-shaped, I'd actually consider a Nokia Lumia 900 Windows Phone for all of 30 seconds before heading back to the altar Genius Bar and ordering up an iPhone4s. Windows Phone is a unique experience, and (based on my very limited exposure) it appears to "just work".

  4. Take some time to get your bearings
    The Metro style Start screen, apps, and touch-centric interaction model are going to take some getting used to, but I think they'll be worth it in the long run. I set up the previous Win8 release (Developer Preview) on my MacBook (again in a virtual machine), but was quickly turned off by the apparent lack of support for mouse and keyboard interaction (as opposed to touch). This time around, the non-touch equivalents are much more clear. 
    1. Top right or bottom right of screen pull up the "Charms" menu
    2. Top left of screen lets you toggle to previous app
    3. Bottom left of screen lets you jump to start menu
    4. To close/exit a Metro app, hover at the top of screen until cursor turn to a hand, then click and drag downward

    Keyboard commands still work just fine, too:
    1. Alt-Tab to switch between apps (Metro and non-Metro)
    2. Hitting Windows key will take you to the Start screen 
    3. While on the Start screen, just start typing for search

    I have to admit to being really frustrated for a couple of days, but after I figured out the above I was feeling pretty comfortable.There is certainly some dissonance between the touch / tablet experience and the mouse / desktop experience...hopefully that will improve over time.

  5. The dreaded Start screen
    I've heard a lot of whining about the Start screen, and a fellow developer made the following observation: "It's just a start screen instead of a start menu." And you know, he's right. It works just the same way as the Start menu that we all know and (presumably) love:
    1. You can get to it by clicking the bottom left of the screen (even though there's no actual UI element until you put your pointer down there)
    2. You can get to it by hitting the Windows key
    3. You can use it to search (just start typing)
    4. You can get to all of your apps (Metro and non-Metro)
    5. Right clicking allows you to organize or delete applications
    6. You can still get to the desktop (yes, the desktop is still there) by hitting Windows-D
Windows 8 appears to have a lot going for it (ARM support, smaller footprint, better performance, support for touch, picture password, Windows to Go), and I know that Microsoft must have had to struggle to come up with something new, fresh, and innovative while not totally alienating their current ginormous, ofttimes whiny user base. I think they've done a great job, but there will be lot of stick-in-the-muds and nattering nabobs of negativism who will say they've blown it.

In the end, they had to come up with something compelling enough to get us out of our comfort zone sand make us want to upgrade...and I think they've done that.

Wednesday, November 9, 2011

Complex Email Validation with Regular Expressions for Fun and Profit

a@b.com is a valid email address.

Well, not according to the regular expression pattern I've used to validate email addresses for last several years. And with new domain name changes coming in 2013, I thought it was time to reexamine and revise my email validation best-practices.

Here's my existing pattern, which works just fine about 90% of the time:
\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*

After reading an excellent (albeit ancient) blog post by Phil Haack (whose short and sweet pattern didn't accommodate a@b.com either), I found a good (and very legible) solution in a comment to said post by SeanG. What I like best about his solution is that he actually broke down the pattern into its component parts, instead of just slapping it down in onelongunintelligiblesinglestring. The commenting may seem a little excessive to some, but when it comes to regular expressions (which I don't tinker with very often), I prefer to be spoon fed.

The end result is a new static C# class based on that code.

One nice feature of this class is that not only can it be used for server-side validation, but it also exposes the pattern so it can be used for the ValidationExpression property on a RegularExpressionValidator control. And I've also handled the case where the email address is not required.


So now I've centralized the pattern in one place, and when those new domain names start showing up in 2013 I can make changes to accommodate them in a single place.


using System;
using System.Text.RegularExpressions;

namespace Web.Business.Validators
{
    public static class EmailValidator
    {
        #region Properties

        public static string RegexPattern
        {
            get
            {

                // <any CHAR excepting <">, "\" & CR, and including linear-white-space>
                string qtext = "[^\\x0d\\x22\\x5c\\x80-\\xff]";

                // <any CHAR excluding "[", "]", "\" & CR, & including linear-white-space>
                string dtext = "[^\\x0d\\x5b-\\x5d\\x80-\\xff]";
                // *<any CHAR except specials, SPACE and CTLs>
                string atom = "[^\\x00-\\x20\\x22\\x28\\x29\\x2c\\x2e\\x3a-\\x3c\\x3e\\x40\\x5b-\\x5d\\x7f-\\xff]+";
                // "\" CHAR 
                string quoted_pair = "\\x5c[\\x00-\\x7f]";
                // <"> *(qtext/quoted-pair) <">
                string quoted_string = string.Format("\\x22({0}|{1})*\\x22", qtext, quoted_pair);
                //atom / quoted-string
                string word = string.Format("({0}|{1})", atom, quoted_string);
                // "[" *(dtext / quoted-pair) "]"
                string domain_literal = string.Format("\\x5b({0}|{1})*\\x5d", dtext, quoted_pair);

                // atom
                string domain_ref = atom; 
                // domain-ref / domain-literal
                string sub_domain = string.Format("({0}|{1})", domain_ref, domain_literal);
                // sub-domain *("." sub-domain)
                string domain = string.Format("{0}(\\x2e{0})*", sub_domain);
                // word *("." word) 
                string local_part = string.Format("{0}(\\x2e{0})*", word);
                // local-part "@" domain
                string addr_spec = string.Format("{0}\\x40{1}", local_part, domain);                 // add starting position and ending position
                string regexPattern = string.Format("^{0}$", addr_spec);

                return regexPattern;
            }
        }

        #endregion

        #region Public Methods

        public static bool IsValid(string emailAddress)
        {
            return IsValid(emailAddress, true);
        }

        /// <summary>
        /// RFC822 complaint email address validation.
        /// see http://iamcal.com/publish/articles/php/parsing_email/ for explaination
        /// </summary>
        /// <param name="emailAddress">Email address to check.</param>
        /// <param name="isRequired">Is email address required?</param>
        /// <returns><c>false</c> if not valid email address, otherwise <c>true</c>.</returns>
        public static bool IsValid(string emailAddress, bool isRequired)
        {
            // Check to see if email address is required

            if (!isRequired && string.IsNullOrEmpty(emailAddress.Trim()))
            {

                // Email address not required
                return true;
            }

            return new Regex(RegexPattern).IsMatch(emailAddress);
        }

        #endregion
    }
}


And just for additional yucks, here's a unit test with some pretty wacky examples of both valid and invalid email addresses. (Test cases come from above blog post and Wikipedia.)

/// <summary>
/// A test for IsValid
/// </summary>
[TestMethod()]
public void IsValidTest()
{
    ValidEmailAttribute target = new ValidEmailAttribute();

    // Test valid email addresses
    Assert.AreEqual(true, target.IsValid(null, false));
   
Assert.AreEqual(true, target.IsValid(string.Empty, false));
   
Assert.AreEqual(true, target.IsValid("a@b.com"));
   
Assert.AreEqual(true, target.IsValid("a@b.co"));
   
Assert.AreEqual(true, target.IsValid("a@b.c"));
   
Assert.AreEqual(true, target.IsValid("a.b.c'@example.com"));
   
Assert.AreEqual(true, target.IsValid(@"""Abc\@def""@example.com"));
   
Assert.AreEqual(true, target.IsValid(@"""Fred Bloggs""@example.com"));
   
Assert.AreEqual(true, target.IsValid(@"""Joe\\Blow""@example.com"));
   
Assert.AreEqual(true, target.IsValid(@"""Abc@def""@example.com"));
   
Assert.AreEqual(true, target.IsValid("customer/department=shipping@example.com"));
   
Assert.AreEqual(true, target.IsValid("$A12345@example.com"));
   
Assert.AreEqual(true, target.IsValid("!def!xyz%abc@example.com"));
   
Assert.AreEqual(true, target.IsValid("_somename@example.com"));
   
Assert.AreEqual(true, target.IsValid("niceandsimple@example.com"));
   
Assert.AreEqual(true, target.IsValid("a.little.unusual@example.com"));
   
Assert.AreEqual(true, target.IsValid("a.little.more.unusual@dept.example.com"));
   
Assert.AreEqual(true, target.IsValid(@"much.""more\ unusual""@example.com"));
   
Assert.AreEqual(true, target.IsValid(@"very.unusual.""@"".unusual.com@example.com"));
   
Assert.AreEqual(true, target.IsValid(@"very.""(),:;<>[]"".VERY.""very\\\ \@\""very"".unusual@strange.example.com"));

    // character @ is missing
    Assert.AreEqual(false, target.IsValid("Abc.example.com"));
    // only one @ is allowed outside quotations marks

    Assert.AreEqual(false, target.IsValid("A@b@c@example.com"));
    // none of the characters before the @ in this example is allowed outside quotation marks
    Assert.AreEqual(false, target.IsValid(@"""(),:;<>[\]@example.com")); 

    // quoted strings must be dot separated or the only element making up the local-part
    Assert.AreEqual(false, target.IsValid(@"just""not""right@example.com")); 

    // spaces, quotes and slashes may only exist when within quoted strings and preceded by a slash 
    Assert.AreEqual(false, target.IsValid(@"this\ is\""really\""not\\allowed@example.com"));
}


Share & Enjoy!

Thursday, August 11, 2011

Open XML SDK and Excel Pivot Tables - Pulling Data

As you may already know, the new Office document formats are pretty much "ZIP" files with XML files inside. These XML documents are formatted according to the Open XML spec, which Microsoft has released version 2.0 of their SDK for interacting with using strongly typed classes in .net.

If you are interested in downloading the SDK it is here:
http://www.microsoft.com/download/en/details.aspx?displaylang=en&id=5124
The documentation for the SDK is here (thought it is rather sparse):
http://msdn.microsoft.com/en-us/library/bb448854.aspx

Now, if all you want to do is read data out of particular cells, the best article I have found thus far explaining this is here:
There is also a useful MSDN article that has a method XLGetCellValue() that you can copy-and-paste into a class:

However, if you are working with pivot tables, this is probably not the case since you want the data and the presentation of the data in the cells may change if the user, say, reorders the spreadsheet.

First you need to get the PivotCacheDefinition from the WorkbookPart. A sample implementation of this might be...

The PivotCacheDefinition has an enumeration of CacheField's, which define each field in the dataset and also contain the values of a particular field in any given row. Since you will need to reference these often, I found it helpful to put them into a dictionary so that I could reference them by name and have the index handy.
I created a class to store the CacheElement and the index...


...and populated the a dictionary from the PivotCacheDefinition:

To get at the rows themselves, you enumerate the PivotTableCacheDefinitionPart.PivotCacheRecords property. Be careful not to confuse this with the "PivotCacheDefinition" we used earlier. Here is some sample code that would use the dictionary above to pull the address value for each row:


This can all be greatly simplified by putting a method that accepts a CacheElement type and a PivotCacheRecord and extracts the value for us. This will always be a type string (since this is XML) which we will have to cast into whatever type of value that we want.

Not all values are stored in the CacheField elements since some of them are strings that are stored in the row/field itself. For these you can use something like...
PivotCacheRecord.ElementAt(x).GetAttributes().First(a => a.LocalName == "v").Value;

I found it very helpful to look inside the XLSX file itself and dig around to make sure I was pulling the correct data and learn where everything is stored. Just use your favorite ZIP extractor and unzip the XLSX. Here is what I found:

(root)\xl\sharedStrings.xml – An index of strings
(root)\xl\worksheets\sheet1.xml – Sheet 1’s cells, which may reference strings in sharedStrings.xml
(root)\xl\pivotCache\pivotCacheRecords1.xml – Each record which indexes values in pivotCacheDefinition1.xml, unless the particular field is of special String type (then it has the value itself)
(root)\xl\pivotCache\pivotCacheDefinition1.xml – The columns in the pivot table data (not what is shown in the Excel file) and the actual values that are indexed