Wednesday, June 22, 2011

How to change the text color of Android options menu

As promised in the previous post, here comes the second installment of styling the options menu.  In the previous post we saw how we could change the background color of the options menu.  This time we are going to look at how we could change the text color of Android options menu.

We already know that styling the options menu is not really easy.  As shown in the previous post, its a eight step process.  To change the text color of the options menu we need to put in another hack.  We need to add a ninth step.  Lets look at all nine steps once again.
  1. Setting the custom LayoutInflater.Factory instance.
  2. Implementing the onCreateView method that will actually set the background selector for the options menu.
  3. In the implementation of onCreateView, check if the view that is to be created is of type com.android.internal.view.menu.IconMenuItemView.  Yes, I know, its an *internal* class!
  4. If the view is of type IconMenuItemView then create the view using the LayoutInflater.
  5. At this point we have an instance of IconMenuItemView but we cannot change the background color of the view directly.  Even if we change the background color at this point, framework updates it again and our changes are overwritten
  6. We have to change the background color after the view has rendered, using the Handler API.
  7. Change the background color in run method of the Runnable instance that we pass to the Handler's post method.
  8. Change the text color using reflection.  This is the new step.  We need to change the text color using reflection.
  9. Catch all exceptions that could occur in the entire process.  Notice that, we are using an internal class there are high changes that its behavior could change without any notice.  In fact this approach does not work on Android 2.3.  In this case we have to fallback to the default menu styling.
Lets look at code to make things clear

The menu_selector.xml and colors.xml have been kept unchanged from the previous post.  The comments are embedded in the code.  One new methods has been added setTextColor.  This method does the job for us!
The above screenshot shows the menu item color changes when its pressed.

That's it!  We have successfully changed the background color and text color of Android options menu.

Saturday, June 18, 2011

How to change the background color of Android options menu

I have been trying my hand at developing apps with Android.  I have had my fair share of leanings.  Today I am going to share my experience around styling the options menu in Android.

What is the options menu?

Options menu is the menu that is shown to the user when the Menu key is pressed on the Android phone.

Lets look at an example, that will make things easier to understand.

The layout file.

The above code shows the following GUI
As you can see from the above screenshot the background color of the Activity is greenish while the default Color of the selected element of the options menu is Orange.

It definitely looks inconsistent and ugly.  This is the reason we need to style the options menu to match the rest of the application theme.

Android platform is highly configurable.  One can change all aspects of look and feel on the android platform.  Hence, when this task was given to me, I thought it would be a 2 min fix.  But unfortunately it took much longer than I expected.

All I wanted to do is change the background selector of the options menu.  Android does not provide a straight forward way to do this.  In fact, it meant writing quiet some code.  I decided to write a post on this, so that other can benefit from my learning's.

So back to the original question.  How do we change the background color of Android options menu?

How do they do it?

Now there is nothing fancy about the above code.  But to change the background color we have to write some fancy code :)

Changing the options menu background color is a 8 step process
  1. Setting the custom LayoutInflater.Factory instance.
  2. Implementing the onCreateView method that will actually set the background selector for the options menu.
  3. In the implementation of onCreateView, check if the view that is to be created is of type com.android.internal.view.menu.IconMenuItemView.  Yes, I know, its an *internal* class!
  4. If the view is of type IconMenuItemView then create the view using the LayoutInflater.
  5. At this point we have an instance of IconMenuItemView but we cannot change the background color of the view directly.  Even if we change the background color at this point, framework updates it again and our changes are overwritten
  6. We have to change the background color after the view has rendered, using the Handler API.
  7. Change the background color in run method of the Runnable instance that we pass to the Handler's post method.
  8. Catch all exceptions that could occur in the entire process.  Notice that, we are using an internal class there are high changes that its behavior could change without any notice.  In fact this approach does not work on Android 2.3.  In this case we have to fallback to the default menu styling.
Yep, I know, it looks like a lot of work and pretty complex too.  Lets look at the code to make things more clear.

The menu_selector is an XML file that choses the background color based on the state of the menu item.  My menu_selector.xml looks like this

The colors.xml file looks like

The comments are embedded in the code. One new methods has been added called setMenuBackground. This method does the job for us!

The screenshots of the styled options menu
 As we can see the Options Menu background color has been changed from Orange to greenish.

