TuneMenu – See the Current iTunes Song in your Menu!

November 10th, 2009

TuneMenu a nice little MenuItem that I wrote that simply displays the current song in the OS X menu bar. You can download it here It also has a couple menu features:

As you can see, clicking the MenuItem opens a submenu that shows the album name, as well as a “Lookup Lyrics” item, which will pull up a new browser tab and point it towards the corresponding lyricwiki.org page. This works most of the time, depending on how cleanly you’ve named your songs.

You can also customize the font, although I can’t say I’m too proud of my methods. To change font settings, you have to open up your Terminal and run any of these commands:

$ defaults write com.silentmac.TuneMenu font-name Arial
$ defaults write com.silentmac.TuneMenu font-size 11
$ defaults write com.silentmac.TuneMenu font-bold TRUE


I prefer the font Cambria, size 10, bolded.

So why did I write it? I love iTunes, but I usually find myself paging back and forth in Expose between applications to pull iTunes to the front just to check the name of the current song. When you have 8+ applications running at the same time, this can be somewhat of a pain (especially if you accidentally pull Photoshop to the front, which has a 100mb file loaded up). I looked around to find iTunes notifiers for Mac - I didn't want anything with Growl (having Growl constantly attack me would just force me to stop listening to music). After a bit of looking around, I found iTunes Current Song Menu, which sounded perfect, but, alas, was shareware that cost $20. I didn't want to pay $20 for a simple little script - how hard could it be - so I busted out XCode and came up with this an hour later.

If you're interested, the source can be found here.

Joe

CSV to sqlite3 Cmd Line Converter

November 3rd, 2009

Every now and then I have an iPhone project that requires me to search through a large amount of local data. Luckily, Apple makes the sqlite3 library available on the phone, so all I have to do is create a compatible sqlite3 database, and I can then search through that in my app. This is much easier said than done.

In the past, I usually find myself parsing a CSV or some other simple formatted file through a custom script that then directs the output to a database I had already created. This works, but is not efficient. Writing a script anytime I need to populate a database is not optimal, so this time I decided to create something more general.

I created a script (source available here, and an OS X binary available here) that takes in a pre-made CSV and pre-made sqlite3 database file, and then populates the database. Sample usage looks like:

$ ./csv2sqlite3 upc.sqlite stores upc.csv

Where upc.sqlite is the database file, ’stores’ is our table name, and upc.csv is the CSV file. Of course, you have to have a pre-made database (in this case, upc.sqlite) before you run it. Creating a database is very easy. Just hop into the directory you want the file saved in, then run:

$ sqlite3 database-name.sqlite
SQLite version 3.6.14.2
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> CREATE TABLE stores (id varchar(10), name varchar(150), state varchar(3));
sqlite> .exit


Make sure that the table has the same structure as the CSV (same amount of fields).

