Google+ Badge

Wednesday, December 21, 2011

JIDE Table as a base for the scheduler

For the moment I am going to punt on MigCalendar and give JIDE a shot for our scheduler. We already have a license for JIDE so might as well get some use out of it and we can drop a custom control and JAR files from the project with the switch.

The JIDE table supports various row sizes, multilevel columns, custom rendering, row labels, etc. Pretty much everything we need I hope as long as I can get a really nice looking custom cell renderer in place. Row / Column spanning is in there but night be tricky to handle what we need. I also need to pivot things but will probably do that with a table reconfiguration instead of using their pivot stuff due to special layout needs.

I did run into some issues with the new MarginArea as the demo they provide was missing that file. I almost got the code running by guessing at they layout but it was not working. A quick email to them got me the example code I needed to complete the task. Very happy with the turn around time on that request.

Going to take some effort to get it all in place but for the first days work I feel I got a lot of progress done. The documentation is still a bit iffy, English is a second language for them and web support is light but I have a feeling I can fight through this one.

I hate to give up on MigCalendar but so many here have tried to make it work the way we need it that it is really time to move on to something else that hopefully solves most of our needs and does not introduce too many new headaches.

Tuesday, December 20, 2011

MigCalendar = slow progress

While MigCalendar might be powerful and flexible it is poorly documented and there is pretty much non-existent support on the Web. The company locked down their forums so you can only submit bug reports. I did just submit one so I hope I get an answer to what seems something simple really soon. I hate it when you don't have access to a forum to share problems and solutions with other users.

I did break down what I am attempting to do into a single source code file. I want to get as much running as possible in that before I integrate with the main product. I have been doing work in the main product too and running into the same issues. With all the other work I am attempting in the main product I don't want to get my lack of knowledge of MigCalendar to get in the way of my lack of knowledge on the rest of the scheduler.

I have looked for other products that support our needs and have found nothing. I can find simple calendars that show a day, week or month of events for a single person and with time on left and day above but we need to show times for facilities / room, providers, etc. I need a flexible header and activities driven by categories. I need to show overlapping appointments.

It is getting close to fish or cut bait time. This happens with a lot of 3rd party controls. You get a nice demo going or get a first cut of your code running then you start to run into walls. I am not sure if some of the walls can be broken down here or if they are solid limitations. Writing a header, footer, time range and full grid with drag and drop would take a lot of time but in the end you get exactly what you need. I have written plenty of custom controls in Java and other languages so I can tackle this. Start with a small piece and build it up until it all works.

Others in the office have had the same experience. They can fix an issue here and there, tweak the look and then get out quickly. The open issues have been left open because no one has been able to fully understand what MigCalendar does. I have it working better in some places - especially with two level deep categories in the header - but broken in so many others. We know it can run fast, you can see that in the demos, but our code is rebuilding too many things as things are selected. The demos tend to just have one layout where we allow the user to toggle how they are looking at things similar to pivot tables in Excel. It has to recalculate too many things about the activity when this happens. That also leads me toward doing a custom solution that is optimized for the data we have. Generic is wonderful when you can pull it off but there are times you need things to do exactly what you want and do them very fast.

When I was working in the stock market industry I wrote a custom grid in Java to do high speed painting of real-time market data. It could handle a lot of updates using dirty cell paint management and doing paints on a quarter second timer. For a long time it was used by one of the big "do your own trading" places that you see advertise on TV. Not a scam place, a legit trading company that is still around. Sure I can look over that code and get some ideas. It was all Java 1.1 AWT and I did a conversion to 1.2 Swing at one point. Old stuff but highly optimized.

Monday, December 19, 2011

New application version out on both Markets on to MigCalendar

I finished up the latest release for both Android and iOS and QA accepted it as ready to release on Friday. I submitted it to the Android Market where it appeared nearly instantly. To my surprise it appeared in the Apple App Store on Sunday - I got an email from Apple on its updated status. I did not think they actually worked over the weekend. This is the second time it appeared two calendar days after submission.

Based on activity they just gave it the illegal method check once over and let it go to the store as it is an update to an existing application that requires a login to use. It is only useful to our clients. It is not like we are selling it and forcing you to buy a login outside of the App Store so I don't think Apple gives a rip about what it does, if it leaks memory or how clients use it. I don't they even care if it follows their UI guidelines. As far as I know it is not doing anything out of the norm. I triple make sure I am not using any private API calls, run it through the leak detector as I add features and really the app is used to access our servers and get to the data the client owns and to send updates to the data as needed. I am very happy it went through the process quickly.

Now that everything is out of beta we can turn on access at our server level and let the doctors have some real fun with the application. I am really curious to see how many installs we have for each platform and how much data traffic it generates. Doctors can take pictures of the medical paperwork on site so we should start to see images flying about soon enough. Well I won't see the images, I don't look at live medical records, but I can see file sizes and what not from the log files.

We will await feedback from the clients before we plan the next release. I have some items I would love to add to the program but if no user requests them it is better to work on the real needs and not my guesses. This means I am on break from the mobile side of things for a bit. Good thing to happen just before the holiday season.

I will be working on our front office appointment scheduling program. It makes heavy use of MigCalendar. I have messed around with that a bit but am a total novice. The original developer is not here so I will have to figure out most of it on my own. MigCalendar is a bit of an odd bird. Things are stored in statics that I don't feel belong there. The API is very powerful but not easy to understand. I started work on this a few months back and made a lot of notes so I am reviewing those and diving into the code. Various object constructors have numerous parameters which can get hard to follow.

Our current UI is lacking in usability. There is a tabbed interface but the tabs are really just controlling what is visible in the calendar. There are two buttons inside each tab that control the filtering operations. I am converting these into a combobox with 4 items mirroring the tab / button combinations listed above. This gives more vertical room in the UI and gets rid of a tab that is not really a tab and buttons that toggle but don't. It allows me to add more root + filter modes in the future and cleans up the code nomenclature as the buttons were namee after their functionality in the first tab which has no relationship to what they do in the second tab. It also leaves one instance of MigCalendar running instead of two. Yes, this whole application was in dire need of help.

Can't say I am looking forward to getting this all up and running again. One of those projects you just want to scrape and start from scratch. I feel like it should appear on "Extreme Code Makeover" and we should blow up a computer with C4 while some dude with OCD runs around screaming. Once I get over the initial learning curve it should not be too bad. Of course once it starts working like it should more requests will come in and I will be tagged as "the guy" that knows the appointment stuff. Since I am taking off the last week of the year I also get a chance to forget most of what I figure out this week. Nothing like starting over twice in a few weeks period.

Thursday, December 15, 2011

New ADT 16 includes Lint - very nice

Grabbed the new Android ADT version 16 and it includes Lint. I like to keep my code as clean as possible so I found this very useful. My initial warning list was 374 items which can seem a bit scary. I have knocked that down to 252 items doing some very simple things. All of the information provided by Lint is useful but not all of it is something you must fix.

I have some strings that are hard coded in my XML files. They say things like "date placeholder" and the like. I have them to help me visualize things when I am using the preview window. Actual values will be filled in at run-time. There are also a lot of "Missing contentDescription attribute on image". I might just shut this one off. This program is used by doctors. Being visually disabled pretty much means they can't do their job. I don't need to support that functionality in this project.

I was able to cleanup some "useless layout" issues. I converted single row table layouts to liner layouts. I was setting the background color and cache color hint in a lot of my XML files. It recommended using a theme instead as everything was getting painted twice - once with the them background color and then with my override color. I set up an application level theme in the manifest to solve that problem. It also forced me to define the background color in the style.xml file so I did a quick search across all XML files and updated them to use the define. I can now change the color in one place and have the entire app use it. I should have done it this way from the start but somehow cut and paste seems more fun than doing it right when you are in a hurry. We are hitting final QA cycle and I want things to be as clean as possible meaning this was a good time to fix past oversights.

I still have a couple of "children can be replaced by one and a compound drawable." items to clean up. That will involve code changes and not just XML changes as the image in font of the text changes at run-time depending on the status of the item. Makes sense to use the setCompoundDrawables instead of having two controls in a LinearLayout when the TextView already has full support for that. I will probably wait to change those for the next release as I could find subtle issues to work around since I have not used this particular functionality in a layout used for table rows.

It is highly recommended that you grab the latest ADT and see what warnings it has in store for you. Between the new Lint and the utility I wrote to scan my project for orphan resources I am feeling pretty darn good about the next release. QA has not found any issues in a few days so it looks like we will ship in the next few business days and allow our clients to enjoy all the new functionality.

Wednesday, December 14, 2011

A better understanding of iOS memory management

I got a free eBook called "Objective-C Fundamentals" from Manning. Chapter 9 covers memory management, something we all love. While I have read various things about viewDidLoad viewDidUnload and didReceiveMemoryWarning I had not tied them all together. Since my app can get into low memory conditions when using the camera reading this chapter gave me a better understanding of things. It was time to tackle this beast.