That's all folks!

We have successfully changed the background color of Android options men.  But wait a minute, how to change the default text color of the Android options menu?  If this question is bothering you stay tuned, in the next post I am going to explain exactly that!

Saturday, June 11, 2011

Visual Studio 2010 Fixing "The referenced project XX does not exist" Error

Recently one of my friend was facing a weird issue while building our application. The application was developed in C#. We were using MSBuild as the build tool.

What was the issue?

The issue was simple, she was not able to compile the application.

The issue was special in many ways because, all of us (i.e. her team members) were able to compile the project just fine. Even the continuous integration service - Jenkins (formally Hudson), was able to compile the application. We were not able to compile the application only on her machine. The build failed with the error

The project "AB.dosomeprojects.Services.FillImporter.Service.csproj" referred the project "abcdefgh.dosomeprojects.Common.csproj" which was in the path "..\..\..\abcdefgh.dosomeprojects\abcdefgh.dosomeprojects.Common". The path was relative to the Service project directory.

The directory structure was correct, Common project was correctly reffered from the Service project, but still we were not able to build the app on her machine.

Initially, we thought that there must me some local changes on her machine because of which, she was not able to run the build on her machine.  Build was not passing neither with Visual Studio 2010 nor with MSBuild and that too only on her machine.

We looked for any local changes on her machine, but unfortunately her machine was clean.  There were no local changes.  We were simply clueless!  Why does the project build fine on other developers machine while it does not build on her machine?

After some intense Googling, we found an interesting hidden treasure shipped silently by Microsoft Visual Studio 2010.  I call such issues as hidden treasure because, I can hardly believe my ears when I hear/read about such an issue.

I am sure it will come as an surprise to you as well.  The issue is:

The Real Problem:

Visual Studio 2010 fails to build the project when the following conditions are met:
  1. Solution with multiple projects exists, where there exists dependency relationships among them
  2. The sum of the following two path length is exactly added up to 259 characters (= MAX_PATH – 1) 
    1. The path of a referencing project’s directory.
    2. The relative path to a referenced project from the current directory (= a referencing project’s directory).
Hmm, cryptic information lets decrypt it.

Condition 1: We have solution with multiple projects where there exists dependency relationships among them

Yes, we do have the solution with multiple projects in it and one project depends on the other.  In our case the AB.dosomeprojects.Services.FillImporter.Service.csproj depends on the abcdefgh.dosomeprojects.Common.csproj project.  Hence, condition 1 is met.

Condition 2: The sum of the following two path length is exactly added up to 259 characters (= MAX_PATH – 1)

This one is tricky.  Which path sum are we talking about?

Condition 2.1: The path of a referencing project’s directory.

The path of the referencing project's directory, in our case the path of the AB.dosomeprojects.Services.FillImporter.Service.csproj on the file system, which was, "E:\projects\ABC\SVN\dosomeprojectsweb\trunk\AB.dosomeprojects.Services\AB.dosomeprojects.Services.FillImporter\AB.dosomeprojects.Services.FillImporter.Service" has total length of 158 chars.

Condition 2.2: The relative path to a referenced project from the current directory (= a referencing project’s directory).

The relative path of the referenced project from the current directory, in our case its the relative path of abcdefgh.dosomeprojects.Common.csproj, which is, "..\..\..\abcdefgh.dosomeprojects\abcdefgh.dosomeprojects.Common\abcdefgh.dosomeprojects.Common.csproj" has total length of 101 chars.

The sum total of these two path lengths: 158 + 101 = 259!


Wow! condition 2 is also met.

As I said, we have uncovered an hidden treasure of Visual Studio 2010.  Its unable to build the project when the path length of referencing project + path length of relative path to a referenced project = 259!

If the sum total is one less or one greater than 259 it all works fine! On other developers machine the total path length was less than or greater than 259 but was never equal to 259 and because of this reason we were all able to build the project but she was not able to build the project on her machine! Weird isn't?

This issue occurs due to a bug in the Path.GetFullPath in .NET Framework library.  This is a known issue in Visual Studio 2010.

The Fix:

The workaround is pretty straight forward, rename one of the folders a bit longer or a bit shorter and make sure that, the sum of the two path length is NOT equal to 259 chars.

For more information on the issue visit the offical Microsoft Knowledge base article here http://support.microsoft.com/kb/2516078

