Unit Converter and a Custom Control in WP7

I recently finished the development on my first Windows Phone 7 app, titled Unit Converter.  While the idea is hardly unique, I decided to write one for two reasons 1) it was a good chance to get some hands on experience developing WP7 apps and 2) it was something I needed while traveling through Japan.

Most of the code in the application is straightforward and easy to understand. There was one obstacle I ran into that I felt was worth blogging about: custom controls.  The app supports a number of different units.  I needed a way to select these units.  I choose to use LoopingSelector control, which is available via the WP7 Toolkit, in the Primitives namespace.

My first thought was to make these selections available on the screen itself.  However, given the way the LoopingSelector displays and the effect it tries to achieve, it just didn’t look right.  This is where I drew inspiration from the DatePicker control and how it uses a second page to support the selection.

My first attempt at this worked, but was pretty flakey.  I used a TextBox and whenever it got focus Navigated to the page supporting the selection.  After selection the user would “Go Back” to the main page and the unit selected would be shown.  The flakiness came in with the management of the Focus.  I found it very hard to get ride of the focus once the user returned to the page.  This meant the user would have to select somewhere else on the screen.  Like I said, flaky.

It was at this point, drawing upon more inspiration from the DatePicker that I finally achieved a good solution, a custom control.  And thankfully, I could use the DatePicker source code from the Toolkit.

What I ended up doing was creating a custom button, similar to the DatePicker, and internalizing everything.  It ended up working quite well.

First thing to do is to inherit your class from Control.  This will give you access to several methods and allow you to use the class in XAML.

   1: [TemplatePart(Name = ButtonPartName, Type = typeof(ButtonBase))]

   2: public abstract class ConversionUnitPickerBase : Control

   3: {

   4:    // code goes here

   5: }

The TemplatePart attribute denotes what sort of base type this control “is”.  In this case, we are viewing the control as basic button. The actually look of this control is applied through a style, shown here:

  1:         <Style TargetType="pickers:ConversionUnitPickerBase" x:Key="ConversionUnitPicker">
  2:             <Setter Property="Background" Value="{StaticResource PhoneTextBoxBrush}" />
  3:             <Setter Property="BorderThickness" Value="0"/>
  4:             <Setter Property="Foreground" Value="{StaticResource PhoneTextBoxForegroundBrush}" />
  5:             <Setter Property="HorizontalContentAlignment" Value="Left" />
  6:             <Setter Property="PickerPageUri" Value="/Pickers/ConversionPicker.xaml" />
  7:             <Setter Property="Template">
  8:                 <Setter.Value>
  9:                     <ControlTemplate TargetType="pickers:ConversionUnitPickerBase">
 10:                         <StackPanel>
 11:                             <Button x:Name="PickerButton" Content="This is a Test"
 12:                                 Background="{TemplateBinding Background}"
 13:                                 BorderThickness="{TemplateBinding BorderThickness}"
 14:                                 FontFamily="{TemplateBinding FontFamily}"
 15:                                 Foreground="{TemplateBinding Foreground}"
 16:                                 Height="72" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" />
 17:                         </StackPanel>
 18:                     </ControlTemplate>
 19:                 </Setter.Value>
 20:             </Setter>
 21:         </Style>

The key thing to look at with this code sample is the Setter.Value which effectively creates the template.  Unfortunately, to this point I have not figured how to apply this style to all controls automatically, as is done in the Toolkit source.  For the sake of time and sanity, I simply specified it manually in my XAML:

   1: <pickers:TemperatureUnitConversionPickerBase x:Name="sourceUnit"

   2:                               Style="{StaticResource ConversionUnitPicker}"

   3:                               TemperatureType="Celcius" />

It would appear, based on the samples, that the way to do this is through themes, but I simply have not figured it out yet.

Returning to our class which inherits from Control and is decorated with TemplatePart attribute, the only method that I have found the need to override is OnApplyTemplate which effectively gets the ball rolling, see below:

   1: public override void OnApplyTemplate()

   2: {

   3:     // this is a cleanup step

   4:     if (_mainButton != null)

   5:         _mainButton.Click -= new RoutedEventHandler(HandleUnitPickerLaunch);

   6:  

   7:     base.OnApplyTemplate();

   8:  

   9:     // get a reference to the button we are representing

  10:     _mainButton = GetTemplateChild(ButtonPartName) as ButtonBase;

  11:     if (_mainButton != null)

  12:     {

  13:         _mainButton.Click += new RoutedEventHandler(HandleUnitPickerLaunch);

  14:         _mainButton.Content = Value;

  15:     }

  16: }

This approach reminds me a lot of Android, where we are looking up controls based on names and then populating them through a find method.  Since this control is really a “super” button, we are only wiring up the click event.

