Writing your first Android App (GDG Columbus meeting, February 21, 2013)

Last night, I attended the Google Developers Group Columbus meeting “Writing your first Android App presentation” at the Blue Diesel offices here in town. Special thanks to Casey Borders, the main presenter who kept us moving through the code heavy presentation. I am still a novice when it comes to Android development, so I did learn quite a bit at the meeting.

BTW, happy birthday to Jeri Ryan, who capably portrayed Seven of Nine in Star Trek Voyager.

Fix the SQLite error “The database disk image is malformed”

I suppose it was only a matter of time before someone’s database got corrupted. Of course, customers don’t want to hear that their issue is a once-in-a-year issue, they just want to get their data back. (Of course, they have used the app for a long time and not done any backups, but that topic could fill an entire blog.) Here are the steps I took to fix the SQLite error “The database disk image is malformed”:

1. Use the sqlite3 app in the Mac OS X Terminal to create a .SQL export file

So I fired up the Terminal and changed to the directory where I had saved the bad database file, and entered the command:

sqlite3 DB.sqlite

This launches the sqlite> prompt, at which I entered the following commands:

.mode insert
.output dump_all.sql
.dump
.exit

At this point, you have the .SQL file in the same directory as the bad database file.

2. Remove transaction statements from the file

I received some errors in the next step, so I would recommend that you manually edit the .SQL file and remove any kind of transaction statements. In my example, there was a BEGIN TRANSACTION statement on the 2nd line of the file and a ROLLBACK statement on the last line. I removed both of these lines and re-saved the file.

3. Use the SQLite Manager extension for Firefox to create a new database file and import the .SQL file

The last step is to launch your Firefox and open the SQLite Manager extension, create a brand new database, select Import from the Database menu, click Select File and find the .SQL file, make sure the BEGIN TRANSACTION/COMMIT check box is clear, and click OK.

At this point, I had a new SQLite file that did not give the malformed error message any more. As with any database file corruption issues, I probably got a bit lucky that the file was not too badly damaged, or damaged beyond repair.

Here was the blog post by Sergei Dorogin that I found that got me part way to the solution in my instance:

SQLiteException “database disk image is malformed”

BTW, Happy Birthday to Roy Face, the former great Pittsburgh Pirates pitcher. He was one of the coaches at this past year’s Pirates Fantasy Camp, and seemed like a very nice person.

.NET regex to find strings inside curly braces

Regular expressions all by themselves have the magical ability to amaze and confound all at the same time. Throw in a dash of .NET string interpretation and you have a recipe for pulling out the remaining hair that you might have on your head.

So I was trying to use a regex to do some searching inside of a large string for strings inside curly braces, such as {this} and {these three words}. My string matching routine would return “this” and “these three words”, without the quotes of course. It took a bit of trial and error, but here is the important stuff for the routine that I put together to accomplish this task:

const string pattern = @"\{([^\}]+)\}";
foreach (Match match in Regex.Matches(theLargeString, pattern))
{
    Console.WriteLine("Found a field match: " + match);
}

BTW, a special birthday shout out to one of the mega stars of our generation, who turns 50 years young today. Happy birthday, Larry The Cable Guy.

Migrating ASIHTTPRequest to AFNetworking (submit to a URL with POST variables)

For my third and final AFNetworking migration series of posts, let us consider calling a web service URL with POST variables.

Here was the ASIHTTPRequest code:

    NSMutableString *fileURL = [NSMutableString stringWithString:THE_URL];
    NSURL *url = [NSURL URLWithString:fileURL];
    __block ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
    [request setDelegate:self];
    [request addRequestHeader:@"Content-Type" value:@"application/json; charset=utf-8"];
    [request setRequestMethod:@"POST"];
    [request setPostValue:theID forKey:@"id"];
    [request setCompletionBlock:^{
        NSLog(@"success with response string %@", request.responseString);
    }];
 
    [request setFailedBlock:^{
        NSLog(@"error: %@", request.error.localizedDescription);
    }];
 
    [request startAsynchronous];

And here is the corresponding AFNetworking code:

    NSMutableString *fileURL = [NSMutableString stringWithString:THE_URL];
    AFHTTPClient *client = [[[AFHTTPClient alloc] initWithBaseURL:url] autorelease];
    NSDictionary *params = @ { @"id" : theID };
    NSMutableURLRequest *request = [client requestWithMethod:@"POST" path:fileURL parameters:params];
    AFHTTPRequestOperation *op = [[[AFHTTPRequestOperation alloc] initWithRequest:request] autorelease];
    [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSLog(@"success with response string %@", operation.responseString);
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        NSLog(@"error: %@", error.localizedDescription);
    }];
    [op start];

BTW, Happy Valentine’s Day to one and all.

Migrating ASIHTTPRequest to AFNetworking (uploading a PNG to a web service)

For part 2 of my AFNetworking migration experiences, let’s cover uploading a file to a web service.