Now you know, we are having so much fun with .Net (sarcasm intended)!

Saturday, May 28, 2011

How to build the web project on the CI server without Visual Studio 2010

On one of the ASP.Net project, we were using Visual Studio 2010 IDE and MSBuild, to compile and build the application.  Pretty standard setup for a web project right?.  On developer machine all worked very smoothly, since each developer had Visual Studio 2010 installed locally on their machines.

We setup a continuous integration (CI) server Jenkins (formally Hudson) for this project.  Naturally, we didn't wanted to install Visual Studio 2010 on the CI server.  But we still wanted to build our app on the CI server.  Hence, we installed Microsoft Windows SDK 7.1 and all its components on the CI server.  After installing the SDK, we were able to build all projects except the web project on the CI server.

The error that we were getting was

Obviously, since the Visual Studio 2010 was not installed on the CI server, the path mentioned in the error didn't exist and that's why the web project was not able to build.

I opened the csproj file in Notepad++, to find the reference of "Microsoft.WebApplication.targets".  I found a reference of "Microsoft.WebApplication.targets" in the csproj which looked liked 

Indeed the web csproj file had an import statement, which added the reference to "Microsoft.WebApplication.targets". But I was not sure why this reference was needed in the first place.

My first attempt at fixing the CI server:

I removed the reference of "Microsoft.WebApplication.targets" from the web csproj file and tried building the application using MSBuild.  To my surprise, the app build fine, it ran all the tests without any issue.  My confidence grew a little, I thought I had the solution.  I thought the reference was added to the csproj by Visual Studio 2010, but was never used.

I committed the fix, build went green on the CI Server as well!

After a few hours, some developers complained that, when they now open up the web project, Visual Studio 2010 prompted for converting the project into a Visual Studio 2010.  Certainly, this behaviour was triggered after I removed the import statement from the web csproj file.

Its pretty clear now, why the import was required.  Visual Studio 2010 identifies that, the project is a Visual Studio 2010 Web project using the import of "Microsoft.WebApplication.targets".  Hence, although the build run's fine using MSbuild on developer machine as well as on CI server this is not the right fix!

My second attempt at fixing the CI server:

Googling a little I found that, there are only two options to fix this particular issue.
  • Install Visual Studio 2010 on the CI server
  • Copy the "Microsoft.WebApplication.targets" file manually from any developer machine to the CI server
It was sad to see that there was no standalone installer that installed the "Microsoft.WebApplication.targets" at the desired location.  We had to either install the full Visual Studio 2010 or copy the file manually.


I decided to chose the lesser evil of the two.  We copied the file "Microsoft.WebApplication.targets" from my machine (located at path C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v10.0\WebApplications on a Windows 7 64 Bit installation) onto the CI server (at the path C:\Program Files\MSBuild\Microsoft\VisualStudio\v10.0\WebApplications.  CI server has Windows 2003 OS installed on it.) and then ran the build.  Yep! It worked!  The build went green on the CI server.

I know, its just an hack, but for now I had no other option!

Saturday, May 21, 2011

iOS: Fixing the WiFi connection closed after 30 minutes

One nice Saturday afternoon when I was doing almost nothing, I got a phone call.  The number was big and didn't look like it was from India.  I picked up the call and said "Hello...", a familiar voice replied back.  The voice was of one of my good friend.  This friend of mine had gone to Germany for a month, his iOS project was going live.  He had called me that afternoon to discuss about a problem that he was facing in his app.  

The Problem

The application was to be deployed on iPad.  Application used to poll the server for updates every 10 seconds.  For the application to work correctly, it was necessary that the polling should work flawlessly.

This polling worked well, however, he was facing one ugly little problem.  The issue was, after about 30 minutes, the polling would stop and because of this the application would not function correctly after 30 minutes.  This would happen only if the iPad is not connected to the charger or a computer.  If the iPad was connected to the charger or a computer polling would function correctly for any duration.

The workaround

To get the polling to work again user has to do one of the following
  • Connect the iPad to a power source i.e. a charger or a computer
  • Pressing the home button on the iPad and reopen the application
The Fix

I had not faced this issue earlier.  In fact, I have not done much iPhone development (at least non yet), but I decided to give it a shot anyways.  I decided to google about this issue and find a fix.