From this point the implementation is your own.  In my case, I choose to make this class abstract so I could inherit it for my specific conversion types (Distance, Weight, Volume, and Temperature).  As part of the click handler for the button itself, I had it open my conversion picker, as shown below:

   1: private void OpenUnitPicker()

   2: {

   3:     _applicationContent = PhoneApplicationFrame.Content;

   4:  

   5:     PhoneApplicationFrame.Navigated += new NavigatedEventHandler(

   6:        PhoneApplicationFrame_Navigated);

   7:  

   8:     PhoneApplicationFrame.NavigationStopped += new NavigationStoppedEventHandler(

   9:        PhoneApplicationFrame_NavigationStoppedOrFailed);

  10:  

  11:     PhoneApplicationFrame.NavigationFailed += new NavigationFailedEventHandler(

  12:        PhoneApplicationFrame_NavigationStoppedOrFailed);

  13:  

  14:     _applicationFrame.Navigate(PickerPageUri);

  15: }

This code is effectively helping to do the switch, by saving the content of the control before the Navigate.  PickerPageUri is simply the Uri path within the Silverlight application to the XAML page which will allow the user to pick the unit they are using in the conversion.

We can pass values to this Uri using an interface and the Navigated event, an example is below:

   1: void PhoneApplicationFrame_Navigated(object se, NavigationEventArgs e)

   2: {

   3:     if (e.Content == _applicationContent)

   4:     {

   5:         // close the picker

   6:         CloseUnitPicker();

   7:     }

   8:  

   9:     if (_pickerPage == null)

  10:     {

  11:         _pickerPage = e.Content as IUnitPickerPage;

  12:         if (_pickerPage != null)

  13:         {

  14:             // assign default values to picker page

  15:             _pickerPage.ConversionType = ConversionType;

  16:             _pickerPage.SelectedUnit = GetConversionUnit();

  17:         }

  18:     }

  19: }

What is happening here is as we leave the “page”, we track a reference to the next page.  Through the casting to an interface we can access properties.  We use the same approach for reading the data coming back from the Picker page:

   1: private void CloseUnitPicker()

   2: {

   3:     if (_applicationFrame != null)

   4:     {

   5:         _applicationFrame.Navigated -= new NavigatedEventHandler(

   6:           PhoneApplicationFrame_Navigated);

   7:         _applicationFrame.NavigationFailed -= new NavigationFailedEventHandler(

   8:           PhoneApplicationFrame_NavigationStoppedOrFailed);

   9:         _applicationFrame.NavigationStopped -= new NavigationStoppedEventHandler(

  10:           PhoneApplicationFrame_NavigationStoppedOrFailed);

  11:  

  12:         _applicationFrame = null;

  13:         _applicationContent = null;

  14:     }

  15:  

  16:     // selected unit will be null if they hit cancel

  17:     if (_pickerPage != null && _pickerPage.SelectedUnit != null)

  18:     {

  19:         // get the value through the interface

  20:         _mainButton.Content = _pickerPage.SelectedUnit.LongDisplay;

  21:         SetUnit(_mainButton.Content.ToString());

  22:     }

  23:  

  24:     _pickerPage = null;

  25: }

The key lines here are 17 – 22, which take care of reading the data coming back, so long as the user didn’t cancel, which is handled by the null check.

Writing custom controls is a very powerful tool for your toolbox, I learned this writing Android as well.  The ability to customize is essential in mobile development.  Always consider the user, the usage, and the native paradigms of the platform when designing an interface.  This is a case where being unique may not be a good thing, mobile interfaces are highly specialized.

I plan to deploy this app after additional testing next week, the source code will be made available here at that time.

Day 12: The Trip Home

You knew it had to happen.  Everything went so well throughout the entire trip you knew bad things were bound to happen eventually.  Day 12 could also be titled to the VERY long day from hell.

The day started normal enough. As per my plan I awoke at 9am and headed downstairs to the breakfast buffet at the Marriott.  I knew I would be traveling for almost an entire day (though I had no idea how stressful it would be) so a big breakfast was essential.  Tip: I tried numerous times to eat scrambled eggs in Japan but they are simply too runny, I wouldn’t order them if you don’t like them that way.

P8140001_resize

Following this, I returned to my room to relax until 11am which is the designated time I had picked to head out (the hotel checkout is 12pm).  My room looked pretty awful I must say, though I didn’t have housekeeping the day before, so I expected as much Smile

P8140002_resize

At 11:00am it began.  After showering, dressing, and triple checking that EVERYTHING was packed I began my journey leaving the comforts of Marriott and hopefully ending at my parents house in Ann Arbor, MI.

P8140003_resize

I had decided to take the same route back to Narita as I took to get to Tokyo, so I took the Hibiya subway line to Ueno where I purchased a ticket for the 1pm Skyliner (I arrived around 12:15pm, so I did have to wait).

P8140004_resize