The home screen in the app consists of 6 images the user can press. Below those images are labels and behind all of that are shelves that the images sit on. The labels are pasted onto the shelf front. Pretty basic stuff but the images can be large on the iPad or retina iPhone so we need to be a good neighbor and throw them away during low memory conditions.

First mistake I was making - loading all the images in the init method. I should load things in viewDidLoad. I moved all that code which was easy enough.

Second mistake - thinking viewDidLoad and viewDidUnload are called in pairs. Gee, the names sure lead you to believe that will happen but it does not. Apple added viewDidUnload support in iOS 3.0 but did not add pair calling code. Since this view in my app is not using an XIB because I totally change the layout between portrait and landscape I don't get viewDidUnload support even when I call self.view = nil in didReceiveMemoryWarning. If I set it to null then I will get multiple viewDidLoad calls but never a viewDidUnload call.

Here is what happens:

Initial view appears
   viewDidLoad is called
As I go back and forth between the Home view and other views - no other calls made.
Go to a view off the home view (home view not visible at this point)
Force low memory condition in simulator
   didReceiveMemoryWarning is called
     I set self.view = nil
     I thought viewDidUnload would be called - it is not
   Hit "back" button on second view
   Home view becomes visible
   viewDidLoad is called on Home View

Since I am not using an XIB file I have to do all my memory clean up in the didReceiveMemoryWarning call. It all works fine now and running the memory leak detector shows all is good.

I can tell things are working because the images "fly" into place when the view first appears or you rotate the device. First time up they fly from 0,0 to their spot on the shelf. Toggle back and forth between the Home view and another view and they don't fly as they were already in right spot. Toggle to second view, force low memory condition and return to Home view and they fly back in place again. Of course I put in breakpoints to verify all the calls are being done in order too.

The app is being a good citizen. Things are loading in the correct methods. Memory is being dumped in the correct methods. Now if only the methods were actually called in pairs to avoid all this confusion. Such is life when you don't use Interface Builder and IBOutlets due to your interface being different between portrait and landscape. I run 3 shelves in a 2x3 layout in portrait and 2 shelves in a 3x2 layout in landscape.

Monday, December 12, 2011

First OSX Utility - find unreferenced image files

Today I wrote my first OSX application. I have been writing iPhone apps in Objective C but I had not done an OSX application. I needed a utility to tell me what image files were unreferenced in my iOS project so I can remove them and clean things up.

Started out with a base OSX Application in Xcode. Added some basic fields to the XIB file - Directory name, [Browse...] button, [Find Issues] button and a text area to show the results.

Next I figured out how to browse for a single directory and get the URL back. Take that URL and do a recursive search for all other directories. In each directory find image files using an image extension list and file all code files (*.m, *.h).

Once all the file information is available start looking in the *.xib, *.h and *.m code for images either by NSResourceName, extension or by imageNamed: as that call will automatically look for PNG files. As we find references in code increment the image name reference. I have code to ignore commented out lines and blocks of code although the block indicator needs to start the line not be buried within a line at this time.

At the end of the run it builds a string of all orphan image files (those without reference in code) and populates the text area with the list. This leaves some false positives for icons that you need to publish the app but those are pretty easy to ignore at this point.

I added a progress indicator as the program run can take a few seconds to run, clean up of the menu system to show only valid items, update the about dialog text and adding a real program icon.

Finally I added a check for {name}.png / {name}@2x.png to show what missing retina images or orphan retina images you happen to have. I found eight images that I had not done a retina version of so this was a handy validation. I also found an image that was not being used and was being referenced from an unrelated directory.

This does find false positives but mainly on the numerous icon files you need for all iOS device flavors. I might need to add another text field for you to type in "files that start with {string}" to ignore but only people who are OCD enough to crave an error free run would care. Everyone else will go "yeah, forget those" and move on.

I am using some code I found thanks to Stack Overflow to read basic text files a line at a time. Totally surprised, although at this point I should not be, that is missing from the core library. So odd to me how many very basic things are missing from the iOS / OSX SDK. It still appears that Apple likes to add new API areas but not make existing API calls better.

My overall understanding of Xcode / XIB files / Objective C put the whole project in at under a day. I learned how to query for a directory, roll through a directory structure, read lines from a text file and do widget anchoring in XIB so my window resizes / relayouts out controls as expected.

At this point the program is not as powerful as the Java version I wrote for Android project validation. On the Android side I can check images, strings, layouts, animations, etc. and I don't have any false positives as all my icons are referenced in one of my XML files. For OSX I am making a bit more of a guess about the code to see if you are using an image. The utility will help me do this round of clean up and will keep it clean as time goes on. I really wish the SVN integration with Xcode was better. I know I can delete the file from the project but it does not delete it from SVN. I will have to manually clean that up or do the work under AppCode from Jetbrains as it does full integration.

Friday, December 9, 2011

New Android Utility - find orphan references

I decided to write a new utility to help clean up my Android project. It is a simple Java command line only program, no JFrame / no GUI, that takes a base directory as its only parameter. From that it builds a list of all sub-directories (ignoring .svn directories) then finds the Android generated R.java file.

R.java is parsed based on "public static final class" and "public static final int" to build a list of ID strings.

Each .JAVA and .XML file is then scanned in the directory tree looking for resource IDs in their various formats such as @string/ in XML or R.string. in Java. The reference count is incremented. At the end of the run I print out all the items that have a reference count of zero. I was able to clear up a dozen strings, a couple of layouts and 10 images from the project. Strings and layouts don't amount to much but images sure can when it comes to download size. Some of the images were in multiple directories to handle various Android sizes. Getting rid of layouts is always good as you might open one, edit it a bunch, the find out you edited something that is never used. Killing a layout might kill other image and string references too.

I wrote something similar to this at a previous job to find orphan strings before we sent the Java and C++ programs out to be localized. That saved us from paying money to localize a string that was never used in the program. I also checked for orphan images against the Java code and some other orphan resources against the C++ code. It would be nice if this was just built into Eclipse.

Now I need to do something similar for the iOS side. It will not be as straight forward as the images are directly referenced in the code as string names and I need to add a special check for @2x images to make sure they have a non-retina mate. I guess this would be a good time for me to write my first non-iPhone based Objective C program. Might put a GUI around it with a [Browse..] button to pick the directory and a list control to show the results. Seems to be cheating to write just a command line program on the Mac.

I want to run the program a few more times against various XML / Java code formats before I release it to the wild. It appears to work like a champ against the code I write but I am the only author and follow a consistent code format which does not make for a well rounded test. I kept getting false positives until I tweaked various bits of the parser. Trust but Verify was the rule until the final run when it was 100% accurate for my coding style.

Anyone have a big interest in this program and want to be a beta tester? Leave a comment and I can send you the source code (it is all of 288 lines in a single file) so you can see what you think and request improvements.

 ** Update ** Code appears below for anyone to run and check out

package org.peck;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Simple command line program to find the R.java Android generated code and
 * look for orphan references against *.java and *.XML files  
 * 
 * @author kevin.peck
 * @date December 9, 2011
 */
public class AndroidResourceScan {
    
    private static List<String> directories = new ArrayList<String>();
    private static List<RefInfo> references = new ArrayList<RefInfo>();
    
    private static List<XmlMapping> xmlMapping = new ArrayList<XmlMapping>();
    
    private static final String RESOURCE_FILE = "R.java";
    private static final String CLASS_START = "public static final class";
    private static final String ITEM_START = "public static final int";
    
    /**
     * Android resource scanner
     * 
     * Find and parse the R.java file and see if there items in that file
     * that are not referenced in any *.java or *.xml files
     * 
     * @param args  Arguments, base directory
     */
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Android Resource Scan by Kevin Peck");
            System.out.println();
            System.out.println("Parse the Android generated R.java file then look");
            System.out.println("for orphan references - any defined IDs that don't");
            System.out.println("appear in a JAVA or XML file in same directory tree.");
            System.out.println("Comment lines are ignore in all files");
            System.out.println("NOTE: .svn directories are ignored.");
            