After some intense googling, I found that, iPad switches off the WiFi connection automatically after 30 minutes to preserve battery.

That's a good strategy alright but, what about the apps like my friends app?  The app need to poll the server every 10 seconds to function correctly.  How can we stop the iPad from switching off the WiFi connection automatically after 30 minutes?

The fix was pretty simple, One needs to set a property in the property list file.  The property is called UIRequiresPersistentWiFi.

Quoting from the Apple developer documentation
UIRequiresPersistentWiFi (Boolean - iOS) specifies whether the application requires a Wi-Fi connection. iOS maintains the active Wi-Fi connection open while the application is running.
 That was it!  I knew that, I have found a fix to my friends problem.  Asked him to add the property to the plist file and do a test again.  As expected the problem was solved.  Now, the app was able to poll the server for any duration without a problem even when the iPad was not connected to the power source!

Simple things like this waste a lot of developer time, but once you learn something the hard way, you will never forget it!

Saturday, May 14, 2011

Fixing the "skia decoder->decode returned false" error while downloading images on Android

I had to implement a common requirement in one of my Android project.  I had to show a image that was hosted on the web, in my Android application.  Now that is a very common situation right?  I am sure you must have faced this situation before.

I would have imagined, showing the images from the web would be as simple as setting the correct image source URL to the ImageView.  But as it turns out life is not always that simple. 

So how do we show the images from the web using the ImageView?

  • We have to first download the image using the HttpClient
  • Decode the image data using BitmapFactory and convert it into Bitmap
  • Associate the Decoded Bitmap with the ImageView
Lets look at the code to show the web image in the ImageView

The above code works and works well, it does show the image hosted on the web in the ImageView.  However, sometimes, I get an error in the log saying

Now, this is something unexpected. Whenever I get this error the image download stops and image is not shown in the ImageView.
The Cause:

Googled a little and as usual, I was not the only one to face this issue.  There are lots of threads online, that mention this particular issue (here and here), "decoder returned false". 

It seems that when skip method of the InputStream class does not skip the given number of bytes then, the BitmapFactory fails to decode the InputStream.  Usually, the issue occurs on a slow internet connection.  However, I cant confirm that, I have a 2Mbps dedicated internet connection, even on this connection issue is reproduicable! 

The Fix:

There are two ways of fixing this issue.

Method 1 - Method that involves writing some custom code

This method makes sure that, the skip method of InputStream actually skip's the mentioned number of bytes.

To do this, we will need to wrap the InputStream into a custom class, override the skip method and ensure that we skip the given number of bytes no matter what.  This can be achieved by subclassing the FilterInputStream class and overriding the skip method.  Lets look at how the new implementation of the skip method.

As seen in the above code, the skip method simply makes sure that we skip the give number of bytes.  The class FlushedInputStream now needs to be used while we decode the image. The updated code with the FilteredInputStream looks like

Method 2 - Method that involves using the BufferedInputStream

This method is very simple and straight forward we simply need to wrap the imageContentInputStream with the BufferedInputStream. This also seems to fix the problem. The updated code looks like

Yep, these two methods fix the "decode returned false" issue.

Saturday, May 7, 2011

Is your Bank Account safe with ICICI bank?

ICICI Bank is one of the biggest bank in India.  Its renowned for having most number of ATM (after SBI) in the country.  I always though that, ICICI Bank has a good security model to prevent online thefts.

No, this post is not an advertisement of ICICI bank, I wanted to share a weird experience that I had with ICICI Bank.  This experience will expose the security model of ICICI Bank.  Over and above it, I wanted to help them correct their mistake, but they didn't do anything as yet.

So what was the incident, how did I try to help and what was their response?

The Incident

One fine Monday morning, I got 4 SMS'es from ICICI Bank, informing me that for a certain account XXXXXXX12345

  • Address has been updated
  • ATM pin number has changed to 1234
  • Land line number has been changed to 020-30212345
  • Mobile number has been changed to 9912345678

When I got those SMS'es, I was a little surprised.  I was holding an account with ICICI Bank but I never requested them to change all this information for my account.  I had not called them for any such requests, then, who call them for changing all this information?  Was my Bank Account hacked?

