Monday, October 18, 2010

My first Android app - 18 things I learned

I have written my first Android application. It is a game and it works pretty well on my phone and the emulator. I still want to tweak the sound effects to match the themes and to maybe add a two player mode. Then I will have to decide how to release it on the market. I could go with a lite free version and a small charge paid version or I could go with an ad supported free version. Leaning toward the ad supported free version at this point.

For this discussion the actual app does not matter. I want to cover what I learned. First off I have been programming in Java for a number of years so this is not about Java programming.

1. If you know Java then Android programming feels pretty natural but there are enough differences that you can't make assumptions. I got burnt more than once.

2. Setting up the environment took more than I expected. Everything is out on the web and I was happy to find a lot of on-line support no matter my question. What took a bit was a lot to download for all the Android flavors from 14. to 2.2. I had a little issue finding the proper USB driver so I could debug on my actual phone.

3. Running directly on the phone is faster than using the emulator. Plus it really shows the experience as you use your finger instead of a mouse. When you drag small things around with the mouse they are easy to see, not so much with the finger. The emulator is pretty solid. I did not find differences between what the phone did and what the emulator did.

4. Eclipse is a really nice IDE. Drop files into the drawable directory and they show up as resource IDs automatically. It does have some issues. It did not install the app 100% of the times when I pressed the run button. Could be the phone's fault. I was not totally consistent in swiping to unlock and getting ready to run. I also had the IDE pause when using intellisense, very annoying.

5. The UI does not stop running when you show an alert dialog. Something you get used to in Swing. Not a big deal, once you know that you just code with it in mind and it does make sense. You don't want to freeze the UI waiting on the user.

6. Rectangle is left, top, right, bottom instead of x, y, width, height. Just something to remember. 

7. Checkbox uses isChecked and setChecked. I had code to do isSelected and setSelected. The code compiled but did not do what I expected. Made an assumption. Never understood why Swing did not use the properly named methods. Glad Android does.

8. There is no Combobox but there is a Spinner. When I think Spinner I think numbers but the Android Spinner does the work of the Combobox and it does it just fine.

9. Strings belong in string resources, not in your code. I am familiar with this as the Java code I have written has been internationalized. Of course I was not doing that by default, I was hacking together code to make it work but in the end I moved all the strings into string resources.

10. The canvas is pretty powerful. All the features I needed were supported and well named.

11. Not all phones are equal. Different screen resolution being the big one. I have a new phone so I have a big screen. I set the emulator for a smaller screen. I need to tweak the code even more to fit as many screens as possible and to not leave dead space on big screens.

12. You have to draw a lot of icons. I had an icon in my drawable directory but it was not the one I would see on the phone. I had to drop special sized ones in each of the drawable directories to have the proper one to show up. Even at that the proper one only showed up under applications. I had put a short cut to my app on one of my screens but that icon did not change during the next app install. I had to remove it and re-add it to the screen to get it to change. Icons are not a standard Windows size - 256x256, 128x128, 64x64 but are 72x72, 48x48 and 36x36.

13. Playing sound is easy but they don't always play to the end. I need to figure out why that is happening. The API is really straight forward.

14. The amount of on-line information is impressive. The official Android help pages are a great starting point then I would use what I found there to find some solid example code. I was always able to find what I needed with minimal searching. 

15. The preference system is simple. I needed to save the sound, flashing and theme settings. Find a quick sample and 5 minutes later it was in place.

16. The options dialog was harder than I thought it should be. This seems like something that nearly every app will need so I thought there would be base classes to handle it. I found what I needed on the web but I can see where we will not see standardized dialogs.

17. I love the menu button, something I wish the iTouch had, but I had to dig around to find images for that area. These images tend to be gray scale with an indented look. Not typical color icons. I wanted to stay consistent with t;he Android look so I found where they are hidden in the SDK samples.

18. I was worried the game would take a long time to write but I got it done in a few days. I had the game running in Swing so the engine was ready to roll. I took the game to a new level with themes and greatly improved graphics. I also put in a help system using a basic alert dialog. I never did a help system with the Swing version as I did not want to learn RoboHelp or some other HTML based help system. I really could have cheated like I did with Android, just never push myself to do it.

All in all I am very impressed with the Android platform. I am late to the game so others have done the hard stuff and published a ton of code samples for me to use. It appears the SDK team learned from various Swing mistakes and weirdness producing a cleaner API. I am sure I forgot a lot of other things I learned, this is just my first Android app and I don't consider the app done. It is fully functional but like any piece of art it never really feels done. I will have to choose a stopping point and release it one day.

Monday, October 4, 2010

I now own and Android phone

Various things came into play but I decided to buy an Android phone. I have the Samsung Galaxy S via T-Mobile, which was my current carrier. First, my son is doing a lot of things with Boy Scouts so he has needed a phone while out in the neighborhood selling popcorn, trash bags and grocery store coupon books. He has also had to borrow others phones numerous times to get hold of us after camp outs.

Second my wife calls me to get web information while she is out and about. Mainly phone numbers and addresses. She has an older BlackBerry but with the new data plan it also gets web access.