            System.out.println();
            System.out.println("Usage: AndroidResourceScan {base path}");
            return;
        }
        
        xmlMapping.add(new XmlMapping("@string/", "R.string."));
        xmlMapping.add(new XmlMapping("@drawable/", "R.drawable."));
        xmlMapping.add(new XmlMapping("@color/", "R.color."));
        xmlMapping.add(new XmlMapping("@style/", "R.style."));
        xmlMapping.add(new XmlMapping("@id/", "R.id."));
        xmlMapping.add(new XmlMapping("@+id/", "R.id."));
        xmlMapping.add(new XmlMapping("@anim/", "R.anim."));
        xmlMapping.add(new XmlMapping("style name=\"", "R.style."));
        xmlMapping.add(new XmlMapping("<attr name=\"", "R.attr."));
        System.out.println("Scanning " + args[0] + " for R.java file");
        buildDirTree(args[0]);
        
        boolean haveResFile = false;
        File resFile = null;
        for (String dirName : directories) {
            resFile = new File(dirName + RESOURCE_FILE);
            if (resFile.exists()) {
                haveResFile = true;
                break;
            }
        }
        
        if (!haveResFile || (resFile == null)) {
            System.out.println("Unable to find " + RESOURCE_FILE + " in " + args[0] + " directory tree");
            return;
        }
        System.out.println("Found resource file " + resFile.getAbsolutePath());
        buildRefList(resFile.getAbsolutePath());
        
        System.out.println("Scanning JAVA files for references");
        scanFiles();
        
        int orphanCount = 0;
        System.out.println("Orphans found");
        for (RefInfo refInfo : references) {
            if (refInfo.refCount == 0) {
                orphanCount++;
                System.out.println(refInfo.name);
            }
        }
        System.out.println("total " + orphanCount);
    }
    
    /**
     * Scan the directory tree .java and .XML files 
     * Send them to proper parser as found
     */
    private static void scanFiles() {
        for (String dir : directories) {
            File fDir = new File(dir);
            String [] files = fDir.list();
            for (String fileName : files) {
                if (fileName.endsWith(".java")) {
                    scanJavaFile(dir + '/' + fileName);
                } else if (fileName.endsWith(".xml")) {
                    scanXmlFile(dir + '/' + fileName);
                }
            }
        }
    }
    
    /**
     * Open a java file and scan it for R. references
     *  
     * @param fileName
     */
    private static void scanJavaFile(String fileName) {
        BufferedReader reader;
        try {
            reader = new BufferedReader(new FileReader(fileName));
            String line;
            boolean inComment = false;
            
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (inComment && line.endsWith("*/")) {
                    inComment = false;
                } else if (!inComment && line.startsWith("/*")) {
                    inComment = true;
                } else if (!line.startsWith("//")) {
                    int iStart = line.indexOf("R.");
                    while (iStart != -1) {
                        if (iStart != 0 && " (=".indexOf(line.charAt(iStart - 1)) != -1) {
                            int iEnd = iStart;
                            while (iEnd < line.length() && " ,);".indexOf(line.charAt(iEnd)) == -1) {
                                iEnd++;
                            }
                            String refName = line.substring(iStart, iEnd);
                            for (RefInfo refInfo : references) {
                                if (refInfo.name.equals(refName)) {
                                    refInfo.refCount++;
                                    break;
                                }
                            }
                        }
                        iStart = line.indexOf("R.", iStart + 1);
                    }
                }
            }
            reader.close();
        } catch (IOException exp) {
            exp.printStackTrace();
        }
    }
    
    /**
     * Scan XML file looking for XML mapped entries
     * 
     * @param fileName  Name of file to scan
     */
    private static void scanXmlFile(String fileName) {
        BufferedReader reader;
        try {
            reader = new BufferedReader(new FileReader(fileName));
            String line;
            
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (!line.startsWith("<!--")) {
                    for (XmlMapping xmlMap : xmlMapping) {
                        int iStart = line.indexOf(xmlMap.xmlName);
                        if (iStart != -1) {
                            int iEnd = iStart + xmlMap.xmlName.length();
                            while (iEnd < line.length() && " \"><".indexOf(line.charAt(iEnd)) == -1) {
                                iEnd++;
                            }
                            String refName = xmlMap.resName + line.substring(iStart + xmlMap.xmlName.length(), iEnd).trim();
                            for (RefInfo refInfo : references) {
                                if (refInfo.name.equals(refName)) {
                                    refInfo.refCount++;
                                    break;
                                }
                            }
                        }
                    }
                }
            }
            reader.close();
        } catch (IOException exp) {
            exp.printStackTrace();
        }
    }

    /**
     * Build directory tree based on a root node
     *
     * @param baseDir  Root node to scan for sub directories
     */
    private static void buildDirTree(String baseDir) {
        String topDir = baseDir;
        if (!topDir.endsWith("/") && !topDir.endsWith("\\")) {
            topDir += "/";
        }
        File fDir = new File(topDir);
        if (fDir.isDirectory()) {
            directories.add(topDir.replace("//", "/"));
            String [] files = fDir.list();
            for (String fileName : files) {
                String subDir = topDir + '/' + fileName;
                File testDir = new File(subDir);
                if (testDir.isDirectory() && !subDir.contains("/.svn")) {
                    buildDirTree(subDir);
                }
            }
        }
    }
    
    /**
     * Build a list of public static final class references found in given file
     * 
     * @param fileName  Name of file to process ({path}/R.java)
     */
    private static void buildRefList(String fileName) {
        BufferedReader reader;
        try {
            reader = new BufferedReader(new FileReader(fileName));
            String line;
            String className = "";
            boolean inComment = false;
            boolean inClass = false;
            
            while ((line = reader.readLine()) != null) {
                line = line.trim();
                if (inComment && line.endsWith("*/")) {
                    inComment = false;
                } else if (!inComment && line.startsWith("/*")) {
                    inComment = true;
                } else if (!line.startsWith("//")) {
                    if (!inClass && line.startsWith(CLASS_START)) {
                        className = line.substring(CLASS_START.length()).trim();
                        if (className.endsWith("{")) {
                            className = className.substring(0, className.length() - 2);
                        }
                        inClass = true;
                    } else if (inClass) {
                        if (line.startsWith(ITEM_START)) {
                            int nameEnd = line.lastIndexOf('=');
                            if (nameEnd != -1) {
                                references.add(new RefInfo("R." + className + '.' + line.substring(ITEM_START.length() + 1, nameEnd)));
                            }
                        }
                        if (line.endsWith("}")) {
                            inClass = false;
                        }
                    }
                }
            }
            reader.close();
        } catch (IOException exp) {
            exp.printStackTrace();
        }
    }
    
    /**
     * XML to Resource format mapping 
     */
    static class XmlMapping {
        public String xmlName;
        public String resName;
        
        public XmlMapping(String xmlName, String resName) {
            this.xmlName = xmlName;
            this.resName = resName;
        }
    }

    /**
     * Resource name and reference count (all we care about is count != 0) 
     */
    static class RefInfo {
        public String name;
        public int refCount;
        
        public RefInfo(String name) {
            this.name = name;
        }
    }
}

Feel free to report any bugs you find back to me.

Wednesday, December 7, 2011

New machine built - data xfer time

I built a new machine as an early Christmas present for both my son and I over the past couple of days. My son helped me build it which was a cool experience for him. He is getting my current machine as a nice upgrade. His older AMD machine has been having all sorts of issues from not wanting to boot to booting but then not seeing the NIC or having the sound be bad. It could be a flaky power supply but I was due an upgrade anyway. Since I program for a living a 5 year old machine running a 32bit OS was starting to show its age.

It was a fun experience for him. I have a feeling we are not very far off from never building again so he may never use these skills. I looked for a pre-built box just to avoid the potential build headaches but could not find anything set up the way I wanted for a good price. If the price was good it was some crappy brand. If the parts were good it was $350-$500 more than the parts. I also drive a manual transmission. Cars and computers are like my coding - closer to the bare metal that many like to go. I write custom controls and never shy away from writing gut level code.

Here is the configuration

Intel Core i5 2500K
Thermaltake TR2 RX 750W Modular Power Supply
Samsung 22X DVD‘RW Burner with Dual Layer Support
Gigabyte GA-Z68XP-UD3 LGA 1155 Z68 ATX Intel Motherboard
Sentey Extreme Division Optimus GS-6000R Mid Tower
OCZ Technology Vertex 3 Series VTX3-25SAT3-120G
Zotac ZT-50303-10M GTX 560 Ti Overclocked 1024MB
ASUS Xonar DG 5.1 PCI Sound Card
Corsair 8GB DDR3-1600 (PC3-12800)

I went i5 instead of i7 after seeing in various websites that the i5 is a better gaming CPU due the hyperthreading getting in the way on the i7. I am not one of those "must have the top of everything" guys. I want a system that is stable and comes at a reasonable price. This is also why I did not get the $600 video card or the $1,200 dual video card set up. I bought all of it at the local MicroCenter. I normally order off NewEgg but they had a very limited processor selection and the price was higher than just picking it up local. NewEgg were cheaper on the sound card and DVD player but only by a few bucks. I just wanted to get it all at the same time ready to build.

I did go with a sound card even though the MB has built in sound. I have never been super impressed with motherboard sound and research shows the ASUS $30 to be a nice upgrade for a really good price. It is the 5.1 card, not 7.1, but I have a 5.1 setup I am happy with so it was the proper fit.