After some initial anxiety, I calmed down and rechecked those SMS'es.  When I reread the messages, this time a little carefully, I realized that, my bank account number does not end with "12345".  Analyzing further I realized, I do not have a land line whose number is 020-30212345.  Surprising thing was, the mobile number was indeed correct.  9912345678 was indeed my mobile number.  Rest of the details (except the mobile number) were no way related to me.

The Response

By now it was clear to me that there has been a mistake.  I decided to call up the ICICI Bank call center to find out what was wrong.  I cleared the IVR authentication, got my call transferred to the Customer Service Executive.  First, I wanted to confirm that my Bank Account was not hacked.  I asked them if there were any requests made for changing the address, phone number, atm pin of  my account.  Thankfully, there were no such requests made for my account.  

Now the question that was bothering me was, who holds the account that ends with "12345" and why was it associated with my mobile number?  I asked the Customer Service Executive about which accounts are associated with my mobile number?

She said, there was indeed one account associated with my mobile number that ends with "12345".  Bingo! 

Being a decent man, I told her that, the account that ends with "12345" is not my account.  There seems to be a mistake, because of which my mobile number was associated with incorrect account.  She told me, she can see that the account is held by some "Mr. Pathak".  She also told me that the account was newly opened in the "Bhandarkar Road Branch - Pune".  I requested her to kindly delink my mobile number from his account.  She agreed and said this will be done in 2 days.  I was relieved.

By now I was thinking, this is such a big problem.  If someone gave an incorrect mobile number, or the bank's customer service executive made a mistake noting down the mobile number, All vital information will be SMS'ed to the incorrect mobile number.  I knew the other guys ATM pin number, I knew his account number, I knew his address, his land line number.  If this information falls in wrong hands, anything is possible.

By now you must be thinking that the issue must have been resolved.  But unfortunately, the issue does not end here.  After about 5 days I got an SMS informing that 25000 Rs. were credited into the account ending with "12345"

My first reaction to this was, WTF!

I called up the ICICI Bank call center again, to find out why my mobile number was not delinked.  This time they informed me that they cannot delink my mobile number from "Mr. Pathak's" account on my request.  I need to prove that, the mobile number under question is actually my mobile  number.  I asked them, what should I do to prove it.  They said visit any nearest ICICI Bank branch with and ID proof and the post paid Bill of my mobile number.

My reply to that was, I have no motivation to go to the bank to prove this.  Its a clear case of human error, they should have delinked my mobile number from Mr. Pathak's bank account.  

I wanted to help them rectify their mistake, but they will not let me do so.  Why the hell should I waste my time?  I definitely have better things to do!

The conversation ended there.  I was frustrated, why do people stop using their brains?  Why are processes so rigorously implemented that people stop using common sense?  

A few days passed, I got two more SMS informing that 25000 Rs were withdrawn from the account ending with "12345" and the account balance was 0.0 Rs.  The other message informed that, the balance of bank account ending with "12345" had fallen below the allowed limit of 10000 Rs.

This was the limit, I decide to actually visit the ICICI Bank branch and once and for all get my mobile number delinked from Mr Pathak's bank account.  I carried a photo identification proof (my PAN Card) and the post paid bill of my mobile number.  I went to the "Shivaji Nagar - Pune Branch"

The response that I got from the branch was unbelievable.  The lady at the bank said she cannot delink Mr. Pathak's account from my mobile number.  I showed the bank lady, my pan card, even the post paid mobile bill for my mobile number with my name written all over it!

Unfortunately nothing helped, she said she cannot do it.  She said it was against the processes of ICICI Bank!

I urged her that, there has to be a way to do it.  Inform Mr. Pathak about the mistake, get him to rectify this problem.  She assured me that something will be done.  But till date nothing has been done.  Till now I get SMS'es about Mr. Pathak's bank details on my mobile number!

How did I try to help
  • Called up the call center twice to inform them about the mistake
  • Went to the bank and proved that the mobile number indeed belonged to me but still they didn't delink it from Mr. Pathak's bank account.
  • Wasted 5 hrs of my life fixing a problem that didn't impact me much.

What should we learn from this incident

Software is meant to reduce human effort.  Processes are meant to reduce human mistakes.  This incident is a perfect example of what happens when people follow processes and use software without using their brains!

PS: All events and conversations mentioned in the post are real.  However, all Names and Numbers have been changed to preserve the privacy of a certain person.
Have some Fun!