The train arrived perfectly on time and I was on my way in great comfort.  I even got to see the new Tokyo Sky Tree Tower, which is the new central hub for telecommunications, replacing Tokyo Tower.  The thing is huge, but not yet open.  It will open next year, and I will probably visit Japan again just to see it Smile

P8140008_resize

P8140007_resize

This is where things began to go wrong.  There are three terminals at Narita. (One for Domestic, Two for International).  I was 95% sure which terminal to go to, but I decided to have Mami check to be absolutely sure.  She did confirm I was right but also informed me that my Air Canada flight was delayed 3 hours!!!  Given I only had a 1hr 30m layover in Toronto, this was an immense problem.

So I arrived at Narita and headed to the Air Canada desk.  The thing I don’t like about Narita are the crazy hours the reps work.  They don’t open their stations until about 2hrs before their first flight, so I had to wait 2hrs (first flight was at 5pm to Vancouver and I arrived at about 1pm).

The reps were aware of the problem with my flight, that was good, but were not sure what to do since they didn’t have any space on other flights through Star Alliance out of Japan.  Finally, they found space on an American Airlines flight to Chicago.  I had no idea they could simply issue a ticket for a completely different airline, but they did, as well as a ticket from Chicago to Detroit.

Unfortunately, as I had arrived at Narita International Terminal 1 and American flew out of International Terminal 2.  So I had to carry all of my bags to a different terminal and HOPE that American and United accepted my ticket from Air Canada.

P8150013_resize

Thankfully, American accepted the ticket request from Air Canada, but told me I would have to wait till I got to Chicago to see if United would accept my ticket request and let me travel to Detroit.  So I left the ticket counter with only one boarding pass and stressed about whether I would end up stuck in Chicago; a lot can happen during a 11hr flight.

Thankfully none of that came to pass.  It was interesting in Chicago, going through Customs (they must have had 4 planes land and about a 100 US citizens returning) and dropping my checked luggage off at Terminal 5, taking a train to Terminal 1 and getting a boarding pass for the plane my luggage was heading for.  So just to be clear, I essentially put my luggage on the plane BEFORE I was sure I was going to be on the plane…

Anyway, I reached the gate at O’Hare and waited for the flight to Detroit.

P8150016_resize

Interestingly, this plane took off only 15m after my flight from Tokyo took off (630 to head for Detroit, 615 to head for Chicago); always love how going back across the date line screws with your head.  The flight to Detroit was awful. Not because of United but because I had easily the loudest crying kid I have EVER dealt with on a plane before, and he was sitting right in front of me.  New Rule: If I ever had kids they will not go ANYWHERE near a plane until AT LEAST the age of 5.

But I arrived in Detroit at 830pm, I didn’t even try to calculate how long I had been traveling.  Of course, Detroit was a slow as ever with getting the luggage out, and it wasn’t helped by the fact that since I put my luggage on the plane BEFORE I had a ticket I wondered if it would even be there.

But I eventually did get it and Mom even took a picture of me at the end of my LONG journey back home (still not complete until I drive to Grand Rapids).

P8150018_resize

One thing this journey has taught me is, I will NEVER EVER fly Air Canada again.  Just totally awful and incompetent.  I have to wonder why they delayed the second flight, hopefully not because of the media player issue that delayed my flight to Tokyo.

But I shouldn’t focus on the negatives.  This was a great trip overflowing with memories and the people I met.  I love Japan and I love the Japanese.  The trip had its ups and downs and I am glad to be home but, make no mistake, I will return to Japan in the future Smile

Day 11: Sad Goodbyes and Final Sightseeing

What a trip it has been!!! Hiroshima, Kyoto, Fuji, Tokyo, Osaka, and everything in between, I could not have asked for more.  Alas, all good things must come to an end.  I let the girls sleep in as late as possible this morning before surrendering their room to Marriott.

We were all happy, still sore from the Fuji climb (except Mami cause she is just weird), and sad to be parting ways.  Mari was going to stay a night with Mami in Fussa while I completed one final day of sightseeing.  I went with them to Tokyo station where we said our final goodbyes.

IMG_1050_resize

Truly sad, I am not sure when I will see them again, Mami and I possibly might hang out on vacation in Hawaii next year, but that isnt confirmed yet at all.  Mari MIGHT be traveling to the US and thus has promised to visit me if she is nearby, so well see.  One thing is for sure, I wont be going back to Japan for pleasure for a few years.

Once I parted ways with the girls, I headed for the mecha of geekdom: Akihabara or Akiba.  This center of all things anime is a place I always make a point to visit when I am in Japan.  Where else could a place like ‘Gundam Café’ actually survive more then a week Smile

IMG_1051_resize

And, it being the center of geekdom, a Yodobashi Camera is required

IMG_1056_resize