Here is the original code:

    NSString *ws = [NSString stringWithFormat:@"http://myurl.com?id=%@", theID];
    NSURL *url = [NSURL URLWithString:ws];
    NSData *postData = [[[NSData alloc] initWithContentsOfFile:filePath] autorelease];
    ASIFormDataRequest *request;
    request = [[[ASIFormDataRequest alloc] initWithURL:url] autorelease];  
    [request setPostValue:pictureFileName forKey:@"file"];  
    [request setData:postData withFileName:pictureFileName andContentType:@"image/png" forKey:@"file"];
    [request setRequestMethod:@"POST"];
    [request setShouldAttemptPersistentConnection:YES];
    [request setUploadProgressDelegate:progressView];
    [request setCompletionBlock:^{
        NSLog(@"success with response string %@", request.responseString);
    }];
 
    [request setFailedBlock:^{
        NSLog(@"error: %@", request.error.localizedDescription);
    }];
 
    [request startAsynchronous];

And here is the AFNetworking code:

    NSString *ws = [NSString stringWithFormat:@"http://myurl.com?id=%@", theID];
    NSURL *url = [NSURL URLWithString:ws];
    NSData *postData = [[[NSData alloc] initWithContentsOfFile:filePath] autorelease];
    NSDictionary *sendDictionary = [NSDictionary dictionaryWithObject:postData forKey:@"file"];
    AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:url];
    NSMutableURLRequest *afRequest = [httpClient multipartFormRequestWithMethod:@"POST"
                                                                           path:@""
                                                                     parameters:sendDictionary
                                                      constructingBodyWithBlock:^(id < AFMultipartFormData > formData)
                                      {
                                          [formData appendPartWithFileData:postData
                                                                      name:pictureFileName
                                                                  fileName:pictureFileName
                                                                  mimeType:@"image/png"];
                                      }
                                      ];
 
    AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:afRequest];
    [operation setUploadProgressBlock:^(NSUInteger bytesWritten, long long totalBytesWritten, long long totalBytesExpectedToWrite) {
        if (totalBytesExpectedToWrite == 0)
        {
            progressView.progress = 0.0;
        }
        else
        {
            progressView.progress = totalBytesWritten * 1.0 / totalBytesExpectedToWrite;
        }
    }];
 
    [operation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
     {
        NSLog(@"success with response string %@", operation.responseString);
     }
                              failure:^(AFHTTPRequestOperation *operation, NSError *error)
     {
        NSLog(@"error: %@", error.localizedDescription);
     }];
 
    [operation start];

The AFNetworking code is somewhat longer, so that is a bit of a problem. If anyone has any suggestions on how to make the AFNetworking code a bit more concise, please let me know.

BTW, Happy Birthday to Joe Don Baker, a distinguished actor with such outstanding films to his credit such as Mitchell, Final Justice, Fletch, and of course the all-time classic, Joysticks.

Migrating ASIHTTPRequest to AFNetworking

After using the ASIHTTPRequest library for a long time (so sad that Ben Copsey decided to abandon it), I finally decided to give in and switch to AFNetworking by Mattt Thompson and Scott Raymond. It has taken some trial and error to get it working correctly, so here is what I have found so far.

Conversion #1: Simple call to a URL

For this conversion, I am just making a call to a web service URL. I don’t really care about the return value, I just care if the call went through or whether there was an error in communications with the web service.

The ASI code:

    NSString *urlString = [NSString stringWithFormat:@"http://myurl.com?id=%@", theID];
    NSURL *url = [NSURL URLWithString:urlString];
    __block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
    [request setDelegate:self];
    [request setCompletionBlock:^{
        NSLog(@"success");
    }];
 
    [request setFailedBlock:^{
        NSError *error = [request error];
        NSLog(@"error: %@", error.localizedDescription);
    }];
 
    [request startAsynchronous];

The AFNetworking code:

    NSString *urlString = [NSString stringWithFormat:@"http://myurl.com?id=%@", theID];
    NSURL *url = [NSURL URLWithString:urlString];
    AFHTTPRequestOperation *op = [[AFHTTPRequestOperation alloc] initWithRequest:[NSURLRequest requestWithURL:url]];
    [op setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject)
     {
         NSLog(@"success");
     }
                              failure:^(AFHTTPRequestOperation *operation, NSError *error)
     {
        NSLog(@"error: %@", error.localizedDescription);
     }];
 
    [op start];

Yeah, admittedly this one was not very hard to convert. Stay tuned, other more complicated conversions are coming.

BTW, Happy Birthday to Jim J. Bullock, who excellently portrayed Prince Valium in Spaceballs.

Thoughts about Ubuntu on my Chromebook

For my extended vacation that I just got back from, I decided to leave the MacBook Air at home and try to purchase the Acer Chromebook from the Google Play store for the price of $199. Upon receiving the first one, there was a problem with the screen backlighting not working properly, and after conversing with the Google Play customer service people, I got a replacement unit that worked just fine.

The next step was to put something else on it than the Chrome OS. Not that I am against the idea of the Chrome OS, but I knew that I was not going to be in internet service areas all of the time, which would limit the functionality of the Chromebook. So I found this posting on how to install Ubuntu on a Chromebook:

How to install Ubuntu on Acer’s $199 C7 Chromebook