The SSD was a bit of a splurge. I have SSD at work and it is so nice to boot quickly and move right along after that. I am also moving over a normal HD to this setup to store music, images, etc. The case has a ton of fans but they are all pretty large and spinning slowly making the setup very quiet. The sound of a HD spinning is so familiar this just seems like it is not working at all. Guess I will even hear more of my game sounds when I get the chance to play.

Once it burns in for a few days I will fill out all the rebate paperwork and get that sent off. I hate rebates but I do have $100 coming back to me so it will be worth filling it out. I also need to download Assassin's Creed Brotherhood which I got for free with the video card.

I installed Win7 Ultimate 64bit and the Windows Experience comes to 7.5 with 7.8 being the highest and everything pretty much in that zone. Install was quick from DVD to SSD and boot time is faster than any machine I have ever used.

The build had two difficult areas. One was getting the stinking CPU cooling fan to seat properly. Seems I could get 3 corners in and have one pop out. The second was getting it to boot. Initially fans would kick on, single beep from post and then no video. I tried all sorts of things and gave up for the night thus making this a two day build. Finally used a screwdriver to short the CMOS jumper and then it booted. Not sure what in CMOS / BIOS was so annoyed it needed a reset especially since this is a brand new board and setup but that worked.

I turned off on-board sound in BIOS then watched the CPU health for a bit. CPU temp kept climbing so I shut it down and reseated the CPU fan again. Fired it up and CPU stayed at 45C for 20 minutes so I was happy and did the OS install.

I copied various files over from my server, installed updated video, sound and MB drivers along with Java and Eclipse. I still have a ton of other work to do to get it all in shape. I need to pull the HD out of my son's original machine and put it in my old machine so he can copy over files as he needs them. He will get to reinstall some of his games too. I did a backup of my Steam games and will move that over.

The new MB does not have IDE on it and I was planning on moving my secondary IDE drive into this machine. Guess I need a IDE / SATA adapter to do that. Right now all HD prices are up due to flooding so I hate to buy a SATA drive when I have usable drives to store music and what not. They might not be super new, fast and shiny but they spin up and hold data. I have access to what I need on our shared server drive so it is not a critical item but I hate to get in and out of the case too many times. I need to go in again to route power supply cables in a cleaner manner at the very least.

Going to be a number of days / weeks before I figure out everything I am missing. Always some utility you use rarely but need from time to time. Since my old machine is just going to my son and we are not reformatting it I will be able to move across anything I need with ease. That machine has dual HD right now so I will copy over everything I can think of from one drive to the other and put that drive in my new machine then put his old drive in there as his D: drive so he has access to his stuff.

Now I am ready to play some new games to see how this thing really works. I have Gears of War that I got for $20 a long time back. It always stuttered on the old box so I could never get into playing it. Now it should run just fine. Might be the first thing I install to prove this new beast was worth it.

Means I wont have much to open under the tree this year but really the holidays are all about watching other people open stuff you bought them. Shame I did not have the new setup over Thanksgiving when I got a second copy of Portal 2 during the Steam sale. My son and I played the co-op all day long finishing all levels. Near the end his machine would not show where the white goo was on the level so he could not place portals. My machine did things just fine. Could be a reboot of his machine would have kicked it in, could be his flaky machine would have never worked or his video card was just too old or memory shy to every handle it. He is so ready to play single player Portal 2 on a "real" machine that getting him to do homework is going to be rough. When daddy gets a new machine everyone wins.

* UPDATE *
Did a ton of data transfer last night. I now have my backed up Steam games installed (overnight process) and am downloading my free copy of Assassin's Creed Brotherhood today as it refused to accept $0.00 payment last night but did allow it this morning following exact same steps.

I have my son's old IDE drive in his new / my old machine and I copied over all his documents to the C: drive. As he reinstalls games we will move over the save game data from that drive and eventually we will remove it from the system as it is a waste of power to spin it up. I set up a new account for him and left my account so I can copy up to the network if I would happen to need something I forgot over there.

There were two IDE DVD drives in the case but only one is set to run right now as I stole the other IDE channel for the old HD. He is running without a burner at this point but with a slot load reader. Lets him install games but I need to get the burner back in place so he can burn videos and other school projects in the future. Of course we can burn them from the server, my wife's laptop, my machine or my other son's laptop meaning it is a hassle but not a big deal.

I installed Gears of War. It would not run saying I had a modified EXE. I installed the patch and it ran but it will not let me leave the video set up screen. I assume it is a Win7 64bit issue but gave up and went to bed instead of trying over and over. I will try a Steam game tonight but the ones I have use the old source engine so I doubt they show off the graphics card. I need to break down and by Skyrim but I really want it to go on sale. I have never paid $60 for a game and hate to start now.

Thursday, December 1, 2011

Android Out of Memory issues

The dreaded "Out of Memory error" with corresponding "Force Close" on your Android device is very frustrating. It was happening to our users from time to time but today I think I may have found the issue. I won't know for sure if this is a true fix until it is out in the field.

I have a couple of ways to acquire images in our app - you can take a picture with the camera or grab an existing image from the gallery. In either case I have to check out the image size and shrink it down to less than 1 meg of data via scaling, quality compression or both. It has to be less than 1 meg as I am holding the images in a SQLite blob to be sent to the server at a later time. I can't leave things in the gallery due to HIPAA rules and I can't trust the user to not delete the original image between pick it and transmit it time frames.

You can also view the image at any time within my app using a viewer I wrote that allows pinch zoom, drag scrolling, rotation and fit to window functionality. This is where things started to go awry.

If I opened and existing image, exited that activity then tried to capture an image from the camera there was a good chance I would get the out of memory issue. If I just took pictures but never viewed them or waited some amount of time before viewing them it might work just fine. This also depended on the device and version of the OS I was using. To find the issue I opened an image in the viewer then closed the viewer and did a heap dump via DDMS in Eclipse, converted that dump via hprof-conv in the android sdk\tools directory and loaded it up in MAT (memory analysis tool) under Eclipse. The Dominator Tree showed the ImageView of the closed activity to still be holding on to a weak referenced large bitmap. I guess the reference is not weak enough for the GC to kick it out of memory during the camera processing even though I was calling System.gc() before I started the camera bitmap manipulation.

To solve the issue I added the following to the image viewing activity:


/**
 * On the way out dump our images
 */
@Override
protected void onDestroy() {
  imageView.setOnTouchListener(null);
  imageView.setImageBitmap(null);
  System.gc();
  super.onDestroy();
}


Now after hitting the back button from the image activity I don't have a large weak reference to an image floating about in the heap. I have been able to view, take pictures, view, lather, rinse, repeat without any issues. I was running these test on a ASUS Transformer tablet running OS 3.2.1

I am also making sure I call recycle on the bitmaps I use during my scale / quality reduction routines during the post camera processing.

Probably a good idea to manually release any large images you use in your application as activities go away to avoid these problems. Right now I am not releasing all the smaller images I am using but it might be a good idea to make a sweep through the heap dump to see what else is sneaking around.

Not sure if the System.gc() call is even necessary and I know they recommend you don't call it but at this point I can't see it hurting anything and I am only doing it on activity destroy so I am leaving it in there for good luck and hoping I don't hear of any more OOM crashes in the field.

Wednesday, November 30, 2011

iOS Scope Bar for searches

I was cheating a bit for searching in our app. There are two columns in many of the tables - Code and Description. Most of the time the Doctors would search on the code but occasionally they want to search on description to find something like "liver abscess". To keep screen space at a premium I had the hint text tell you to type a leading SPACE to search by description. Of course that is not totally obvious and many don't read the text.

When I hit the tablet side of things I did two search fields on the Android like this:

Code: [           ]      Description: [                                   ]  [x]

You could fill in one or the other and I blank the one you are not using. Works nicely and there is plenty of horizontal space in either portrait or landscape to pull that off. Codes are small so that entry field eats up less horizontal space.

On the iOS side I am using the UISearchBar and it supports only one entry field. I was going to give up on doing the dual search on the iPad but then I discovered the scopeBar and associated methods on UISearchBar. You can set various button titles and that allows the user to toggle the type of search they are doing.

It works and I have converted the code to use it but there is a bit of oddness to it. First the toggle buttons always appear below the search bar. It would be nice to move the buttons to the right of the search bar on the iPad as I only have two buttons in most cases and plenty of screen space. Might as well show more search results. This makes for some very long toggle buttons on the iPad.

Second you can toggle the search mode then tap to type the text but once you do that the toggle buttons are disabled. You have to cancel the search, change the toggle button, restart typing your search if you entered in the wrong mode. Seems this is all one control so why disable part of it?

To save space on the iPhone it would be nice if the toggle buttons only appeared after you invoked the typing of search text. Right now they waste a lot of screen space on that small screen for the ability to toggle which is probably not used very often.

Finally there is only one keyboard mode for the entire search bar. I want it to be in numeric mode if they are doing a Code search and in alpha mode if doing a Description search but I can't do that. If my search involves Code / Description instead of Last Name / First Name I start in numeric mode but they have to manually toggle back to alpha mode to type description. I covered the 95% case here but wish I could cover 100% instead.

