Friday, December 30, 2011

Hello World - Spring 3.0 STS

This is to lay out the very bare details needed to create a "Hello Word" application using Spring 3.0 STS.

I actually had quite a bit of difficulty locating a tutorial on how to do it. Unfortunately, the flagship screencast at the site shows the source code for a project that exercises various aspects of - but doesn't show how to set up your own project. This leaves you struggling a bit for tutorials that tell you how to actually work the controls of this monster.

Almost by accident, I stumbled across a couple of tutorials which were enough to get me started. The first one, which I've unfortunately lost, basically showed how to run the server and what the default source code looked like. Unfortunately, it didn't work. But at least it got me started on the right track.

A better tutorial was this:

http://blog.springsource.org/2011/01/04/green-beans-getting-started-with-spring-mvc/

I *almost* works out of the box - you just need to do a *little* bit of debugging.

Anyway, without further ado, here are the steps to follow:

1. From Eclipse STS, type file > new Spring Template Project (then choose) SpringMVCProject

2. If you follow the tutorial, you'll give the project a name like "baremvc" and a package name of "xyz.sample.baremvc".

NOTE: Watch out for caps on the project name - it appears to be the url name to call, and is case-sensitive. I need to test this.

3. Because the blog is about a year old, there are slight variance in code he suggests and the code that is generated. In fact, you can leave the code as generated for home.jsp and HomeController.java.

If you do use the code for "HomeController" as provided, make sure to make the return line "return "home" and not

return "WEB-INF/views/home.jsp";

4. Now, run it on the server. Right click on the project and select "run on server".

5. Enter http://localhost:8080/baremvc

You *should* be ok. One thing to watch out for is if you set up multiple project - I think only one of them can have a controller set up with a "/". The rest of them must need something extra. I'll check that.

Thursday, December 29, 2011

Spring - the Container pattern

I've started reading "Spring Recipes" by Gary Mak. It covers up to 2.5, and we're up to 3. But I've read up to chapter 5 and I still think it's worth reading, because it conceptually explains the concept of spring, as well as providing a great overview of its functionality and lots of working code.

The very first pattern the book discusses is the "Container" pattern. Essentially, instead of instantiating a concrete class into an interface, which introduces a dependency on the concrete class and kinds of defeats the purpose of the interfaces, you instead create a Container which instantiates the concrete class and makes it available via a "get" method which specifies an id.

In the example shown, a "report generator" class is separated into two classes - a report generator class and a report service class. The report service class gets the report generator class from the container's "getComponent" method, specifying "reportGenerator" as the id. Then, when it needs to generate an annual report, monthly report etc., it just makes a call on the report generator object.

The upshot of all this is that the the Report Service doesn't have a dependency on the specific Report Generator (unlike before, when they were combined).

Since the container class also instantiates the Report Service and stores it in the same object map as the report generator, it can be accessed in a main method. The main method instantiates the container, gets the report service from the container, and then calls the appropriate generate method on it.

You could argue that you've made the system more complex by introducing the container, and just kicked the dependency can down the road to the container class. On the other hand, you've separated the dependency-type code (which type of report) from the reporting service code. Perhaps the real gain is that it represents a way to separate distinct functionality into separate classes, in a way that keeps a class that uses an interface from also referring to the concrete class. That's got to be a win.

Tuesday, December 27, 2011

A sprinkling of Spring

Although this blog is about mobile development, I also would like to learn a bit about Spring technologies. This is mostly because pretty much any non-Android Java job out there has some element of Spring or Spring concepts in it. So, basically it's just good to know.

The goal therefore of this post is to get a simple hello world up and running in Spring. I've downloaded the IDE about 7 times. But, now I'm determined to get the hello world done.

At this url: http://www.springsource.com/developer/sts

we find the link for download the SpringSource Tool Suite. Note you can skip filling in the form on the right if you click on the link that says "I'd rather not fill in the form".

On that download page - http://www.springsource.com/downloads/sts - we find that there the current Version is "2.8.1.RELEASE", it's eclipse 3.7.1 and there's a dmg (mac install file) listed right at the top. 385 MB.

springsource-tool-suite-2.8.1.RELEASE-e3.7.1-macosx-carbon-installer.dmg