I was able to purchase a new digital photo frame to hold the pictures from this trip.  I always make it a point to make one large electronics purchase when I am in Japan, since its cheaper then the States in most cases, since a lot of the electronics are literally made in Japan, not China.

Afterwards, I did some walking around Electric Town and got some pics of the various shops in the area include a Maid Café.  For the unaware, a recent trend in Japanese dining is a small café that features pretty Japanese girls dressed in maid outfits serving you drinks and beverages.  I guess this is to live out some mental fantasy guys have, but this is Japan, weirdness is part of the culture Smile

IMG_1060_resizeIMG_1063_resizeIMG_1064_resize

As I journeyed to the Tokyo Metro station for the Hibiya line I accidentally found something really cool: an Anime figure display.  Anime, being so popular in Japan, has a deep cult following and people will be meticulous in models of various mechs or characters from these animes.  My favorite figurine was of the VF-25 ‘Messiah’ Custom, flown by Alto Saotome in the Macross Frontier series.

IMG_1068_resize

I love Macross, it has some of the best combat scenes in any anime EVER!!! And the whole three stage transformation of the VF fighters is just sick.  See Macross Zero if you don’t believe me.

So after I geeked out in Akiba, I headed back to the hotel to plan for the final stage of sightseeing.  Many tourists fall into the trap of visiting the Tokyo Tower to get a view of Tokyo from high up.  But there is another place, and its free, that you can get a good view of Tokyo: The Tokyo Metropolitan Government building in Shinjuku.  45 stories high, the top floor is available to tourists and provides breathtaking views.

IMG_1122_resizeIMG_1135_resize

Unfortunately, there is one draw back to taking pictures here.  In the day time its not a problem, but they don’t keep the interior dark so taking pictures at night is very difficult cause of the light inside the tower vs the outside.  I don’t know if Tokyo Tower is better for night, I have never tried.  I do know that next year, the point will be moot with the completion of the Tokyo Sky Tree.  It’s a new tower, that is replacing Tokyo Tower as the main transmission line for city communications.  The reason: the buildings in the city haven’t gotten so tall they affect the signals and communications ability within the city.

So, I learned last year about Odaiba.  This is the portion of Tokyo that is comprised of recovered land, or land that was created in Tokyo bay to meet the ever expanding real estate needs of Tokyo.  This year I learned that not only is the place cool as hell still, but Tokyo has made it easy for us guys to know where to take a girl on a date.  This place is absolutely perfect and seems designed for it, honestly, I felt out of place being by myself surrounded by couples.  But how can I complain, I just spent the last week hanging out with two pretty Japanese girls in Mami and Mari Smile

But, the biggest reason I go to Odaiba is the scenery, it gives you some perfectly unobstructed views from Tokyo Bay of the coast around Minato-ku and the Rainbow Bridge.  It also shows off United States of Odaiba, which is a running joke among the Japanese.  I still say unless you make the place totally bankrupt and full of itself, it doesn’t represent the US well enough.

IMG_1160_resizeIMG_1173_resize

This concluded the sightseeing that I wanted to accomplish and so I returned to the hotel ready to head back to America and return to life.  I feel kind of sad to be leaving, I really love it here, but I suppose you are not supposed to want to leave a vacation, if it’s a good one, and this one was beyond good.

I don’t know when I will return to Tokyo, but I know that I will in the future.  There is just too much I love about the city to never come back.  I really want to see Mami and Mari again and the view from the Sky Tree is going to be awesome.  Also, while my camera was more then adequate for my picture taking needs, it is clear that I need more lenses, more equipment, and a better understanding if I want to capture the pictures I want to capture.  These are all good, but they just don’t capture what the eye see’s; perhaps that impossible but I want to get as close as I can.

Day 8: Return to Tokyo

Today was… interesting.  So originally the plan had been to leave with Mari from Shin-Osaka and head to Tokyo for the final leg of the trip.  This all changed due to a misunderstanding that was had between her and I. It is always a risk when dealing with females in general that you will say something that gets taken the wrong way, and this risk is amplified when dealing with a culture as different as Japan’s. The end result was, I ended up making this trip alone.

P8100006_resize

Three hours on the Shinkansen for this portion.  I don’t know why but, I actually didn’t feel that well while traveling.  I have various thoughts on why, but with enough water and food I started to feel better.  This was one of those times that McDonald’s was essential.  I find that while I do enjoy most Japanese foods, I am hesitant to eat at a Japanese restaurant unless I have someone to help me.  In addition, the customs in a restaurant here are very different from America.  For example, the waiter and waitresses NEVER come to your table and ask things.  Once they seat you and you decide you grab any person walking by and give them your order.  For me, its very difficult, cause I am so used to the opposite.

The accommodations for the day before Fuji were at The Prince Sakura Tower Tokyo hotel.  The Prince is a very famous high end hotel throughout Japan so, needless to say, I was looking forward to this stay.  It did not disappoint.  Easily the best hotel of the four I stayed at, it even had a Jacuzzi in the bathroom.  Well appointed and nicely located near Shinagawa station.