While initially I was happy with Apple for providing a generic search control I ended up frustrated due to its lack of flexibility. On the Android I started out thinking "Where is a generic search widget?" Implementing one was easy enough but I should not have needed to do that. I ended up with an exact layout for my needs and more flexibility in the end under Android. Typical programming issue that occurs when you are developing on multiple platforms.

Tuesday, November 29, 2011

Xcode is still crappy

I keep crashing Xcode. I have an M file open and intellisense pops up. I do the three finger swipe to switch to the H file and it crashes. I lose the work since my last save. This happens at least once a week. So stop doing that the Doctor says. I switch between my PC using Eclipse and Xcode on the MacBook Pro multiple times per day.

I don't remember crashing Eclipse in a long time. I just use the IDE and it works. Then I get on the Mac to convert over some Android code I just wrote and I forget it crashes and boom I crash it yet again. Would be nice for Apple to fix this one but I have doubts it will happen as Xcode does not get updated very often. Sure they tie in a new iOS release but the IDE itself does not change much.

Just rebooted the Mac after an update install and crash, did not notice update was ready until Xcode died. Time to go back over and see how much of my work it lost this time.

Wednesday, November 23, 2011

iOS 5.01 and WiFi - not happy

The iPad 2 I use for development and testing has been acting flaky. It is WiFi only and the connection comes at goes at will. I sit about 12' away from the wireless router. I had no issues at all until the 5.0x updates but since then it has been iffy at best. Searching the web and I found a lot of other people having the same issue. The Android tablet, also WiFi only, has had no issues.

On another Apple note the Java 1.6 update 29 caused us all sorts of memory leaks. Our clients could run our Java program on the Mac for about 15 minutes before running out of memory. No issues on the PC or Linux with update 29, just on the Mac. My boss dug into their bug fix and fixed their fix to release the objects properly and we are back in business. We checked in our overridden code to our project so our users are not hosed. Others on the web are complaining about this issue too.

I am also able to crash Xcode 4 at will. Edit M file, have intellisense / code complete / whatever they call it pop-up, three finger swipe to got to the H file and crash city. I have used the reporting tool to send this off to Apple. Happens to me at least once a week. They tend not to release fixes to Xcode unless they are doing a new iOS release so I bet this is not fixed in the near future.

Low hopes of the Java bug being fixed soon either. It has been a long time for a Java update - going from 26 to 29 - so I bet this hangs out there for a long time too. We don't find Apple to be all that developer friendly so you just suffer along and know how to work around things.

Winding down the round of mobile app development. I pretty much have the two in sync again. Pushing a gig of code on both sides. I added long press to do some default actions. My graphic artist / Mac buddies find most users have no idea a long press exists so I made sure there is a way to get to same functionality via another part of the UI. I just consider the long press a short cut but once you get used to it being there you really like it.

User beta feedback has been very positive. Almost no bug reports, just feature requests. Some of the features make sense and I have been able to quickly add them. Some are borderline insane so they are on a "do it later if ever" list.

I sat down and rethought the way I was doing favorites and got it working the same on both iOS and Android. Pretty happy with the final results and ready to see what the beta crowd thinks of it. I have also made some graphical tweaks to the new menu system to make it look better.

I cleaned up some bugs in the iToast code I got off the web to do Toast notifications on the iPhone. If I set the gravity to top or bottom it was not painting correctly in landscape mode - the mode I generally have the iPad in. I kind of cheated here. The Android Toast code handles multiple Toasts by queuing them up and showing them one at a time. The iOS version just painted them on top of each other. I was going to write the queue code and then found I could just set the screen position instead so I have the refresh message show up on the bottom of the screen and other messages in the middle. Avoids the overlap without a ton of work on my part. This only happens in one situation where you save a case and are coming back to the case list screen. One message is saying "Case Saved" and another "Case List Refreshed". No need to introduce complexity to solve one off needs.

More than ready for the Thanksgiving break. Thus all the polish work for this short week. Might as well leave it in a very stable state so I don't think about it over vacation. I ran the memory leak detection of Xcode and cleaned up all the issues I found. Always good to run that from time to time.

Wednesday, November 16, 2011

Android / iOS sync continues

I sure thought I would be done with the next version of our application by this time but we came up with a number of new features to add before it leaves the Beta stage. I have been adding support for favorites so the Dr. can toggle which CPT / ASA / Diagnosis and other fields quickly just using the ones they use the most.

One of the benefits of dual development is it forces you to look at problems from different angles. I had the favorites all set up on the Android side of things and the Beta clients for those devices have been using it and it appears liking it. What I did on the Android does not fit naturally on the iPhone as far as UI goes.

First off I had to take some time to write a new menu system for iOS to emulate the one I am using for Android. After getting past the initial scare of creating a full blown custom menu under iOS it went rather smoothly. My draft version proved I could do a menu that pops up over another view - including tables - but the graphics design did not pan out well on an iPhone, it looked fine on the iPad. I took a step back and redesigned the menu GUI so it worked great on both device form factors in both orientations. This is a universal binary so the same code is running on both devices.

Many tweaks later I have the menu system running in a lot of places in the UI. Without that I was not going to be able to pull of the favorites as designed. Here is where I ran into the next issue. I was using my menu in two ways on the Android to do favorites, one menu to select a mode and one menu to select which items to be in the favorites list. This would not play well under iOS. I also had allow the user to control the font size under iOS and its current UI left something to be desired. After taking a step back, many head scratches and many pen scratches on paper I came up with a new single menu system that is much cleaner and easier for the user to grasp and solved the font sizing issue too. I just finished phase one of my implementation on iOS and am pretty happy with it. Now I have to convert those ideas back over to the Android which technically should not be difficult but will be time consuming as I have to touch a lot of code.

Because I needed to make something work on the iPhone my Android users will have a better experience. This has happened in both directions as there are areas I wrote on the iPhone first and during my Android conversion I discovered better ways to accomplish the same thing. Some of it comes from just wanting to get something running but when you have to write the code a second time you think it through again and make it better. That has happened over and over during this process. I have deleted, modified and bug fixed a lot of code as I go back and forth between the two platforms. At times it feels like you are just spinning your wheels and will never get it all done. Heck it is not all done but I am in the home stretch.

I often wonder what it would be like if there was another developer working on iOS. Would we be able to come up with ideas together that work well on both devices? If you don't code on both do you have blinders on for the one you don't code under? Is it better for one developer to hash out an idea and get a working prototype and then show it to the other so they can shoot holes in it and make it better? You could have each developer tackle a non-overlapping area then swap ideas. Working on the same area at the same time, unless you are following a foregone UI pattern, seems a waste of effort.

There are a lot of "Oh man, I have to write this AGAIN!" moments when you are the sole mobile developer. At times you dread writing a tough hunk of code in another language using another IDE. Not sure if I am sold on the universal binary idea for iOS. Sure it beats a 2x version of your iPhone app on the iPad but Xcode / Interface Builder don't make is very easy to have NIB files per platform. It is easy to do on Android, just use same named XML files in different directories and everything is handled for you. I have a lot of IF checks for the iPad in the code already and am not looking forward to more of them. We might be getting close to the point of doing dual development there which would make my work load even more insane.

Tuesday, October 25, 2011

Fun with Android DatePicker and 1970

I was doing a new round of testing on our Android tablet. The application is used for medical information entry and one of the fields is date of birth. It has been working like a champ on the phones using 2.01, 2.2, 2.3 but on the tablet running 4.1 you could not enter anything before 1970. You could type in 1963 and it would set it back to 1970 and the [-] button would disappear.

I totally get why it stops at 1970 as that is the epoch year used for all sorts of things in computers. Obviously the Android team changed a default in later versions of the SDK. My phone, running 2.2, allows you to go back to 1900.

After some research I found a solution that works for all devices I have tested against. In my XML file I added android:startYear="1900" to my definition. If you look in the latest documentation they discuss android:minDate but that is only available in later version of the SDK.

Before I stumbled upon startYear I tried to use reflection to call the setMinDate(long) on the datepicker but I did not have any luck doing that. I am really happy that was not the solution. I would much rather use something in the XML that solves it on all platforms.

Of course after I did this SVN got confused about a directory already being locked and would not allow me to check in the change. I found Team -> Cleanup in Eclipse solved that issue.

Now that I have my new "set favorites" processing in place on the Android I need to convert all that work over to the iPhone / iPad. Research time as some of it just does not have a direct translation to iOS and while the menu system is possible on the iPad it is officially not supported on the iPhone so I am going to have to come up with a work around. The first menu system I found has caused apps to be rejected by Apple due to private method usage making it a non-option. Dual iOS / Android development has a lot of headaches.

Wednesday, October 19, 2011

ADT 14 breaks Eclipse 3.7 - FIXED