Ok, this will take a few minutes. I'm at Panera, but it's 4:21 in the afternoon, so, no one's really around. While that's going on, let's look up how to do a hello world somewhere on the net.

Actually, if I click on the "Spring Framework" link from the main page, it looks like there's some promising links in there. "Get Started" has links for get sts (I'm doing that), tutorials, code samples, doc, forums. Let's go to tutorials.

Ok, how about this screencast:

http://www.youtube.com/watch?v=kSITVsOUvLU

It's a 15 minute webinar.

Ok, I've just finished watching part 1. It's a nice overview of core spring, what it is conceptually. Let's move on to part 2.

It's asking me to install Spring now. I'm just double-clicking the .dmg, and then the install app. It installs into /Users/myname/springsource, and springsource has 4 directories.


apache-maven-3.0.3
spring-roo-1.1.5.RELEASE
sts-2.8.1.RELEASE
vfabric-tc-server-developer-2.6.1.RELEASE


When you start sts, it creates this workspace:

/Users/myname/Documents/workspace-sts

Ok, now the IDE is up. I moved all the tabs to the left side, which is much nicer. There's a dashboard display with a lot of neat looking links, including tutorials, as well. There's an image below.





Ok, he says just do file, new, spring template project. Then choose "simple spring utility project" or "spring mvc project" - unfortunately, he doesn't tell you which one right away because he's already created his projects. Let's go with the utility for now. It's kind of an overview...

Actually, he kind of breezes over the code. it's more of a showing what you can do than a "do this" kind of presentation. And there are actually 5 videos altogether. I feel like I know a bit more, but I'd really like to get that hello world going. We'll tackle that in the next post.

Thursday, December 15, 2011

Getting files from the iPhone/iTouch to the Mac

As I'm implementing a new feature in my app which updates the database, I've come to realize that I'll be needing to copy the database to my mac for checking purposes. I'm going to follow a post in Stack Overflow that purports to do this.


http://stackoverflow.com/questions/3456613/how-to-copy-log-file-from-iphone-to-desktop-pc

If you write out your logs to a file inside the App's Documents directory, and you have access to the device, then you can download the Application Data directory. I've done this before to copy off the logs and SQLite database from our application. You can follow the instructions here:

Managing Application Data

To make a copy of your application’s device–based file system to your Mac:

In Xcode, choose Window > Organizer.

// done

In the Organizer, select your device in the Devices list.

// note - click on the devices icon on the top of the display
// done


In the Summary pane, click the disclosure triangle next to your application.
Click the download button (the down-pointing arrow to the left of the Application Data package), as shown in (the figure below:)




// Note - you need to choose "applications" on the left side in order to
// get the list of applications.


In the dialog that appears, choose a location for the file-system copy.
Note: I'm not sure it show the triangle you need to click on, so I've included my own screenshot of it.

// Actually, in XCode 4 it's apparently a bit different. Look for the download
// arrow at the bottom of the display:




Download the file to a convenient directory. Note the file downloaded is an "xcappdata" file. Right click on select "Show Package Contents" on it. This gives you access to the files.

Note that if you're dealing with a database, then it may or may not be in there, depending on if you left it in the resources directory (non-updatable) or copied it into user documents. See http://tinyurl.com/cks54ay for info on how to do that.

Wednesday, December 14, 2011

Simple constants in Objective C

At this point, we just want to implement a bit of date manipulation logic.

If the user had less than 5 seconds remaining before returning the question, then we want to use a shorter amount of days - call it "SLOW_RESPONSE_INTERVAL_DAYS". Otherwise, we'll use "FAST_RESPONSE_INTERVAL_DAYS;

In the Android, it looks like this:


static final int SLOW_RESPONSE_INTERVAL_DAYS = 3;
static final int FAST_RESPONSE_INTERVAL_DAYS = 5;

In iOS, pretty much is seems like defines are used...

#define SLOW_RESPONSE_INTERVAL_DAYS 3
#define FAST_RESPONSE_INTERVAL_DAYS 5

We'll keep it inside the class where we're processing the spaced repetition logic, so that ought to be sufficient for our needs.

iOS - incrementing a date value from a string input