The biggest limitation to this script right now is that it only works for databases where all fields are varchars (no floats or ints I'm afraid). You'll have to customize the script to get this to work for you.

Well, that's it for now. Hope this helps someone

Joe

Embedded Youtube Videos in an iPhone App

October 12th, 2009

About a week ago, a client asked me to do a project that involved embedding a YouTube player inside of an iPhone app, so that the video would play, and then control would be returned to the app. Hesitantly, I searched for a bit before getting back to him. I found this entry over at the YouTube API blog, which shows you how to add YouTube videos to your app by embedding them as a webpage inside of a UIWebView inside your app.

This method works, but the downside is that you have a big ugly YouTube frame/link in your UIWebView:

The client didn’t want this, so I was forced to look for a workaround. Initially, I tried to hide this webview and simply reroute touches to the device on to this specific view. No dice. I tried executing Javascript, calling the “onClick()” handlers that belonged to the embedded object. Still no dice. Weird. I executed some more Javascript to print out all DOM objects on the page, and, surprisingly enough, the <embed> and <object> objects did not exist.

The only possibility left is that UIWebView, upon finding an embedded YouTube video, replaces it with another subclass of UIView inside of the scroll view. Luckily, every UIView provides us open access to its subviews and superviews, so there is no way for Apple to hide the view structure. So I wrote a quick little function that recursively looks through a view’s contents and returns the results as an NSString, to be NSLog’d:

- (NSString *)showSubviews:(UIView *)view tabs:(NSString *)tabs {
	if (!tabs) tabs = @"";
	NSString *currStr = tabs;
	currStr = [NSString stringWithFormat:@"%@%@\n", tabs, [view class], nil];

	if (view.subviews && [view.subviews count] > 0) {
		tabs = [tabs stringByAppendingString:@"\t"];
		for (UIView *subview in view.subviews) {
			currStr = [currStr stringByAppendingString:[self showSubviews:subview tabs:tabs]];
		}
	}

	return currStr;
}

And the output:

UIWebView
	UIScroller
		UIImageView
		UIImageView
		UIImageView
		UIImageView
		UIImageView
		UIImageView
		UIImageView
		UIImageView
		UIImageView
		UIImageView
		UIWebDocumentView
			YouTubePlugInView
				UIImageView
				UIImageView
				UIView
				UIView
				UIView
				UIView
				UIButton
					UIImageView

Aha! A YouTubePlugInView! No wonder sending clicks to the webview via Javascript wasn't working - the Youtube player was a native class (not just something drawn on the screen by the UIWebDocumentView). Also, the YouTubePlugInView contains a UIButton - most likely the "play" button you see. Since we have a reference to the button, we don't even need to mess with touch events - we can just call [button sendActionsForControlEvents:UIControlEventTouchUpInside]; and the corresponding code will execute. Genius.

So the final solution looks something like this:

- (void)awakeFromNib {
	webView.delegate = self;
	NSString *htmlString = [NSString stringWithFormat:[NSString stringWithContentsOfFile:[[NSBundle mainBundle]
		pathForResource:@"YouTubeTemplate" ofType:@"txt"]], @"b85hn8rJvgw", @"b85hn8rJvgw", nil];
	[webView loadHTMLString:htmlString baseURL:[NSURL URLWithString:@"http://youtube.com"]];
}

- (UIButton *)findButtonInView:(UIView *)view {
	UIButton *button = nil;

	if ([view isMemberOfClass:[UIButton class]]) {
		return (UIButton *)view;
	}

	if (view.subviews && [view.subviews count] > 0) {
		for (UIView *subview in view.subviews) {
			button = [self findButtonInView:subview];
			if (button) return button;
		}
	}

	return button;
}

- (void)webViewDidFinishLoad:(UIWebView *)_webView {
	UIButton *b = [self findButtonInView:_webView];
	[b sendActionsForControlEvents:UIControlEventTouchUpInside];
}

Where YouTubeTemplate.txt is a local text file containing:

<pre><code><html><head>
<meta name = "viewport" content = "initial-scale = 1.0, user-scalable = no, width = 212"/></head>
<body style="background:#F00;margin-top:0px;margin-left:0px">
<div><object width="212" height="172">
<param name="movie" value="http://www.youtube.com/v/%@&f=gdata_videos"></param>
<param name="wmode" value="transparent"></param>
<embed src="http://www.youtube.com/v/%@&f=gdata_videos"
type="application/x-shockwave-flash" wmode="transparent" width="212" height="172"></embed>
</object></div></body></html></pre></code>

The app requests the webview to load the contents of this file (replacing the %@'s with the video ID), waits for the load to finish, then finds the button in the YouTubePlugInView, and triggers its UITouchUpInsideEvent. And after that, the video will play in an embedded fashion.

Pretty tough stuff.
Joe

dls.py Updated, App Store Earnings Parser Working Again

August 16th, 2009

Apple recently updated their iTMS portal, breaking my previously released App Store earnings parser. I finally got around to patching it and releasing an update this weekend… Simply download the new script, open it in a text editor and fill in your username and password in the following lines:


USERNAME = ''
PASSWORD = ''

Then save and rename the file 'dls.py' , then call it like so:

/usr/bin/python /path/to/script.py

The script will execute, and spit back what apps were sold that day, like so:


$ python dls.py
Retrieving records from 08/15/2009
SlayerStats:	8
PizzaNow:	1
whois:		1
Total:	10 sold, for $6.98

Enjoy! Check the previous post for information on how to setup a cronjob that emails you your daily earnings :)

- Joe

A Little Experiment

July 4th, 2009

I realized today that I’d have to buy another year of iPhone Developer membership very soon, which costs $99. I bought into the program a day or so before launch, so my deadline is coming up quick. I was a little bummed about this (it’s $100, after all), so I figured I’d try and see if I could write an app and put it up on the store and see if I could cover my costs. I was sitting in a Mexican airport, waiting for my flight to board so I could make it back to the states for the 4th, and I came up with the idea for a “HowFar?” app. Basically, I would use google to geocode two queries into coordinates, and then calculate the distance between them. I figured it’d be an okay 99 cent app.

I used the same techniques from my post about iPhone forms, and even threw in some custom graphics. The result model was this:

I made all the clip art either sitting in the airport, or a few thousand feet in the air. I had to wait till we landed (and went through customs etc.) to finally test the app, but after a few minutes I had it working! I happily found out that Google geocodes basically anything, so I can find the distance to, say, “Apple Headquarters” or the “Statue of Liberty.” Awesome.

Overall, it is a very basic app, took me around 3 hours (which would have been otherwise squandered in an airplane), and will hopefully make me enough money to pay for my membership. I’ll make sure to post figures as I get them.
-Joe