Just to make sure my week goes as smoothly as possible I installed ADT 14 / SDK 14 for the new Android 4.0 build and now I can't build anything on my Win7 64bit box.

First I installed the new SDK. Then I installed the new ADT after I got some errors. Now I just get an error that my project has errors but not a single window will tell me what the error happens to be. I installed the SDK from scratch and still have the same error. I installed a fresh version of  Eclipse 3.7, ADT 14 and SVN plug-ins and still have the same issue. I have run every update possible. Our bandwidth sucks right now so this is taking a really long time and I am getting really tired of experimenting. I will have to give this a try at home to see what happens there tonight.

I did cleans before I did builds and did the Android -> Fix Project menu item but nothing will get me past the error message that I have a project error but nothing will tell me what the error is. Not a single file, other than the main project, is marked as in error. I see others are having the same issue on the web. I looked in the project properties and nothing is showing up as in error there either.

At this point I am running Eclipse on my Mac as it has not been updated to 14. No idea what to do next. I just got out of the iTunes / Xcode / iOS 5.0 install hell. At least that is all working. Now I get to be really annoyed at Google / Android from screwing this up. Guess I can blow away all the version 14 stuff and go back to 13 to see what happens. Seems this is all a bit rushed.

Well not the fault of Eclipse or Google on this one. My developer certificate happened to expire at the exact same time I installed the update. Once I deleted the old certificate off my HD an let Eclipse build a new one all was fine. Somewhere in there showing errors in the Problem tab had gotten turned off - it was only showing warnings thus it looked like nothing was wrong. What a huge waste of time and energy it took to figure all of this out. I wish the error message would show your the current error list instead of just saying "there is an error, good luck finding it!"

Friday, October 14, 2011

Pains of iOS 5.0 from a developer perceptive

We needed to test our app under iOS 5.0 so I began that process yesterday.

Step 1: Download and install new iTunes
Success - no issues

Step 2: Connect iTouch Rev 4, install iOS 5.0
Success - but blew away everything on device. I only use it to test, not a big deal.

Step 3: Install and run app on iTouch
Failure - Xcode does not know about iOS 5.0 devices so it refused to install. I find this to be stupid as the target of device of the code is 3.0.

Step 4: Download and install Xcode 4.2 for Snow Leopard
Success / Failure - Takes a long time to get it but it installed. Install works but it blows away all SDK version prior to 4.0. I can't test with a 3.2 simulator at this point. Xcode 4.2 has some new warnings that are valid. I fix those issues in the code while I am fixing the iOS 5.0 issue.

Step 5: Install app on iTouch
Success - it now will install

Step 6: Test app on iTouch
Failure - it crashes because some really old code is using NSIndexPath passed to didSelectRowAtIndex path call later. I need to copy and retain the data to solve the issue. Never a good programming idea in Objective C to think any variable passed to you will hang around. No other version of iOS cared about this particular one. Could be they fixed some things to get ready for dual core processing or other threading issues.

Step 7: Check in changes
Failure - Xcode "sees" SVN for a brief second then gets lost. Use IntelliJ AppCode to check in changes. Find out I need put the URL to our instance of SVN into Safari. Safari bitches about certificate but I can tell it to stop bitching then Xcode starts to work. Xcode SVN interaction has always been spotty at best and again it bites me in the behind.

Step 8: Archive for app submission
Failure - Xcode can't find my deployment provisioning profile. Delete profiles, refresh, rebuild,  restart Xcode, pray, etc. Something finally works and I can set the deployment area to the proper profile. Archive menu item is disabled. Search Google and remember that you must select iOS Device as target for this to be enabled. Remember how stupid that is from the last time this happened to me so many months ago.

Step 9: Validate
Failure - Message tells me it can't find the app in iConnect. Google the message and see that I have to have the app in "waiting to upload" state before it will validate it. Next step will be for the boss to do that so I can try again.

Step 10: Test on iTouch without iOS 5.0
Failure - By default Apple has decided that anyone not running on a armv7 device should just throw the worthless piece of crap in the trash and give them more money. Connect device, press RUN and all you get is a message that it finished running on the device even though it did not install or run. No error, no console, no help - thanks Apple. You have to open up the build settings and add armv6 back into the supported architectures then it will run on the device just fine. If you only test with the simulators you would not find this issue as the simulator is i386 architecture and I was able to run all the way back to 4.0 using that. After installing Xcode 4.2 you would be hosed unless you tested on physical devices.

Step 11: Submit to store
Success - Once the boss got the app in "ready to submit" mode I was able to validate and submit it without any issues. The error message was stupid. We also put up new screen shots for both the iPhone and iPad.

Step 12: Released to market
Success - It only took Apple two days. I was expecting 10. Kudos to Apple for cranking this one out. Last time it took 7 days and I figured they would be swamped. We did put in the notes that it was an iOS 5.0 fix being released. Between that and small download count I am sure they just pushed it through figuring if we screwed the pooch on this one it would come back to haunt our users and us but not really Apple.

A huge waste of time this has been. What you need to do is not obvious. The new updates from Apple are not friendly and blow away or break things. Apple is rather developer hostile. I need to get the app submitted to the market as it is broken under iOS (our fault, not Apples fault). With a rash of people submitting to Apple right now for iOS 5.0 who knows how long I will be in the queue. All our current users who upgrade will be hosed. This is a huge failing of the Apple Store. I will not be able to get a couple of line fix out to our customers in a timely manner.

Xcode continues to be a very annoying IDE. If this is what Apple considers a good UI and user friendly then I don't want to know what they are like when they are mad at me.

Thursday, October 6, 2011

Released Android Version


We put the new Android version on the market yesterday. Should have been a really simple process and it started out to be so but one little piece screwed us over for a bit. I did the export in Eclipse and after we typed in the two passwords we have the APK file ready to roll. Logged into the publish side of the Market and uploaded the APK then went to change to screen shots and what not. Things did not work. I kept getting an error and was told to try again later. Later never seemed to be late enough.

I read that Chrome was having issues with the new market. Go figure, the browser from the company that owns the Market does not work. We tried I.E. figuring it had to work but it had the same issue. FireFox worked without a hitch. What the hell are they doing that only works in FireFox? Very annoying but it has been submitted and our various devices were notified there was an update so we know it is out there and working.

Our Beta test Doctor now has access. We have to flip on a bit on our server for them to be able to see the new functionality so everyone else will see some minor color scheme and little tweaks but they will not have access to the new functionality until we are ready to turn it on.

Of course I found an issue already. Not a crash but one of the tables was not loading all the data it needed as I only asked for Type 1 data and I needed Type 1 and Type 3 data in the table. Pretty simple fix and there is a good chance the Doctor will not need this. I can easily release an update if he does need it for this testing.

I also did some UI tweaks while I was in there. Some buttons needed to be larger to give them a better look and to make them an easier tap target. I took the iPad home last weekend to give it a solid once over but I have not taken the ASUS Android Tablet home yet to run it as a user out the the interruption driven office setting. I did have a little time to play with it today so I of course found things to adjust.

Next up is getting the iOS side ready to submit. I had to make the same table request changes on that side along with a few other tweaks. I got a note from Apple telling me I can get the Gold Master of the SDK and the updated Xcode. I really don't want to do that as it is a 4.6g download then I will just have to get the real release in a little over a week. If it was a patch that would be one thing but it is an overnight download so I am not going to do it multiple times. I have fallen in that trap once before.

The problem is I did the archive and tried to validate but it is annoyed in some manner. So I have to figure that out. Could be I changed the name of something and it does not think I am updating and existing application.

I decided to check out what changed in iOS 5 that may affect our app. I log in to the Apple Developer Network and it does not think I am a developer so it will not let me see anything. I ask my boss to check out the corporate account, which Apple has screwed up many a time, and it needed him to agree to a new terms of use. After that I was able to see the iOS 5 information on the site but I still can not validate out app.

From what I can tell we might be affected by some iOS 5 changes. It reads that way at first glance but until I am able to run iOS 5 in the simulator or upgrade one of our devices - probably the iPad 2 - I will not know for sure.

The sales guy stopped by to have the latest version put on his iPhone for a demo tomorrow. Luckily his phone has already been provisioned so that was easy. I also put the latest version in the iPad 2 and gave that to him as it is much easier to demo on a tablet. Will be interesting to hear what the clients have to say about the application.

Tuesday, September 27, 2011

Converting a phone app to Android tablets and the iPad

Just as I thought I was winding down my Android and iPhone development the Doctors tell us they have bought iPads and Android Tablets to use to enter all the data. Can't blame them as it is much easier to enter data on a tablet. The problem is we were going to target those devices on the next release. Time to quickly switch gears.