P8110018_resizeP8110019_resizeP8110020_resize

Once I was settled I began the first phase of preparation for the Fuji climb: gear switch.  When I arrived in Tokyo on August 4th, I stayed at the Marriott in Ginza.  Due to the size of the my luggage I decided to leave the big bag there when I traveled to Hiroshima.  This has proven to be a great move as I really cannot see how I would have survived otherwise.  Now, I returned to Ginza to switch out my sightseeing gear for my climbing gear.  I love this hotel, they people there are so nice and very accommodating.  And, the English is top notch, so if I do have to say something, I don’t need to struggle with Japanese to say it.

P8110008_resize

After I returned to Shinagawa, I completed my preparation and decided to grab dinner.  I decided on Hooters.  Let me be clear, I am not a huge fan of Hooters, I don’t care for the whole atmosphere, never really thrilled me that much.  However, that is why I wanted to visit it in Japan.  You see, my understanding of Japanese culture, and it was confirmed when I read about Hooters opening here, is the girls don’t really like to wear the sort of clothing that Hooters is famous for.  I was interested, also, to see how the Japanese culture affected Hooters.  Every time I have been to Hooters, I usually get my waitress flirting with me.  I am not stupid, I know she is playing for the tip.  However, in Japan, you don’t tip, so I was wondering how this would affect the atmosphere.

P8110017_resize

The answer is, the girls don’t bother you, so it really becomes like every other restaurant.  Frankly, the service wasn’t that good here; you can tell they are still working out the kinks a bit.

I can honestly feel myself getting sick of Japan, which is probably a bit of a good thing, means I am less tempted to come back next year.  Its an interesting feeling when I ride the train through Tokyo.  A part of me wants to stay forever cause its such an experience, but the other part of me cant stand being here.  For as common as crowds are in Japan, the Japanese people sure do walk around with their heads down a lot.  They don’t seem to even obey the rules that divide the spaces to allow the crowds to use the space more efficiently. I cant tell you how many times they cut each other off, its really amazing.  You wont meet nicer people when you start talking to the but, I swear it might be the most anti-social society on the planet, rude at times.

Anyway, I must lie down and rest.  Big day tomorrow as we climb Fuji.  Days 9 and 10 will be a combined entry Saturday night, hoping for lots of pictures.

Day 5: A Day with the Host Family in Hikone

Today was a very special day, it wasn’t really intended when I planned the trip, I only learned after I had made the arrangements that it would be Asahi’s 8th birthday on this day, knowing that made the trip even more exciting.

IMG_0629_resize

The day started very early, as I had to get to Hikone (my second hometown) by 930am.  Thankfully I have my JR Rail Pass and so have full use of the Shinkansen (bullet train) without having to pay, otherwise this is a very painful trip from Osaka to Hikone.

I arrived in Maibara, which is one station down from Hikone and the nearest Shinkansen stop, and took the Biwako (Lake Biwa) line to Hikone; this is a route I am very familiar with having taken it frequently last year and when I was a student here.  Hikone is the sister city to Ann Arbor and is a very beautiful and historic city.

IMG_0620_resize

Our first stop for this day was, per my request, Hikone-jo (Hikone Castle).  The reason I love going here is, first getting to the castle is a great physical challenge, but more importantly the view from the top is spectacular and gives you so many beautiful shots of Lake Biwa.

IMG_0693_resize

IMG_0647_resizeIMG_0654_resizeIMG_0678_resize

After this, we went and grabbed some ice cream and I got a chance to talk with Serika (17) and Yurina (14).  I let Asahi play with my Windows Phone and he quickly found Angry Birds and started playing, but, like most 8yr olds, he got board and so jumped on my lap and started watching a cartoon (not Anime), Serika captured this scene

IMG_0717_resize

Next it was back to the house with Emiko-okasama.  I got to meet a new friend, Kiki the cat, who is easily the smallest grown cat that I have ever seen, but so adorable.

IMG_0725_resize

So it then became time to pick up Toma from soccer practice.  I decided to accompany Emiko-okasama because Toma and me always get along really well, in fact I am told he kind of views me as his oni-san (older brother).  I was amazed watching his practice because it was around 95 degrees outside and in Japan, except for certain areas or stadiums, they don’t practice on grass, they practice on dirt.  Talk about a dust mess, it was nuts.  I couldn’t believe it, but Emiko explained to me that they don’t want to waste the water it would take to water the field.  This is another culture difference between the US and Japan.

Upon returning to the house with Toma I was asked my Emiko-okasama if I would I like to meet, and help, her English students.  Of course I said “yes”, I love helping people in Japan speak English, in fact Emiko has her kids practice with me all the time, and they help me with my Japanese. So, I don’t remember their names, but we had a good time talking about America and my travels through Japan and stuff that they like, good times.

