Tuesday, July 23, 2013

Real World Testing


Saya is greatly liked by real world people who have sampled it. Once logged in and inside the app, everything is easy and straight forward to use. However, there are some pain points that pose a real threat to loosing out on users:
  1. Registration process – This is by far the most painful part of the app. It's tedious and some users do not understand why it's necessary(coupled with the instinctive feeling of avoiding to give out their phone numbers aimlessly). Most users are confused about the applicability of the received SMS and what to do with it(some I observed reply to that SMS with the code :) . All in all, the general verdict was that a way should be found that takes away registration from the app. *Possible solution illuminated below
  2. Notifications – On new apps there are 2 notifications. On the 2nd one “You currently have no friends. Please Invite or Sync.”, most users gloss over this and choose their required action(*Reading phonebook past 215 contacts seems to be a problem—troubleshooting still continuing). The first notification “Saya may collect device info to enhance experience.” causes relatively more confused than the former. Users have been observed to select the “Read more” option more than “Dismiss”. Good news however is that all this options have a good 'dead end' and the user cannot possibly stray out of the flow.

*Possible Solution to the 1st problem
We can provide an online portal where the user handles the authentication there. I.E
a) Ask for user number
b) Send user code SMS
c) Ask user for the code to authenticate it's his number
d) Send link of prepackaged app to his number

Concentrating on the last option(d), the app can be prepackaged in a number of ways
  1. Recompile on the fly with the number hardwired in the app. This unfortunately is not possible for a signed app, otherwise you would have to sign the app every time it's recompiled.
  2. Add the user number as a field in the JAD file.

Wednesday, July 17, 2013

The History Of Ctrl + Alt + Delete


code1

In the spring of 1981, David Bradley was part of a select team working from a nondescript office building in Boca Raton, Fla. His task: to help build IBM’s new personal computer. Because Apple and RadioShack were already selling small stand-alone computers, the project (code name: Acorn) was a rush job. Instead of the typical three- to five-year turnaround, Acorn had to be completed in a single year.

One of the programmers’ pet peeves was that whenever the computer encountered a coding glitch, they had to manually restart the entire system. Turning the machine back on automatically initiated a series of memory tests, which stole valuable time. “Some days, you’d be rebooting every five minutes as you searched for the problem,” Bradley says. The tedious tests made the coders want to pull their hair out.

So Bradley created a keyboard shortcut that triggered a system reset without the memory tests. He never dreamed that the simple fix would make him a programming hero, someone who’d someday be hounded to autograph keyboards at conferences. And he didn’t foresee the command becoming such an integral part of the user experience.

Bradley joined IBM as a programmer in 1975. By 1978, he was working on the Datamaster, the company’s early, flawed attempt at a PC. It was an exciting time—computers were starting to become more accessible, and Bradley had a chance to help popularize them.

In September 1980, he became the 12th of 12 engineers picked to work on Acorn. The close-knit team was whisked away from IBM’s New York headquarters. “We had very little interference,” Bradley says. “We got to do the design essentially starting with a blank sheet of paper.”

Bradley worked on everything from writing input/output programs to troubleshooting wire-wrap boards. Five months into the project, he created ctrl+alt+del. The task was just another item to tick off his to-do list. “It was five minutes, 10 minutes of activity, and then I moved on to the next of the 100 things that needed to get done,” he says. Bradley chose the keys by location—with the del key across the keyboard from the other two, it seemed unlikely that all three would be accidentally pressed at the same time. Bradley never intended to make the shortcut available to customers, nor did he expect it to enter the pop lexicon. It was meant for him and his fellow coders, for whom every second counted.

The team managed to finish Acorn on schedule. In the fall of 1981, the IBM PC hit shelves—a homely gray box beneath a monitor that spit out green lines of type. Marketing experts predicted that the company would sell a modest 241,683 units in the first five years; company execs thought that estimate was too optimistic. They were all wrong. IBM PC sales would reach into the millions, with people of all ages using the machines to play games, edit documents, and crunch numbers. Computing would never be the same.
code2