The app ran fine on the Xoom but the home screen needed to scale better. I searched the web and found new graphics for that area. With more room using larger fonts also seemed the way to go so I tweaked a few other XML files. I ended up creating a new values-xlarge directory under the res directory to hold a styles.xml file for the tablet. I find the way Android handles this to be super easy. I had to go back into a few of my other XML files to make sure they were using the named styles but that was all. I only have a few files in my layout-xlarge and layout-xlarge-land directories to handle all the changes I wanted to make on the Android Tablet.

Android does handle your app getting kicked out out memory in a way that screwed with our app. We have some global static data (I know wrist slap in order) but I need it for the SQLite database and some other login credentials. When you get booted from memory the OS tries to run your onCreate method of the visible view again. For most apps this would be great, you just pretend you started fresh and go. But with the global variables this is not so fun. I added enough checks that I don't [Force Close] any more but tell the user that the session has expired and they need to login in again. I kick them back to the login screen to get going with a fresh database and session variables.

Initially testing this case was a royal pain. You needed to leave your phone on overnight or run so many apps that it got kicked out of memory. Of course that is stupid and a few internet searches pointed me in the right direction. Fire up the emulator, run your app, use "adb shell" from the command line then "ps" then "kill #id" of your process. This will cause same sequence of events to occur. I brought up each possible activity / view and did that over and over until they all exiting clean to the login screen. You can't do this when running on a device but you can use the DDMS view in Eclipse and hit the red [stop] button to achieve the same results.

Once you start running on a real device, especially a dual core tablet, you hate to go back to the emulator. Of course QA came and took the tablet a few days after I had it in my hot little hands so I am still missing it. So much nicer to type on it and code changes are sent over to it and are up and running in a blink of an eye.

Now for my iPad experience. You can set up a universal binary in Xcode. Once you do that it will automatically switch you to the latest version of iOS as your target which is not a good thing. I wanted to target 3.0x as the minimum iOS version. Since Xcode is smarter than me things quit working on my iTouch until I figured out what it did. Simple enough to change that back.

Initial runs on the iPad looked like initial Android tablet runs. Most things looked pretty good as I was using list views and text editors. The home screen looked crappy though. I was able to use my @2x images for the retina display on the iPad. I used the stretchableImageWIhtLeftCapWidth to simulate the nine-patch image I was using for the shelves on the Android.

topShelf = [[UIImageView alloc] initWithImage:[shelf stretchableImageWithLeftCapWidth:25 topCapHeight:0]];

Worked like a charm and was much easier than I assumed it would be. I had to put in manual positioning code for each of the icons on the shelves for the iPad but there are only 6 images so that was easy enough.

I am adjusting for the height of the status bar in my code too. On the iPhone the status bar height is different between portrait and landscape but it is one height on the iPad. I put code in place to handle those special situations and everything looks pretty darn nice.

Since I have more title bar room I wanted to show the practice name on the home screen. Of course that changed the "back" button on any screen that launched from the home screen which is not what I wanted. Turns out you can do this:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {

    self.navigationItem.title = [NSString stringWithFormat:@"Home - %@", [RESTCaller practiceName]];
    UIBarButtonItem *backButton = [[[UIBarButtonItem alloc] initWithTitle:@"Home" style:UIBarButtonItemStylePlain target:nil action:nil] autorelease];
    [self.navigationItem setBackBarButtonItem:backButton];
}

That code allows you to set the title text to one thing but the back button used by all other areas to something else. Not the way my mind thinks but I understand the logic behind it now that it is in place.

I want to increase the font size on various screen on the iPad. Since there is no real layout manager for iOS, everything is hard coded for widths / heights in the XIB file, this is a royal pain. You can either do secondary XIB files where you have to tie everything exactly back to the code via drag and drop or you can try and tweak things in code playing with bounds and what not. Both are sorry excuses for layout editing. I have made slow progress in this area but find the amount of work involved to be stupid.

The iPad cannot use the same call to show the picture gallery as the iPhone. I had to update the code to make the new call to show a pop-up image picker instead of a full screen gallery picker. I hope that is the only API I am using that they changed on me. Rough one to find, good thing we have QA to beat on it.

UIActivitySheets don't exactly work the same on the iPad either. They show up nice and centered but if you rotate the device while you are displaying one it will not stay centered. It stays relative to its original position. Looks ugly but this will be a really rare case so I did not fix it. There are some suggestions on the web as to how to fix it but nothing worked for me. The only thing left to consider is killing the sheet when you get a rotation notice and recreating it so it shows up in new orientation centered again. Of course if you do that really quickly you will have a new set of issues. Not worth it at this point. I see Apple cheated in this respect. Long tap a link on a web page. It pops up a menu. Rotate the screen. The Screen will not rotate until you dismiss the menu. Not what I would call a particularly user friendly way of doing things.

Camera support on iOS devices is totally hit or miss. If you want to determine if a camera is there you need to use iOS 3.2 and above calls. Seems Apple is not very forward thinking on these things and a lot of stuff gets added in later releases but I can't trust a user to be up to date. Instead I am using a call to get the machine name and basing the availability of a camera off of that. Crappy way to do things but it is working for me so far. I know iPad v1 does not have a camera and you have to be up to iPod (the iTouch) version 4 before you have a camera. The simulator never has a camera which sucks. On the Android side it simulates the camera by showing interface then returning a stock image. I had to test camera usage on an actual iOS device and I have to make sure it has a camera otherwise you get a nice crash. I don't even want to show the [Take picture with Camera] button in the UI if you don't have one.

Which brings me to the another WTF? for Apple. Why are Action Sheets index based? If I remove a button from my Action Sheet it shifts the index of all the buttons below that. Hard to write generic code and you always have to edit two places for each action sheet change you make - once where you create the buttons on once where you respond to their presses. Each button should call its own method or at least you should be able to assign tags to them so you can have one case statement. Honestly assigning tags to UI items seems pretty stinking crappy too. I do it where I can to keep track of things but it is not the most object oriented way of programming.

Some of my buttons had the dreaded "..." on them depending on what I ran it on. No device fragmentation here boys, move along. They were working find on the iPad 2 and both iTouch devices but not on the iPad 1 or an iPhone with an older version of iOS. I had to shorten the text on all devices to make sure it fits.

I have no idea if the Doctors will keep their phones up to date. At this point - pre iOS 5 - there are not OTA updates. If a Doctor never connects his iOS device back to a PC running iTunes he will have no idea there is an update out there. Will be interesting to see what iOS versions we have connecting once we roll this thing out into the wild. I have tried to test on as many iOS flavors as possible and each time I seem to find a new issue.

One other big area I can into was the 1 meg blob limit of SQLite. We are taking pictures with the built in cameras and they can easily be over 1 meg on an Android. The lackluster camera on the iPad and the iTouch don't get you into trouble but an iPhone can. I use JPEG compression to shrink the image down as needed to avoid SQLite issues. It is a shame the iPad camera is so limiting in resolution. It is an unusable solution for our doctors, the images of sheets of medical paperwork are not readable with that camera. We also allow you to choose images from the device gallery which could easily be from another device and over the 1 meg limit.

Overall it was not very difficult to get a universal app running on either the Android or iOS platforms. We are not taking full advantage of the tablet form factor but at least we are not just doing a 2X version of our iPhone app, the images scale up nicely without being blurry, you get to utilize the extra space of the device and things look native in general. 

QA is doing final testing then it will be off to the beta clients. It will be really interesting to get their feedback. Now I just need a little time to destress and I get ready to head back into Java + Swing + MigCalendar land to fix some issues in our appointment application.

Wednesday, September 14, 2011

Which way is up?

Do you ever have days where you can't even begin to tell which way is up? I have been having that feeling for the past week at least.

I am winding down the Android and iPhone development so it is at least ready to go to beta. We have a doctor that is ready to test it. We thought he was going to do it on phones and 7" tablets but he bought 10" tablets instead. I needed to add an xlarge portrait and landscape version of the home screen to the layout directory. Not a big deal but the button images need to be 256x256 to not look stupid small on the screen. My original images are 128x128 and the phone version I use 100x100. Any time you scale up things look crappy but I don't have time right now to find new base images or to totally pretty up the ones I have. I ran the sharpen tool in Paint.NET so they look acceptable.

QA has not had enough time to look it all over either. The main QA person was hit by a car while riding her bike to work. Bike is total but she is doing OK and is now back at work but under the loopy state of pain meds due to a fractured hip. To toss in irony the driver of the car is also a cyclist who has been hit by a car. Pay it forward revenge style it appears.

I think the app is pretty solid as I have run it through its paces a number of times. There are only so many screens and so many data types meaning a lot of the code is hit over and over as you create a case. I installed it a Xoom today and it ran perfectly. I have run it in the emulator but not on the actual device. The big worry is connectivity. If it drops in the middle of some areas it could be bad. Something you think about from a desktop but generally things work but on a phone walking through a hospital could cause the connection to drop easily.