Finally I am going to get to do some coding on a project for the Android and just using the emulator seemed like not the way to go. You always want to run on the real hardware if you have the chance.

I have an iTouch so most of the operation is very familiar. Still learning all the ins and outs. Seems to be a solid phone, very fast and I am happy with it so far. Nice to have the web in your pocket all the time. I was able to monitor NFL scores while out shopping yesterday.

Wednesday, September 29, 2010

Internationalization in Java - when is a number not a number?

The Java product I work on is internationalized and localized - well it turns out for the most part. I updated the code some time back to allow the user to type in numbers in their locale so a German speaking user can type 600,25 (notice the comma instead of a decimal point) and we should consider that to be 600.25 in English.

Two problems arose, the masked entry field I wrote handled this just fine allowing user to type in numbers using their locale settings but the code in others areas converted the typed text to a Double using Double.valueOf(text) and it would fail by tossing and exception. I switched the code to NumberFormat.getInstance().parse(text), adjusted to catch the proper exception and everything now works. This code area allows users to enter custom property values.


One thing I found out, NumberFormat returns a Number. That might be a Double or a Long. If you parse 85 you get a Long. If you parse 85.43 you get a Double. You can ask the Number for its doubleValue if you always want the double. I was getting a typecast error on occasion until I figured this out. If you send in an empty string you get a parse error. You may want to check for that before you parse to assign a default value of Double.MAX_VALUE to indicate empty or 0 if you want empty strings to equal a zero value.

Second problem came from the advanced search area where we allow users to search for previously entered data. We used the same masked entry control but never converted the output to a number. The server takes the search statement as a string so there seemed to be no need. Of course there is a need. The code looks a bit silly but I take the text, convert to a number and convert back to text so it will be in English format to send to the server.
String numOne = numberFieldOne.getText();

// We convert to a number then back to text for i18n reasons
// All numbers set to server in English format, this forces that rule
try
{
   numOne = NumberFormat.getInstance().parse(numOne).toString();
}
catch (ParseException e)
{
   numOne = numberFieldOne.getText();
}

Everything is up and running again. The company has a big push toward international expansion so it was critical that we solved these issues. I must say Eclipse pop-up help on methods is very handy. I viewed the pop-up for Integer.valueOf and it suggested using NumberFormat instead. I love it when you are handed the answer without needing to do a web search right from the IDE.

Converting existing code to i18n can be painful, making it i18n from the start and keeping it that way can be a big help. I will add this discovery to my i18n toolbox. Seems pretty rare for users to type in floating point values but it does happen and you need to account for it.

Monday, September 27, 2010

Why does Java paint different under different Mac browsers?

I understand there are differences between Java on a PC vs. Mac vs. Linux. We have code in place to check for each. Even some code for OS subsets such as some special code for Win7 and Vista vs. older Windows flavors. They added fancy pants painting to the combobox in Vista so my custom control paint needed to reflect those changes.

What gets me is I can run our applet on my PC under Chrome, Firefox, IE, Safari or Opera and it looks the same. All the colors match, buttons look identical, my custom controls paint exactly the same. I can see no differences.

Today I get a few bugs about things not painting correctly on the Mac. No big deal, I grab the development team's laptop, update the OS, update Java and install latest versions of Safari, Firefox, Opera and Chrome. Run Chrome as my testbed and fix the items reported by QA. Just an hour or so of pretty easy work. Then I decided to run the software to see if I spotted any new issues and I found some with toolbar button painting. I resolved those under Chrome then ran it in Safari. Background is a lot darker over the main toolbar but buttons are readable. Then I run it under Firefox and when you have a toggle button on the toolbar you can not tell which button is toggled. It gets a special outline and color in Chrome, Safari and Opera just not Firefox.

Safari on Mac
Firefox on Mac

The three right most green buttons are a toggle group, only one will be selected. They are fit to screen, fit to width and fit to height. On Safari you can clearly tell you are in fit to screen mode. Under Firefox you have no idea.

According to the Java console window that pops up as I run Java in each browser I am running JDK 1.6 update 20 in each one. I am not using any special paint code for this button, just using standard JToggleButton. I did not touch code in this area for my other bug fixes. I run an older version of our applet and it still looks bad in Firefox. Why does the JToggleButton paint differently in a JToolBar between browsers? Why is the browser controlling the background color of the toolbar in some cases? This is not how WORA is supposed to work. Of course I know WORA does not work between OS vendors or versions but I did not expect the browser to mess me up.

My other Mac bug was about things painting outside the custom control. On the PC in Java if you set a clip region outside the control you are currently painting it still gets auto clipped to your control. It was a simple fix, I just did an intersection between the current clip and the rectangle I was about to paint thus if it was out of bounds on the Mac I did not paint where I should not be painting. No Mac check, worked fine on PC with the intersection check. I did add a comment as to why the clipping is there and really it is the most legal way to handle things but it was the Mac that forced the work back on the developer, not saying the Mac was wrong, just the PC Java was friendlier.