One of the enhancements I'm making my iOS app is to add scheduling for a review of particular words. This review date is actually based on the amount of time it takes to answer a given question - the longer it takes, the quicker the review should be scheduled. Another consideration is the internal format in which dates are held by SQLLite, which is a string in yyyy-MM-dd format. This is helpful because now the input and output of the problem solution is limited to string manipulation.

So the input to our method is effectively the number of seconds it took to answer the question. We'll add the input date for flexibility - we could assumed it's today, but this is a trade-off for re-usability.

So, the first thing we need to do is take the string input date and convert it to an iOS date.

The key is the NSDateFormatter object, which has both the "stringFromDate" and the "dateFromString" methods. So, looking at an example from http://iphonedevelopertips.com/cocoa/date-formatters-examples-take-3.html, we adapt it a bit.

Example:

NSString *dateStr = @"20081122";

// Convert string to date object
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"yyyyMMdd"];
NSDate *date = [dateFormat dateFromString:dateStr];

// Convert date object to desired output format
[dateFormat setDateFormat:@"EEEE MMMM d, YYYY"];
dateStr = [dateFormat stringFromDate:date];
[dateFormat release];


We want to turn this into a method in our Utils class, which will also take the number of days:

+ (NSString *) addDaysToDate:(NSString *) inDate daysToAdd: (int) daysToAdd;

So, the first thing to do is take the code above and adapt it to convert the "yyyy-MM-dd" input string to a date.

// Convert string to date object
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"yyyy-MM-dd"];
NSDate *date = [dateFormat dateFromString:inDate];


Well tackle adding days to the date in a moment. For now, assume we've done that and just want to convert it back to the same format it came in as:

// Convert date object to desired output format
// The date format is already the way we want it

NSString *dateStr = [dateFormat stringFromDate:newDate];
[dateFormat release];
return dateStr;


The only remaining question is how to increment an instance of NSDate in iOS. Reviewing the docs, it looks like this might be the one that we're looking for:

dateByAddingTimeInterval:

Returns a new NSDate object that is set to a given number of seconds relative to the receiver.
- (id)dateByAddingTimeInterval:(NSTimeInterval)seconds


However, it's always worth a google check to make sure that's right. As it turns out, it isn't. It doesn't handle daylight savings time, for one thing. According to StackOverflow, the correct solution is this:


+ (NSDate *)getForDays:(int)days fromDate:(NSDate *)date
{
NSDateComponents *components= [[NSDateComponents alloc] init];
[components setDay:days];

NSCalendar *calendar = [NSCalendar currentCalendar];
return [calendar dateByAddingComponents:components toDate:date options:0];
}


So, once we add this to our Utils class, the call should look like something like this:


NSDate *newDate = [Utils getForDays: daysToAdd fromDate: date];

Here's the final (hopefully) product:

+ (NSString *) addDaysToDate:(NSString *) inDate daysToAdd: (int) daysToAdd
{

// Convert string to date object
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"yyyy-MM-dd"];
NSDate *date = [dateFormat dateFromString:inDate];


NSDate *newDate = [Utils getForDays: daysToAdd fromDate: date];

// Convert date object to desired output format
// The date format is already the way we want it

NSString *dateStr = [dateFormat stringFromDate:newDate];
[dateFormat release];
return dateStr;
}

Thursday, December 8, 2011

Android - regaining root access

Loyal readers of my blog, followed by millions (Joe Millions), may recall that I went to some lengths a few months ago to gain root access to my Android device - something that ought to be there by default, btw, since I paid a princely sum for the fun.

Now, it turns out that I've somehow lost root again. Arggh! The gods be cursed!

So, why did this happen? More importantly, how to get it back again? Let's see if we can reconstruct events from my previous posts.

Ah, good - in this blog post, I actually *summarized* what I did:

http://gettingintomobile.blogspot.com/2011/07/nexus-one-rooted-at-last.html

Long story short, it looks like I got through the tough part - unlocking the phone (had to be done from a Windows system), and now all I have to do is follow the instructions from this url:

http://chensun.net/android-development/root-nexus-one-with-android-2-3-3-gingerbread/8/

Ok, somebody noticed why they/I lost root:

Today the OTA Update to Android version 2.3.6 (Build GRK39F) was pushed to my Nexus One. And as i expected (from my experience of previous system updates), also with this android update you will loose your root access.


Ok, when I go to chensun.net, it confirms I can get root on 2.3.6:

Root Nexus One with Android 2.3.3 Gingerbread
Posted on March 26, 2011 by Chen

Update: September 24, 2011.
Also works for Android 2.3.6 on Nexus One!


There still might be a problem - I"m thinking the fastboot could have been a problem

Hmm...it looks like the fastboot needs to be flashed from XP, and not a virtual version either. It'll have to wait till I get home and can use my wife's pc.

Ok - I'm home. Once I got into her pc, it was very simple. In fact, it was just a question of following the tutorial steps, replicated below for you viewing:


1. Download su.zip, and put it on your phone’s SD card.

2. Download fastboot.zip, unzip it to C:\. Now you have a folder C:\fastboot\

3. Download recovery-RA-passion-v2.2.1.img, put it in C:\fastboot\

4. Shut down your phone. Then start your Nexus One in Fastboot mode by holding down the Trackball and press the Power button.

5. Connect your phone to your computer. Open a Command Prompt (Start -> All Programs -> Accessories -> Command Prompt), and run the following commands:

cd C:\fastboot
fastboot devices
fastboot flash recovery recovery-RA-passion-v2.2.1.img

6. Use Volume +/- keys to choose ‘Bootloader’ and hit the Power button.

7. Use Volume +/- keys to select ‘Recovery’ and hit the Power button.

8. From the menu, scroll the Trackball to select ‘Flash zip from sdcard’ and hit the Trackball.

9. Select the su.zip file you previously copied to your SD card and hit the Trackball. Hit it one more time.

10. When done, restart your Nexus One and you should be all set.

There you go. Enjoy your rooted Nexus One :)

Monday, December 5, 2011

Backdoor for testing the purchased app on your iPhone

I ran into a bit of an inconvenience when using my recently released application on my iPod Touch. It's a Japanese vocabulary quiz, and it's limited to the first 100 words until you make an in-app purchase. The problem is, I don't want to purchase my app, because I also want to be able test it as the free download as well. It might seem like a small thing, but I would prefer to work in the sort of "default' setting for my app, generally speaking. It's the first version people will see.

The problem was that, after I had submitted my app, I ran into the in-app-purchase limitation on my own device. To get around this, I commented out the check for purchase code in my app. Then I got busy with life and completely forgot about it. So, when I went to make an enhancement to the app, I was happily coding away when I quite accidentally got into the same section of code which checks for the purchase. I was completely taken aback when I saw the code completely skipping the check for the app purchase. Would Apple have caught the problem had I submitted it? I doubt it - they wouldn't go up to 100. Once released, there would be no reason to purchase the app - I would have been giving out the whole thing for free. With a 1% fill rate for the iAd advertising, I'm not going to be making money on ads, that's for sure. Who knows when I would have finally realized I was giving it away? Note to self - don't *do* stuff like that.

So, as soon as I got over my shock, I immediately set about fixing up the problem. The solution clearly lends itself to some type of checking of the unique id of the device. Surprisingly, this turned out to be very easy to code - just one line, and no permissions required. It can be retrieved and logged as follows:



NSString * uniqueId = [[UIDevice currentDevice] uniqueIdentifier];

NSLog(@"uniqueIdentifier: %@", uniqueId);



There is one important caveat - this method has been deprecated in iOS 5. However, until some future date, it's going to be my solution. Once that method is gone, my app won't compile - there's no danger of giving it away for free.

This method works on the simulators too. It turns out that they all have the same id (amongst themselves, not the iPod Touch). With that information in hand, I quickly created this code:



+ (BOOL) isFreeVersion {

NSString *productIdentifer = @"com.mycompany.myapp.myinapppurchase";

if
(
([uniqueId isEqualToString:@"xxxyyyzzz"]) || // my device's id
([uniqueId isEqualToString:@"zzzyyyxxx"]) // my simulator's id
)

{
return NO; // it's been purchased
}

BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifer];

return !productPurchased;

}



Nice and quick. Let's hope Apple doesn't get around to deprecating this one for a while.