And I must say that it works very well, even though the stock Acer Chromebook has a slow processor, slow hard drive, and only 2 GB of RAM. I highly recommend it for someone looking for an inexpensive travel computer that you can use for web surfing, e-mail checking, light document work, watching movies, etc.

One snag I ran into was an error that came up while updating. When using the Update Manager, I would get the following error after selecting some updates to install:

Requires installation of untrusted packages

So I found a posting on the Ubuntu forums on how to fix this. Basically, I just went to the terminal and ran the following two commands:

sudo apt-get update

and:

sudo apt-get upgrade

After these were done, I was able to get through all of the updates just fine.

BTW, Happy Anniversary to Darryl Sittler, the former Toronto Maple Leafs captain, who on this day in 1976 scored an NHL record 10 points (6 goals and 4 assists) in a game vs. the Boston Bruins.

A better way to launch the Maps app for navigation on iOS 6

Back in the days when Google actually ruled Maps on the iOS platform (5.x and older), you had to push a URL through UIApplication’s sharedApplication object, and the system would see the  Google maps URL and intercept it, sending the data to Maps instead of Safari.

Well this kind of works in iOS 6 as well, substituting maps.apple.com for maps.google.com. But alas, there is a better way.

Here is the code that I am now using to launch Maps, and it works in both iOS 6 and iOS 5.

- (void)launchMaps:(NSString *)serviceAddress
{
    NSString *currentLocation = @"Current+Location";
 
    if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0"))
    {
        // use MapKit to handle launching Maps app instead of pushing a URL
        CLGeocoder *geocoder = [[CLGeocoder alloc] init];
        [geocoder geocodeAddressString:serviceAddress completionHandler:^(NSArray *placemarks, NSError *error) {
            if (error)
            {
                NSLog(@"Geocode failed with error: %@", error.localizedDescription);
                return;
            }
 
            if (placemarks && placemarks.count > 0)
            {
                CLPlacemark *pl = placemarks[0];
                CLLocation *loc = pl.location;
                NSLog(@"Geocoded latitude: %f; longitude: %f", loc.coordinate.latitude, loc.coordinate.longitude);
                MKPlacemark *placemark = [[MKPlacemark alloc] initWithCoordinate:loc.coordinate addressDictionary:nil];
                MKMapItem *mapItem = [[MKMapItem alloc] initWithPlacemark:placemark];
                mapItem.name = @"Whatever name you want goes here";
                mapItem.phoneNumber = @"And if you have a phone number, it would go here";
                NSArray *mapItemArray = @[ mapItem ];
                NSDictionary *launchOpt = @{ MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving };
                [MKMapItem openMapsWithItems:mapItemArray launchOptions:launchOpt];
                [mapItem release];
                [placemark release];
            }
        }];
 
        [geocoder release];
        return;
    }
 
    NSString *urlString = [NSString stringWithFormat:@"http://maps.google.com/maps?saddr=%@&daddr=%@", currentLocation, serviceAddress];
    NSString *escaped = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:escaped]];
}

And here are the defines for the OS checking macros that I use. (Yes, I know you should check for specific features instead of checking OS version numbers.)

#define SYSTEM_VERSION_EQUAL_TO(v)                  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedSame)
#define SYSTEM_VERSION_GREATER_THAN(v)              ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedDescending)
#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v)  ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN(v)                 ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
#define SYSTEM_VERSION_LESS_THAN_OR_EQUAL_TO(v)     ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedDescending)

The address string passed in would just be the components of your address separated by spaces (such as “1060 W Addison St Chicago IL”), and the Maps app should open up and give you the opportunity to start navigation to the destination.

BTW, Happy Birthday to Frank Zamboni, who is now forever immortalized with a Google Doodle.

Codemash conference recap

I have been back now a few days from my first ever Codemash experience, and I can say without a doubt that it was a great conference. Thanks to Jim Holmes and the army of volunteers and presenters that made the conference a rousing success.

I tried to attend mobile sessions since that is my primary focus here at My Service Depot, and the sessions were for the most part very informative. The highlight of the conference for me was Jon Skeet‘s presentation on abusing the C# compiler in Visual Studio. The room was standing room only literally seconds after the presenter before Jon got done with his presentation. Also, I attended both of Chris Risner‘s presentations on Windows Azure Mobile Services, and found them to be impressive, certainly worth a look for some of the ideas that I have floating about in my conscious.

Unfortunately, the lowlight of the conference had to be the line at the bacon bar. I suspect that could have been done a bit better than it was, as I really wanted to attend a session at that time instead of standing in line waiting to get bacon.

BTW, Happy Ratification Day to all my fellow U.S. citizens.

Codemash 2013 Wednesday Pre-compiler

Well this is my first time attending the Codemash developer conference at the Kalahari Resort in Sandusky, Ohio. I must say that this is a very nice conference considering that it is not taking place in Seattle or California.

For the morning session, I attended the HTML5 talk, and in the afternoon, I attended the Android talk. Both talks were very informative. And I especially like the bacon wrapped potatoes that they serve for breakfast.

BTW, happy birthday to Michael Schenker, one of my favorite rock guitar players.