IMG_0730_resize

Probably the most hilarious part, for me, was when they arrived at class and they saw a “giant” American waiting for them in the classroom.  I always love that look of shock on their faces, especially if they have siblings in the car, it amuses me Smile

So it being Asahi’s birthday we had to have a party and with some downtime before Emiko’s next class, we had a small party for Asahi.  The cake was very delicious.  I first became introduced to cheese cake in Japan, ironically, and have loved it ever since.

IMG_0734

Now, fun fact, Akihiro-sans (father) birthday is the day after Asahi, so we were celebrating both here.  After this, we went off to Emiko’s second English class and got to meet those students, but before we did, I said my final Goodbye’s to everyone.

IMG_0743_resizeIMG_0745_resize

I absolutely love them all, and it is so much fun being around them.  I do hope I get a chance to come back in the future.  I just am not sure when I will, this is the second year in a row I have come to Japan for vacation and I love it, its just so much fun being around them.  And I love the fact that I can look at these kids and know that I have gotten a chance, to see them grow.  I remember when Toma was much shorter.  I can see them all growing up and it really is neat.

What a treat the day was, I went back to Osaka that night and collapsed from exhaustion, truly a full and happy day.

Creating a Button Bar in Android

In application development we often talk about the importance of consistency, in particular with respect to interfaces.  In mobile application this is even more important.

In Android we often see a standard button bar as shown below.image

This is a common technique that can provide a very flexible consistent layout.  The core XML for your layout file is shown below.image

This is one of the advantages of the RelativeLayout, the ability the precisely control where elements appear.  In this case, we use a LinearLayout to stack the RelativeLayout beneath another LinearLayout.  The RelativeLayout is then configured to anchor itself to the base of the screen.

I am currently using this technique in an application to provide a consistent look for your application.  It gives you flexibility as well.  You can use the RelativeLayout to add additional buttons to the bar.  You could even apply additional layout controls to the RelativeLayout to add more buttons.

IsolatedStorage ORM Prototype

I have no doubt someone else has already created something similar to this, but I wanted to take my crack at it for the application I am writing.

The idea was I wanted to create something that would manage my entities and save/read them from IsolatedStorage for a Windows Phone 7 app.  I understand that using reflection on a mobile device is something you really do try to avoid; to that end, I have reduced the amount to the bare essential.

The following code snippet shows a sample use case:

   1: var repository = new IsolatedStorageRepository();

   2: private void Button_Click(object sender, RoutedEventArgs e)

   3: {

   4:     repository.SaveChanges();

   5: }

   6:  

   7: private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)

   8: {

   9:     StorageItem item = repository.StorageItems.FirstOrDefault();

  10:  

  11:     if (item != null)

  12:     {

  13:         txtTwitter.Text = item.TwitterHandle;

  14:         txtAccessCode.Text = item.AccessCode;

  15:     }

  16: }

Most repository patterns tend to be based off some sort of code generation.  Not in this case, since the aim is to abstract away the IsolatedFileStorage, which is nothing more then file access.  Because of this, we allow the user to define their own repository derivation (IsolatedStorageRepository in this case).  Below is the code that is required for this repository:

   1: public class IsolatedStorageRepository : RepositoryBase

   2: {

   3:     public IRepositoryCollection StorageItems

   4:     {

   5:         get;

   6:         set;

   7:     }

   8:  

   9:     public IsolatedStorageRepository()

  10:     {

  11:         StorageItems = RepositoryCollectionFactory.

  12:             GetRepositoryCollectionInstance("sampleData");

  13:     }

  14: }

The idea is for developers to define what the repository will look like and where the data is for each “collection”.  The RepositoryBase contains a method SaveChanges which uses reflection to find the dirty collections in the repository and call their, internal, SaveChanges method.

All data is stored in a delimited fashion within the isolated storage file.  The delimiter used is “**,**”, which attempts to be unique enough to where conflicts should not arise.  Using this technique gives the lines a column look and feel.  To map these columns we use the custom attribute: DataColumnAttribute.  This attributes chief responsibility is to store the index the property maps to.  An example entity is shown below:

   1: public class StorageItem : EntityBase

   2: {

   3:     [DataColumn(0)]

   4:     public string TwitterHandle { get; set; }

   5:  

   6:     [DataColumn(1)]

   7:     public string AccessCode { get; set; }

   8: }

This essentially says the value in position 0 is the TwitterHandle, at position 1 it is the AccessCode.  The assignment is done through reflection and is one of the areas that I may make a change to remove reflection for performance gains.  However, the idea is that IsolatedStorage will not contain that much data, so speed is not always going to be an issue.