And yet, few of these consumers were aware of Bradley’s shortcut quietly lingering in their machines. It wasn’t until the early 1990s, when Microsoft’s Windows took off, that the shortcut came to prominence. As PCs all over the country crashed and the infamous “blue screen of death” plagued Windows users, a quick fix spread from friend to friend: ctrl+alt+del. Suddenly, Bradley’s little code was a big deal. Journalists hailed “the three-finger salute” as a saving grace for PC owners—a population that kept growing.

In 2001, hundreds of people packed into the San Jose Tech Museum of Innovation to commemorate the 20th anniversary of the IBM PC. In two decades, the company had moved more than 500 million PCs worldwide. After dinner, industry luminaries, including Microsoft chairman Bill Gates, sat down for a panel discussion. But the first question didn’t go to Gates; it went to David Bradley. The programmer, who has always been surprised by how popular those five minutes spent creating ctrl+alt+del made him, was quick to deflect the glory.

“I have to share the credit,” Bradley joked. “I may have invented it, but I think Bill made it famous.”

Tuesday, July 16, 2013

Another Pesky issue

Been working on monitoring 'Connection Lost' and having opted to go with 'Heartbeat' method( http://stackoverflow.com/a/486695/1048839 ), I stumbled upon a pesky problem that was stubborn enough to have Mawuli convince me to reboot my laptop(1st time in 2 months). Even then, the problem persisted.

To handle new app updates, the app listens for updated from the server and acts accordingly.



  • public void handleNotify(JSONObject json) throws JSONException{      
            JSONObject json_obj = json.getJSONObject("result");        //Check for update variable        if(json_obj.getString("type").equals("update")){System.out.println("Update Found for..."+app_version);            String[] latest_version = split(json_obj.getString("ver")".");            String[] current_version = split(app_version, ".");          
                //Assign new verion number to app_version variable to avoid future            //checks during current session            app_version = json_obj.getString("ver");            //Compare versions and enable update download            if(!latest_version[0].equals(current_version[0])){                //Force download update                cmd_update = new Command("Update", Command.OK0);                cmd_exit = new Command("Exit", Command.EXIT1);                Alert alert = new Alert("Update""New Update available.\nPlease Download"null, AlertType.CONFIRMATION);                alert.addCommand(cmd_update);                alert.addCommand(cmd_exit);                alert.setCommandListener(this);             
                    Display.getDisplay(this).setCurrent(alert);             
                    //Sleep midlet                try {                  
                        Thread.sleep(100000);                    //Force App close                    instance.notifyDestroyed();                } catch (InterruptedException ex) {                    ex.printStackTrace();                }            }else if(!latest_version[1].equals(current_version[1])){                //Ask to update                cmd_update = new Command("Update", Command.OK0);              

                    Alert alert = new Alert("Update""New Update available.\nPlease Download"null, AlertType.CONFIRMATION);                alert.addCommand(cmd_update);              
                    alert.setCommandListener(this);             
                    Display.getDisplay(this).setCurrent(alert);                             
                    //Sleep midlet                try {                    for(int i=10; i>0; i--){                      
                            alert.setString("["+Integer.toString(i) +"] "+"New Update available.\nPlease Download");                        Thread.sleep(1000);                    } 
                     
                    } catch (InterruptedException ex) {                    ex.printStackTrace();                }            }else if(!latest_version[2].equals(current_version[2])){                //Ask to update                cmd_update = new Command("Update", Command.OK0);              

                    Alert alert = new Alert("Update""New Update available.\nPlease Download"null, AlertType.CONFIRMATION);              
                    alert.addCommand(cmd_update);             
                    alert.setCommandListener(this);             
                    Display.getDisplay(this).setCurrent(alert);             
                    //Sleep midlet                try {                    for(int i=10; i>0; i--){                      
                            alert.setString("["+Integer.toString(i) +"] "+"New Update available.\nPlease Download");                        Thread.sleep(1000);                    }                                    
                    } catch (InterruptedException ex) {                    ex.printStackTrace();                }            }          
             
            }     
         
        }//--End of handleNotify(JSONObject);
  • The problems occurs in the small sleeping thread for the last 2 options as it counts from 10 down to one. On the 3rd count(i.e no.7), it fires the cmd_update by itself and therefore tries to execute an update. The solution, weirdly enough, I found was to add another command to the alert and that stopped cmd_update from firing.

    //Ask to update
                    cmd_update = new Command("Update", Command.OK, 0);
                    cmd_exit = new Command("Exit", Command.EXIT, 1);
                    Alert alert = new Alert("Update", "New Update available.\nPlease Download", null, AlertType.CONFIRMATION);              
                    alert.addCommand(cmd_update);
                    alert.addCommand(cmd_exit);
                   
                    alert.setCommandListener(this);
                   
                    Display.getDisplay(this).setCurrent(alert);

    Wednesday, July 10, 2013

    Might have lost the battle but the war is still on

    36 hours later and I still can't figure out what causes "Unhandled Exception" just after handling notify for app update(handleNotify()).

    So I decided to yield(for now) and re-write my code around the problem. Effectively, updates will only be handled for a registered app. They will not he handled for the first session of the app where registration is carried. They will only be handled for subsequent app sessions thereafter. See attached code.


    //Check for updates
            try {
                JSONObject json = new JSONObject(aPacket.getUTF8());
                if(json.getString("mod").equals("notify")){
                    try {
                        //Check for bearer to determine if app is logged in
                        if(packet_handler.getBearer() != null){
                            handleNotify(json);
                        }
                    } catch (RecordStoreException ex) {
                        ex.printStackTrace();
                    }
                }
            } catch (JSONException ex) {
                ex.printStackTrace();
            }

    P/S: Update is handled according to Chinedu's prescribed handling techniques i.e for version x.y.z
    where..
    x denotes major release hence force an update(no grace. App timeout&exit after 100 secs)
    y denotes a feature release hence ask user to update(10 sec grace applied on screen)
    z denotes a bug fix hence ask user to update(10 sec grace applied on screen)

    Tuesday, July 9, 2013

    Handling dead URLs

    Came across this problem when handling an improbable dead url; http://api.saya.im/locbyip which is the Location By IP API. Because I always assumed it will give back something, I failed to handle the odd case of the url being dead.

    This can be done with a small one-liner addition
    throw new IOException("Response:"+http.getResponseCode()+", "+http.getResponseMessage());

    Here's the full code of the openUrl method


    public String openUrl(String url) throws IOException, SecurityException{
            byte[] data = null;      
                   
            HttpConnection http = (HttpConnection)Connector.open(url, Connector.READ_WRITE, true);
           
            //set custom headers
            http.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.1");
            http.setRequestProperty("Accept_Language", "en-US");
                           
            //Read response
            if(http.getResponseCode() == HttpConnection.HTTP_OK){
                int len = (int)http.getLength();
                is = http.openInputStream();
                if(is == null){
                    throw new IOException("Cannot open HTTP InputStream, Aborting..");
                }
                //Else continue reading stream
                response = "";
                Reader in = new InputStreamReader(is);
                StringBuffer temp = new StringBuffer(1024);
                char[] buffer = new char[1024];
                int read;
                while((read = in.read(buffer, 0, buffer.length)) != -1){
                    temp.append(buffer, 0, read);
                }
                response = temp.toString();
            }else{
                System.out.println("Response:"+http.getResponseCode()+", "+http.getResponseMessage());
                response = null;
               
                throw new IOException("Response:"+http.getResponseCode()+", "+http.getResponseMessage());          
            }
            if(http != null){
                http.close();
            }
            if(is != null){
                is.close();
            }
           
            return response;
           
        }//--End of openUrl()