We need to test more often on the Mac. It is an important platform. We have clients using it. Stinks that I have to dig down this deep to fix things and now I have a new problem between browsers that I am not sure how to even fix. Has anyone else run into the JToggleButton paint issue?

Wednesday, September 22, 2010

Two Java rotates - one works one does not (sometimes)

Why does this code not always work?
        g2.translate(translate.width, translate.height);
        g2.rotate((myRotate * Math.PI) / 180, 0, 0);
        g2.drawImage(image, 0, 0, null);

This code appears to work in all our test situation

        AffineTransform trans = AffineTransform.getTranslateInstance(translate.width, translate.height);
        trans.rotate((myRotate * Math.PI) / 180);
        g2.transform(trans);
        g2.drawImage(image, 0, 0, null);

This came up as a client issue. I did not write the original code but I was asked to help track down and resolve this rather annoying issue. When I run the old code on my machine it runs just fine. When we run the old code on a VM on another machine it works fine but running it as a servlet under Tomcat and it screws up and does not generate a rotated image. You get an empty rectangle instead.

The problem is now fully fixed but it just seems odd that the AffineTransform works where the standard rotate does not. You don't get any errors / exceptions. You just don't get a rotated image. We are going to run some timing tests between the two calls as we are curious which is faster. Of course faster and broken is broken so we will not switch back to the original code but we want to make sure server performance is not impacted massively with the new code in place. If this was client side processing we would be less worried but you still want things to run as fast as possible.

Tuesday, September 21, 2010

When all else fails upgrade the JRE

Another team was having issues with some image manipulation so they asked me to help out. They had traced it down to some code doing an image rotation. Code was very simple so no obvious problem. The code was happening in the servelet, get a TIFF, convert to PNG, scale it down and rotate it if the rotation flag on original TIFF was set.

Special TIFF (aren't they all?) so we use the 3rd party library from Acordex to load them and same them as a PNG. Everything there was working fine so I took the code out of the servlet and made a stand alone Java project. It worked perfectly. I wrote out the images to a temp directory at various stages.

I then created a JAR file with the test code so we could run it on the VM using same set up as the Tomcat based servlet. JAR did not run because I cheated to pull the 3rd party code from another JAR. Grabbed FatJar for Eclipse and built a new JAR with all the stuff I needed.

Worked like a champ on the machine of the developer I was helping. Put it on the customer identical VM and it did not work. Everything up to the rotate was fine but the rotate produced nothing for an image. Both I and the developer running JDK 1.6 update 21, the customer VM was update 6. Updated the customer VM to update 21 and it ran just fine. Did some searching for known rotate issues but did not find any. Figured the we have a fix for the customer so might as well let it go at that.

Update 6 is pretty old, don't think the custom is going to fight the upgrade. Anything below 19 is pretty suspect due to security issues anyway. I hate to blame the JRE first, always figure it has to be my code at fault especially when I am not doing anything out of the ordinary. Best be is to create a small test program if possible to narrow things down. Glad this one is off the books.

Monday, September 20, 2010

People will always use the tools they know

My son's school sends out a weekly PDF document of activities. I thought it looked a little odd but was not too worried about it. This week it came as a MS publisher file because they said the scanner was broken. From that I deducted the following steps were happening each week:

1) Create the newsletter in MS Publisher
2) Print it
3) Scan it as a PDF
4) Email it

Not everyone can open a publisher file, the PDF format is the way to go as the reader is free. Same reason I sent out our Christmas letter in PDF format. The oddness came from the PDF I got being scanned. I knew it looked grainy but was not sure if that was a look they intended. Some like the whole retro thing.

I sent the principal a note that they can just get a free printer driver allowing you to print from any program directly into a PDF skipping steps 2 and 3 plus giving you a much nicer looking final product for the email. He just responded that he downloaded the program and it worked like a champ so they will start doing things that way from now on.

Happy to be able to help the school out as they do so much for my kid and all the children that attend.

It always amazes me to what extremes people will go with the tools they have. Not every fix involves a download of something either. Just knowing a different formula in Excel can save hours of manual labor.

I photoshopped a picture for my son's birthday cake. They said bring in the picture, I asked can I bring in a CD or a USB key. I was told they can only scan images so I had to bring in a hard copy picture. I have a feeling that is not really how it has to work but I gave up and printed the picture and gave them that. The cake came out fine, a printed image slapped on icing is going to look fuzzy anyway but I have a feeling there is a way to pull this off without a scanner. Silly image ends up as a JPG in there somewhere why not just start out with that as the source?

Not that I have never done the same sort of thing, taken way too many steps to do a simple process when someone that is an expert in that area could have trimmed it down to two mouse clicks. I bet I did a lot wrong during my photoshop session with the picture. Happens when you don't do something very often but as so as I have to repeat a process the first thing I think is "Computers are meant to handle repeating tasks, I will figure out a way to bend a computer to my will" and I usually do. Sometimes you give up totally, other times you give up and then one day read something on the internet that reminds you of how stupid your process is and you fix it.