The biggest issue with this code at the moment is how to handle the loading of the existing file.  For the moment, when the collection is instantiated the data file behind it is loaded in if it is determined it hasn’t been loaded already.  This means, this will happen whenever you instantiate a new instance.

This also has the potential to run into threading issues, but this code is still in its very early stages.  Also, one has to remember the user is not really going to query the file like they would a database table, or even an XML file.  The structure set within the IsolatedStorageFile is loose, at best.

Goals moving forward are the continued removal of unnecessary reflective code as well as the establishing consistent idea for data access including how to load the data ahead of time in a timely fashion.

The current code and the accompanying example project are downloadable below.

http://cid-630ed6f198ebc3a4.office.live.com/embedicon.aspx/Source/RelationalIsolatedStorage.zip

CodeMash 2011 in Closing

I really love CodeMash.  It is by far one of the most fun and interesting events that I get the opportunity to attend during the year; and it happens on my birthday, so its doubly fun.

My main focus this year was Mobility as I am really diving head first into the mobile application development.  I got the chance to meet a lot of amazing people and attend some amazing sessions on HTML5 for offline mobile app development and my first real look at MonoDroid.

I am making a promise to myself this year that I will learn about HTML5, it really appears that it will be an essential skills, regardless of what area of programming you are using.  I got a chance to see it in action with JQuery touch, and thus another chance to kick myself for not making enough time to fully explore JQTouch.

MonoDroid shows great promise, though still not mature enough where I would consider it a viable alternative to Java programming.  The biggest problem for them, that I see, at the moment is the necessity to include the Mono framework with the apps. I don’t agree with this approach and I have every confidence that they will change this.  If you look at MonoTouch, Mono is not required for those apps, thus the same should be true for Droid apps.

That said, it amazes me how much cleaner .NET code looks compared to most other languages.  Looking at an Android app being developed with Mono compared to Java is like comparing a Picasso to an amateur painting.  It just feels so much nicer with the bevy of language features provided by .NET.

Outside of the sessions, I got meet a lot of consulting companies and other vendors whose products I use; really nice to put a face with those products and see what’s in store for the future, ReSharper 6 looks amazing.

However, the conference would not have been complete without the standard competition for prizes where developers choose to make fools of themselves for prizes.  This year Quick Solutions brought a Kinect to the conference and challenged developers to go for high scores and win cash prizes.  In the end, despite having the highest qualifying score, I came in second when we played the hardest Reflex Ridge level from Kinect Adventures.  But honestly, I was fine with it, really loved the guys I met and the other competitors were just awesome.

SmashDroid also did very well, though not as well as I had hoped.  Some small problems with the app, but overall it was stable and did its job.  But the clock timer didn’t always work.  In the end, it really validated that I need to take a new approach to notifying users with more a service based loop approach.

But CodeMash is CodeMash and it just gets better every year.  Great sessions, great people, great times.  Awesome.  Thanks to everyone for attending and I will see you all next year.

Introducing SmashDroid for Android

One of the aspects of CodeMash that I have come to really enjoy is their dedication to providing REST feeds to allow developers to write applications to work with the data for Sessions and Speakers.

Last year was the first year of this and many people entered, including myself.  Being new to Android development at the time and with not much time to work with my commitment to the client in New York, my application was not where I wanted it to be.  This year, I resolved to start earlier and dedicate more time.  Now with the conference two weeks away, I present: SmashDroid.

image

SmashDroid is an Android app that is able to run on devices supporting Android 1.6 (Donut) and above. It features numerous features, the coolest, I think, of which is the ability to have the device notify the owner when a particular session starts, as well as allowing them to view a schedule.

SmashDroid allows the same drill down functionality commonly found in other CodeMash apps.  It loads the Session and Speaker data each time it starts so as to have the most absolute data possible; this is important because CodeMash does often change its information.

Once you reach the level of a Session, you can choose to have your phone notify you when it starts.  Only one session can be set for notification at a given timeslot, the app will warn you before you overwrite an existing notification.

For the purposes of demonstration: http://www.screencast.com/t/lDKs4k79uErK

Please let me know of any comments. And to answer the question, its not a Windows Phone 7 app cause I don’t own a Windows Phone 7 Smile

Customizing Tabs in Android

In my previous post I demonstrated using the “out of the box” tabs in an Android application.  While this is useful for a basic layout, you quickly come to the conclusion that, frankly, the tabs are hideous and that you need to exert some level of customization over them.

The short answer is that Android does support a deep level of control over the look and feel of the tabs, however, as part of the long answer, this ability has only existed since API Level 4 (Android 1.6).  While this means that your application cannot target Android 1.5 and below.  This is an acceptable compromise, recent studies have shown that the dominant Android OS version is 2.x.