I have researched the universal iPhone / iPad build setup for Xcode and I really want to take that route before we ship. Currently the iPad would just show a 2X iPhone version and I think that would be crappy. It appears Apple is using something similar to Android where you can name your XIB files with a ~ and have an iPad layout without code changes. You do have to tie all the controls to the extra XIB file but that should not be too bad. I will need better resolution images here too. Only the home screen with the buttons that launch you off to other areas should be affected but since I have not tried doing anything native iPad I would guess there are some other gotchas.

I am working on our scheduler written in Java using MigCalendar while I await QA and beta feedback on the mobile side. I have never used MigCalendar before so I have to learn those controls first. It has been a tough row to hoe. The MigCalendar API is not straight forward. Lots of parameters to lots of methods and it is not taking advantage of generics and enumerations as much as it could. The documentation is sparse and on-line help is limited. It seems to be very powerful but not super easy to understand. I have some of the requested enhancements in place I need like a tree control going more than 2 levels deep and multi-line header control for those instances. I now understand why our code was artificially limiting things to this depth as it had categories under categories that are hidden from the user as we filter 3 ways (ugly GUI here) - one from a tab, one from a combobox and one from the tree control. I need to clean all of this up without breaking how existing people use it.

I started with a test application just to learn the basics of the calendar controls. Once I got the header doing what I wanted I moved into our real code and started stripping things out and redoing what is created for the CategoryDepository which happens to be a singleton but not in a good way. This lead me to figure out why the old code that was executed every time any little thing happened in the UI did what it did. Of course nearly everything is broken now. I may have to abandon my changes due to the next item on the list.

Our C# developer has left the building but we need updates the the C# appointment scheduler. I know C# too having written a number of utilities in it and doing a conversion of a real-time stock market program from Java to C# a number of years back. Nothing like doing Java + Swing, Java + Android SDK, Objective C + iOS SDK and C# + WinForms at the same time.

There are 54 open issues on the C# scheduler and customers want them now. I have to get a handle on the code first. Initial glance shows a strong preference to cut and paste versus make a method, lack of any real comments and hard coded SQL strings that repeat every column name in every command instead of a nice collection of column names and the commands being built. You have to search a lot of code if you need to add a new column to a table and make sure everything works around it as the code stands. I can look at the code but not build and run it as it uses a couple of third party control packages and we need to transfer the developer license from the departed developers machine to mine once we sort them all out. He was not much on source control or documenting what he did so it has been a trial by fire.

I will be in another situation where I am using third party controls I don't have experience with just like I am doing with MigCalendar under Java. Mind set switching between Java and C# has been pretty easy in the past. There are some really nice things in C# and some annoyances. Visual Studio is a bit lacking compared to IntelliJ and Eclipse but is generally better than Xcode.

Yep another IDE to use. The Android side is Eclipse, Java + Swing is IntelliJ, Objective C is Xcode and now I get to use Visual Studio too! Different hot keys abound. I can easily be in all four of them in the same day with 3 running on a single machine. Keeps food on the table and clothes on the kids but my mind is turning to mush as keeping things straight during the context switches of the day can really wear you out. Nothing ever seems to be totally done and the list of open tabs I have in PSPad keeps growing. I have a tab with project notes for each item I am working on to keep track of what is left to do and my random thoughts on areas that could be improved. I jot stupid stuff on paper too during the day but try to keep a digital version of my thoughts so I don't forget things I need to revisit when I am in the code or to keep track of what is implemented on the Android that I have not ported to the iPhone or what has not gone in the opposite direction.

Wednesday, August 24, 2011

Android and iPhone apps in sync - here are the code results

I sent the Android version of my app off to QA yesterday. Both the iPhone and Android versions are now in sync and here are some code stats.

*.m   100 files   589,431 bytes
*.h   109 files   123,079 bytes
      =========   =============
      209 files   712,510 bytes


*.java 97 files   633,177 bytes

A lot more comments in the Java code as I have JavaDoc comments over each method but even with that the code base is a lot smaller and a lot less files. Anytime you have less code and less files it is easier to maintain and will tend to have less bugs.

I was surprised at how quickly the conversion went. A couple of things play into that, first I started on the more difficult of the two platforms - the iPhone. It is more difficult due to my experience with Objective C and the Mac in general. Some things are easier on the iPhone such as only having one screen size to deal with. Second once you have the database schema in place and the logical layout of the program ready to roll you are doing more straight coding and less designing.

Doing double development has some benefits. There are things on each platform that are pretty easy to do. They force you to look for a solution on the other platform. I don't want either version to outshine the other. The end user should be able to pick up any device with the app installed and be able to use it. There are differences in button placement - iPhone on bottom, Android on top - and the iPhone always has the "Back" operation as a button on screen where Android users know to press the dedicated back button on the device.

Programming differences

  • I found programming for SQLite much simpler on the Android. I pretty much had to build SQL statements by scratch on the iPhone and pass them off to the database. The Android helper methods make this much easier. Android made it super simple to handle cursor data in a scrolling list. All of that had to be written manually under iOS. 
  • I had to write the multi-part entity code to send images to the server on the iPhone where I was able to use the Apache libraries under Android.
  • Hiding buttons on a toolbar is as simple as setting visibility state on Android. Under iOS I had to totally recreate the toolbar for each unique button layout. Some views are reused for editing vs. viewing of the same data. I needed to hide buttons such as [Save] in the read-only version.
  • The home screen with icons laid out in 2x3 for portrait and 3x2 for landscape was easier to do on Android as I just defined two XML layouts and put them in the proper directories. All of this had to be done in code on the iPhone although I was able to use the animation framework to have a cool looking transition under iOS that I did not port to the Android.
  • Handling JPG images was easier on iOS. Pretty simple access to camera, gallery and image viewer. It was also simple to get to the raw data to store in blobs under SQLite.
  • Doing background / busy threads is much easier under Android. On the iPhone you have to control everything, shut off the UI, get the spinning gear going, dimming screen, etc. On Android I just put the busy work in an async activity and invoked it.
  • Date manipulation is much easier in Java. The method names make sense  startDate.after(endDate) where it is [startDate timeIntervalSinceDate:endDate] > 0 on iOS. 
  • Settings seconds to zero startData.set(Calendar.SECONDS, 0);  for Android and    NSTimeInterval time = floor([startDate timeIntervalSinceReferenceDate] / 60.0) * 60.0;  startDate = [NSDate dateWithTimeIntervalSinceReferenceDate:time]; for iOS
  • iOS wins for speed of simulator (much faster than actual device) and speed when running in debugger but Android wins for actually being able to see variable values, do ad hoc expression evaluation while in debugger and separating output log messages into tabs in the LogCat output window.
  • Android wins for being able to run on a multitude of devices via one code base. I have run it on various phones, the older Samsung Galaxy Tablet and a Motorola Xoom. The iOS version is for the iTouch and iPhone. I can run it on the iPad but only as a double pixel app. Some call it fragmentation but with very few program tweaks having the ability to run on a variety of hardware is just fine with me. Heck I do that with PC programs all the time. I rarely know the monitor resolution up front. Use the space you are given.
  • Easier to view the SQLite database under iOS. I used the SQLite Manger for Firefox on the Mac and was able to point to the DB in the simulator directory and look at all my tables with ease. On the Android side I had to export the DB from the device to a temp directory and use SQLite Database Browser to see the table contents. 
  • Releasing to the clients is a huge win for Android. I can just put it on any device I want. I can submit it to Market and have it available in minutes and the same for updates. For the iPhone I can submit it and wait until Apple approves it. I can not give our clients a solid date as to when that will happen. Same thing for bug fixes. This means our Beta cycle will be pretty simple on the Android and a royal pain on the iPhone. Our clients are not local, I can't just have them stop by for new version. Doing it ad hoc might be possible if we can get a doctor to understand iTunes and how to drag and drop things I send them along with getting their unique phone ID to me so I can provision it and build it into the app. This is a gigantic pain.
No matter which device I was programming against I spent a lot of time in Stack Overflow looking for answers. As I have gone back to the iPhone side to do some program tweaks and add missing features I put into the Android side I again realize how much Objective C and I don't think alike. I still have to look up method names as they just don't roll off my brain. Trying to think how many staring '[' I need before I get to type code just seems silly. Having to type multiple lines to do simple things gets old.

I really don't care for Xcode, I really want tabs that keep open the code I am looking at instead of reusing themselves as they see fit.

Even though I was used to it with C/C++ I really don't like having two files for every object - the H and M files. I just want to add a method and have it there to use instead of adding it twice. Then you have the fun of static variables you want others to access which is tons of lines of code.

It will be interesting to see how the users respond to the application. Will any switch between devices and will they spot differences they don't like between them?

The application is used by anesthesiologist to do charge capture and billing. You have to be a client of ours for it to be of any use to you. All you could see without that access is our login screen so I can't give a link the program for anyone else to give me feedback on why I am insane to prefer Android over iPhone or to tell me where I totally hosed up doing something on either platform.