Our base code will the be the same as the end result from the previous post, I will demonstrate how to take this code and use XML to define what the tab looks like.  Here is our end code, for reference.

   1: public class TabTestActivity extends TabActivity {

   2:     /** Called when the activity is first created. */

   3:     @Override

   4:     public void onCreate(Bundle savedInstanceState) {

   5:         super.onCreate(savedInstanceState);

   6:         setContentView(R.layout.main);

   7:         

   8:         TabHost.TabSpec tab1 = getTabHost().newTabSpec("Hockey")

   9:             .setIndicator("Hockey Scores")

  10:             .setContent(new Intent().setClass(this,

  11:                 HockeyScoresActivity.class));

  12:         

  13:         TabHost.TabSpec tab2 = getTabHost().newTabSpec("Football")

  14:             .setIndicator("Football Scores")

  15:             .setContent(new Intent().setClass(this,

  16:                 FootballScoresActivity.class));

  17:         

  18:         TabHost.TabSpec tab3 = getTabHost().newTabSpec("Golf")

  19:             .setIndicator("Golf Scores")

  20:             .setContent(new Intent().setClass(this,

  21:                 GolfScoresActivity.class));

  22:         

  23:         getTabHost().addTab(tab1);

  24:         getTabHost().addTab(tab2);

  25:         getTabHost().addTab(tab3);

  26:     }

  27: }

Running this code yields the following output:

image

Note: I did change the background color from the previous example

Very unsightly. Lets restructure the the tabs to make them look nicer.  How about this:

image

Looks a lot nicer right? So how did we do this, well I first I added a couple things to our project.  The first was a new XML file to house the layout for each tab, the XML is shown below:

image

As with any layout XML in Android, you can do whatever you like with this, for this example, I am keeping things simple.  We have a simple TextView within a standard LinearLayout view group.

The second thing was an image called “tab_divider.png” (available in the source).  This is used to separate the tabs in the view.

Here is our updated code for the Activity:

   1: public class TabTestActivity extends TabActivity {

   2:     /** Called when the activity is first created. */

   3:     @Override

   4:     public void onCreate(Bundle savedInstanceState) {

   5:         super.onCreate(savedInstanceState);

   6:         setContentView(R.layout.main);

   7:         

   8:         getTabHost().getTabWidget().setDividerDrawable(

   9:             R.drawable.tab_divider);

  10:         

  11:         View content = LayoutInflater.from(this).inflate(

  12:             R.layout.tab_content, null);

  13:         ((TextView)content.findViewById(R.id.lblText))

  14:             .setText("Hockey Scores");

  15:         TabHost.TabSpec tab1 = getTabHost().newTabSpec("Hockey")

  16:             .setIndicator(content)

  17:             .setContent(new Intent().setClass(this,

  18:                 HockeyScoresActivity.class));

  19:         

  20:         content = LayoutInflater.from(this).inflate(

  21:             R.layout.tab_content, null);

  22:         ((TextView)content.findViewById(R.id.lblText)).

  23:             setText("Football Scores");

  24:         TabHost.TabSpec tab2 = getTabHost().newTabSpec("Football")

  25:             .setIndicator(content)

  26:             .setContent(new Intent().setClass(this,

  27:                 FootballScoresActivity.class));

  28:         

  29:         content = LayoutInflater.from(this).inflate(

  30:             R.layout.tab_content, null);

  31:         ((TextView)content.findViewById(R.id.lblText)).

  32:             setText("Golf Scores");

  33:         TabHost.TabSpec tab3 = getTabHost().newTabSpec("Golf")

  34:             .setIndicator(content)

  35:             .setContent(new Intent().setClass(this,

  36:                 GolfScoresActivity.class));

  37:         

  38:         getTabHost().addTab(tab1);

  39:         getTabHost().addTab(tab2);

  40:         getTabHost().addTab(tab3);

  41:     }

  42: }

The first thing I will point is the setting of the divider.  This step is NOT required, however it really adds to the look of the tabs and helps the user separate them, you mileage may very.  This MUST be called at this point, if you do not call it first you will not see your divider.

Ok the important stuff here is the inflated view we are using (R.layout.tab_content) defines the layout of each of the tabs.  You could use “n” views here if you wanted, I simply chose to use the same view for each and update the TextView using findViewById method.  It is required that you re-inflate the view each time you wish to use it. Not doing so will cause you to get errors concerning the parent view.

Once you inflate the view and set the various properties as desired, you call the setIndicator(View view) overload.  This overload is only available in API Level 4 and above (that is Android 1.6).

As you can see, taking this step is very easy and straightforward and allows for an impressive amount of customization.

At this point, you have now completed the process of customizing the look of the tabs.  However, if you run the application you may notice that as you switch between tabs, there is no indicator of which tab is selected.  That will be the subject of the next entry, I will only say at this point that it involves: State-Lists.

http://cid-630ed6f198ebc3a4.office.live.com/embedicon.aspx/Public/TabTest.zip