Ending The Week On An “Up” Note

Ending The Week On An “Up” Note

As frustrating as working on any Blackberry project can be, I’m very happy with the progress being made on a new BB app. Several big pieces came together this week, just in time for the weekend.

It’s very easy to complain while in the throes of thrashing together something new.  And I do tend to thrash. A lot.

But when it all comes together – baby.

My Last Blackberry Bitch This Week – I Promise

My Last Blackberry Bitch This Week – I Promise

How do you know when I’m working on a Blackberry Project?

Because of my non-stop bitching about how poor a job the makers of Blackberry have done implementing their SDK.

It would lead one to believe that I don’t like developing for Blackberry on principle. That is not, in fact, the case.

I mean, I love my kids, I love the color of money, and working on Blackberry projects helps me help the ones I love by allowing me to feed, clothe, house, and school them.

But I can’t for the life of me figure out how the management at Blackberry allows some of the big omissions in their platform persist.

What has set me off today, you ask?

This little beauty:

In Blackberry’s Java implementation, there is no way to disable a button programmatically, without deleting it.

Read that again.

In Blackberry’s Java implementation, there is no way to disable a button programmatically, without deleting it.

Seriously.

Again, it’s not like this thing hasn’t been out a few years. I mean, not to shame them or anything, but back in 1996 Visual Basic was able to do this.

Android can do this. Apple can do this.

C’mon Blackberry. You’ve got to do better than this.

Helpful Hint when Debugging with a Blackberry Device

Helpful Hint when Debugging with a Blackberry Device

One (of the many) frustrating things that one encounters when developing for the Blackberry platform is the frequent number of times that simulators are started and restarted when testing. It’s gotten better, but is nowhere near as seamless as working on Android or the gold standard in this regard, the Apple iPhone.

It’s not a lot better when debugging on an actual Blackberry Device. In fact, one of the odder problems that can sometime occur is that the  Eclipse loader will look like it’s trying to load your code, only to give you a message that “The Device Is No Longer Available”… when in fact it’s connected and waiting happily for you to do something.

The only thing that I have found to reliably and repeatably keep this from happening is to keep the RIM Desktop Manager running in the background while I debug in Eclipse.

I don’t know if this is because the RIM software is using some sort of “keep alive” on the USB connection (which I suspect) or not.

All I know is that without it running, not only am I pulling the Blackberry’s battery to get it to reconnect, I’m also having to reboot my system to get the two on speaking terms again.

It’s often said that software development is more art than science.

It’s because of crap like this.

Blackberry Numeric Formatted Output – You’re SOL

Blackberry Numeric Formatted Output – You’re SOL

If you’ve tried to do some basic formatting for – oh say – currency or thousands-separated floating point numbers in Blackberry Java, you quickly realize something:

You’re spit outta luck.

For you old C developers, I daresay you can’t do without printf, fprintf, or sprintf for doing external and internal string formatting.

But somehow, the geniuses behind Blackberry failed to supply one of the most basic elements used by developers in their apps – the ability to easily format numeric strings for output.

It’s not like Java doesn’t have a Formatter class – it’s just that Blackberry’s version of it doesn’t support locale-specific decimal points and separators. So, something like Formatter fmt = new Formatter("###,###.00") – which works like a charm in most Java environments and on Android – is exception city on Blackberry.

In short, you’re screwed.

There is a solution – write your own formatter (ugh).

The fine folks at Sun do have a sample implementation of a Java printf analog (http://java.sun.com/developer/technicalArticles/Programming/sprintf/PrintfFormat.java). Unfortunately, they prohibit it’s use in commercial applications. Which means it’s worthless as a solution for professional developers.

This may seem like a trivial thing.

Except that you have to take time away from doing real project work to fix a shortcoming in the Blackberry SDK, so that your app can have basic numeric and currency string formatting.

And for those playing along at home, time is money. You heard it here first.

There really isn’t an excuse for this type of shortcoming, in a product line years on the market.

Always Challenge Your Assumptions

Always Challenge Your Assumptions

I spent the better part of the weekend trying to fix a bug that was perplexing me and arresting my progress on a new Blackberry project.

Not that you particularly care, but it had to do with how Blackberry creates “tunnels” (connections) to communicate over the internet. There are about a dozen different ways to connect (BIS, BES, Direct/TCP, WiFi, WAP, WAP2, in HTTP and Sockets variations), and if you don’t hold your tongue ** just right ** your connections won’t happen.

Anyway, almost all of my connections were working perfectly. After all, this wasn’t my first Blackberry project and I was using a library I had developed and used on other projects.

But I had one call that was failing every time I tried to run it on a real device (and maddeningly, working perfectly under the simulator).

What was going on? I mean, I’ve used the library before with no issues whatsoever.

But I hadn’t used it in ** just ** the way I was now using it; and there was the rub.

As it turned out, I was using a call in the library that I had never used before.

And because of this, there was a place in the code where I had neglected to correctly determine the type of connection I needed to make (again with the BIS, BES, Direct/TCP, yada, yada, yada) and so my connections were failing on a real device.

It only took me two days to realize that I needed to challenge my assumptions that the library was right… and if I had done what I should have done originally (namely, examine directly the call in question) I would have had more time to myself and my family this weekend.

For the more technically inclined, I present the code I usually use to create internet connections for Blackberry apps below:

public static final int CONNECTION_DEFAULT = 0;
public static final int CONNECTION_BIS = 1;
public static final int CONNECTION_BES = 2;
public static final int CONNECTION_TCPIP = 3;
public static final int CONNECTION_WIFI = 4;
public static final int CONNECTION_WAP = 5;
public static final int CONNECTION_WAP2 = 6;

public static HttpConnection makeHttpConnection(String url,
HttpHeaders requestHeaders,
byte[] postData,
int connType,
String requestMethod,
boolean uploadData)
{
HttpConnection conn = null;
OutputStream out = null;

if (StringUtilities.startsWithIgnoreCase(url, "www.")) {
url = "http://" + url;
}

try {
if (url.indexOf(";deviceside=") == -1) {
switch (connType) {
case CONNECTION_BES:
url = url + ";deviceside=false";
break;
case CONNECTION_BIS:
url = url + ";deviceside=false;connectiontype=mds-public";
break;
case CONNECTION_TCPIP:
url = url + ";deviceside=true";
break;
case CONNECTION_WIFI:
url = url + ";interface=wifi";
break;
case CONNECTION_WAP2:
url = buildWAP2(url);
break;
}
System.out.print("\n****\nThis is what we're trying to open: " + url);
}

conn = (HttpConnection) Connector.open(url, Connector.READ_WRITE);
try {
if (1==0) {
ConnectionFactory connFact = new ConnectionFactory();
TransportDescriptor[] transports = TransportInfo.getAvailableTransports();
System.out.println( "Available transports: " + transports.length );
for ( int i = 0; i < transports.length; i++ ) { TransportDescriptor t = transports[ i ]; int type = t.getTransportType(); boolean hasCoverage = TransportInfo.hasSufficientCoverage( type ); System.out.println( "Type: " + TransportInfo.getTransportTypeName( type ) ); System.out.println( "Coverage: " + hasCoverage ); System.out.println( "Cid: " + t.getCid() ); System.out.println( "Uid: " + t.getUid() ); System.out.println( "\n" ); } int preferredTransportTypes[] = {TransportInfo.TRANSPORT_BIS_B, TransportInfo.TRANSPORT_TCP_WIFI, TransportInfo.TRANSPORT_MDS, TransportInfo.TRANSPORT_WAP2, TransportInfo.TRANSPORT_TCP_CELLULAR,}; ConnectionDescriptor connDesc; connFact.setPreferredTransportTypes(preferredTransportTypes); connDesc = connFact.getConnection("http://myserver.gr"); if (connDesc != null) { conn = (HttpConnection) connDesc.getConnection(); final int code = conn.getResponseCode(); System.out.println("Code: " + code); } else { System.out.println("No connection"); } } } catch (Exception e) { System.out.println("Exception: " + e.getClass().toString() + " -> " + e.getMessage());
}

if (requestHeaders != null) {
String referer = requestHeaders.getPropertyValue("referer");

boolean sendReferrer = true;

if (referer != null &&

StringUtilities.startsWithIgnoreCase(referer, "https:") &&

!StringUtilities.startsWithIgnoreCase(url, "https:"))
{
sendReferrer = false;
}

int size = requestHeaders.size();
for (int i = 0; i < size;) {
String header = requestHeaders.getPropertyKey(i);
if (!sendReferrer && header.equals("referer")) {
requestHeaders.removeProperty(i);
--size;
continue;
}

String value = requestHeaders.getPropertyValue(i++);
if (value != null) {
conn.setRequestProperty(header, value);
}
}
}

conn.setRequestProperty("User-Agent", "Profile/MIDP-2.0 Configuration/CLDC-1.0");
conn.setRequestMethod(requestMethod);

if (requestMethod.equalsIgnoreCase("PUT") || requestMethod.equalsIgnoreCase("POST")) {

if (postData!=null) {
conn.setRequestProperty(HttpProtocolConstants.HEADER_CONTENT_LENGTH, String.valueOf(postData.length));
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
out = conn.openOutputStream();
out.write(postData);
out.flush();
}
}
}
catch (IOException e1) {
System.out.println("makeHttpConnection: " + e1.toString());
close(conn, null); // Close the connection
conn = null;
}
finally {
close(null, out); // Close the output, but keep connection open
}

return conn;
}

public static int getCoverageBasedConnectionType() {
int preferredTransportTypes[] = {TransportInfo.TRANSPORT_BIS_B, TransportInfo.TRANSPORT_TCP_WIFI, TransportInfo.TRANSPORT_MDS, TransportInfo.TRANSPORT_WAP2, TransportInfo.TRANSPORT_TCP_CELLULAR,};
ConnectionFactory cf = new ConnectionFactory();
cf.setPreferredTransportTypes(preferredTransportTypes);
ConnectionDescriptor cd = cf.getConnection("http://www.google.com");
if (cd!=null) {
TransportDescriptor transportUsed = cd.getTransportDescriptor();
int _transportMode = transportUsed.getTransportType();
System.out.println("\n\n_____\n\nTransportType: " + _transportMode + "\n\n_____\n\n");
switch (_transportMode) {
case TransportInfo.TRANSPORT_BIS_B:
System.out.println("Transport: BIS");
return CONNECTION_BIS;
case TransportInfo.TRANSPORT_MDS:
System.out.println("Transport: MDS");
return CONNECTION_BES;
case TransportInfo.TRANSPORT_TCP_CELLULAR:
System.out.println("Transport: Cell (Direct TCP)");
return CONNECTION_TCPIP;
case TransportInfo.TRANSPORT_TCP_WIFI:
System.out.println("Transport: Wi-Fi");
return CONNECTION_WIFI;
case TransportInfo.TRANSPORT_WAP:
System.out.println("Transport: WAP");
return CONNECTION_WAP;
case TransportInfo.TRANSPORT_WAP2:
System.out.println("Transport: WAP2");
return CONNECTION_WAP2;
default:
System.out.println("Transport: WAP");
return CONNECTION_TCPIP;
}
}

return CONNECTION_TCPIP;
}

static String buildWAP2(String url) {
ServiceBook sb = ServiceBook.getSB();
ServiceRecord[] records = sb.findRecordsByCid("WPTCP");
String uid = null;

for(int i=0; i < records.length; i++)
{
//Search through all service records to find the
//valid non-Wi-Fi and non-MMS
//WAP 2.0 Gateway Service Record.
if (records[i].isValid() && !records[i].isDisabled())
{

if (records[i].getUid() != null && records[i].getUid().length() != 0)
{
if ((records[i].getUid().toLowerCase().indexOf("wifi") == -1) &&
(records[i].getUid().toLowerCase().indexOf("mms") == -1))
{
uid = records[i].getUid();
break;
}
}
}
}

if (uid != null)
{

//open a WAP 2 connection
return url + ";ConnectionUID=" + uid;
}
else
{return "";}
}

Getting Paid – It’s Not Dirty

Getting Paid – It’s Not Dirty

Everyone does it.

Few speak of it openly.

Getting Paid.

We wouldn’t be here if someone didn’t get paid. Your parents did – you’re living proof.

So why are we uncomfortable talking about getting paid?

Double entendres aside, why is it so hard for people to admit that goods and services have intrinsic worth that should be fairly compensated in return for receiving said goods and services?

In part, blame Canada the Internets. Blame iTunes. Blame YouTube. Blame Twitter. Blame Facebook. Blame every “free” service you receive for the perception that content and goods have zero friction, and therefore, zero cost and value.

It’s a lie that has spread too far and too wide.

Alas, content is not free. Neither is the talent used to create said content, whether it be that favorite application you use, that web site you can’t peel yourself away from, or those songs clogging up your phone.

Things have a value, and a cost associated with creating that value.

There’s an old adage that says some people know the cost of everything but the value of nothing. On the internet, most understand this saying as “the cost of everything is nothing, therefore, the value of everything is nothing.”

Weekly, I wind up disappointing someone over this mis-perception. The stark reality is that underlying the act of creation there’s this little thing called “cost of goods sold.” And it’s never zero.

How do the creators of content go about changing the perception that everything online should be freely available, everywhere, at all times?

First, stop being a doormat by giving away your content. And second, be prepared for enduring the consequences of doing so.

Does this sound a little too “get the hell off my lawn?”

Sure it does. It comes with being a grown up.

Just like understanding there’s no such thing as Santa. Not even on the Internet.

Testing for 4G Networks in Android

Testing for 4G Networks in Android

In order to test for the presence / absence of WiMax (4G) you need to do something like the following:


private boolean isNetworkAvailable() {
ConnectivityManager connec = (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo mobileInfo = connec.getNetworkInfo(0);
NetworkInfo wifiInfo = connec.getNetworkInfo(1);
NetworkInfo wimaxInfo = connec.getNetworkInfo(6);

if (wimaxInfo!=null) {
return mobileInfo.isConnectedOrConnecting() || wifiInfo.isConnectedOrConnecting() || wimaxInfo.isConnectedOrConnecting();
}
else {
return mobileInfo.isConnectedOrConnecting() || wifiInfo.isConnectedOrConnecting();
}
}

A little hard won knowledge from the trenches.

When One-Size-Fits-All, It Never Fits Best

When One-Size-Fits-All, It Never Fits Best

Back in the late 80’s – mid 90’s, I was a director for a company that wrote a point-of-sale package for convenience stores. It was called POS-Plus (whether it was “piece of shit” plus or “point of sale” plus probably depended upon which way the wind blew – but I digress).

Anyway, one of our great ideas at the time was this: we were tasked with being able to “talk” to any type of gas pump at any convenience store across this great nation and integrate the operation into the workings of our cash register software.

Our “bright idea” was to partner with a third-party company that was a titular “universal access box” that could communicate with any pump manufacturer (at the time the big three were Tokheim, Dresser-Wayne / Schlumberger, and Gilbarco). The reasoning was that by using a universal, write once, run anywhere solution we would save ourselves time and money and abstract ourselves from the chore of having to learn the workings of each of the models of every manufacturer.

Sorta like trying to discover the Northwest Passage of universal market coverage.

Does this ring a bell? ** cough ** java ** cough ** mobile flash ** cough ** flex **.

Anyway, the “universal” solution never worked 100% of the time, and it didn’t really save us any money because when it broke we ultimate spent our spare time in front of a serial data stream datascope seeing what the pumps were really sending back to our software.

In the end, we discovered that in order to deliver the best product we could, we had to be experts in every device we purported to support.

And eventually, rather than writing a single universal controller we wound up writing three or four manufacturer specific controllers.

Eventually, we DID become experts in practically any type of gasoline pump being used, though we thought we could bypass that step and still have a great product.

We were wrong. You gotta pay your dues.

It was scary for a time that I could practically name – on sight – the make and model of any gas pump at any gas station I passed for many years, even after the company I worked for folded. Not to mention being able to read, by sight, real-time stream data between pumps and controller.

And you thought YOUR life sucked.

Long story longer, in order to provide the best user experience / product possible, in any given market, there is no commercially kick-ass development shortcut to 100% market penetration AND still have a great product that is best of breed.

You gotta pay you dues, and understand, completely, your target audience and market.

There is no universal, wonder bread solution that is going to allow you to write your world beating chinese checkers app in Flash, one time, and have it take every handset by storm.  There’s no miracle cross-compiler that is going to allow you to meta program your way into cross platform compatibility Nirvana.

You want to write a killer iPhone app? You have to do it in Objective-C and stop cry-babying about Apple not letting you do it in Flash. Want to write your app once in Java and then nominally translate minor differences between Android and Blackberry? It will never happen in your lifetime.

Immerse yourself in the Android SDK, and write a killer Android App. Devote yourself to knowing everything about RIM and the Blackberry SDK of choice – Java or Widget – and wow the world.

But stop looking for the Northwest Passage. It doesn’t exist.

Google and Android Market – Really?

Google and Android Market – Really?

I would love to search for apps on Android Market. But I can only do so from Android Market on a mobile device – not from a laptop or desktop.

Really, Google?

I would love to provide a link to my Android apps on Android Market. But, again, I can only do so from an Android Phone but not a laptop or desktop.

Really, Google?

For a company that is predicated on Search and Links, the current state of Android Market is pretty damned lame.

In so many ways, Google has a more compelling story to tell than Apple and iPhone.

If only they could finish the unfinished bits of the ecosystem.

Like the parts that enable your apps to be discovered, and downloaded.

Stupid.

Monday Morning Quarterbacking

Monday Morning Quarterbacking

I spent a great deal of time with Android over the weekend – more than anyone ever should over a long, hot, holiday weekend.

By and large, working with Android is a not unpleasant experience.

It just isn’t all that great either, in some very important aspects: screen design, camera operations, and available memory.

All Android apps are constrained to a 16MB heap space for memory. This may seem like a lot, but once an app gets up and running, this memory gets fragmented – fast – so that you dont have large available chunks of contiguous memory just laying around for allocation. Throw in a few moderately sized bitmaps, and hello Mr. OutOfMemoryException. All the bitmap recycling and garbage collection in the world can’t always keep an image bound app and on its feet without its share of crashes owing to not enough memory available from the VM to load new images. In fact, the only way to have a moderately stable image intensive app is to be diligent, garbage collect liberally, and don’t be sentimental about hanging onto anything too long in memory.

The horrible state of screen / UI design tools for Android is an embarrassment. This is not particularly surprising coming from a company slanted toward engineers. I have come to love Apple’s Interface Builder all the more because of the shit you have to put up with on Android and Blackberry. Let me just say that designing screens using an XML text editor blows more than Whale. It blows Blue Whale.

Finally, one of my more vehement dislikes about Android, is the non-consistency across handsets in how images are returned back to applications from the Camera. For iPhone developers used to the very powerful UIImagePicker interface, the operation of the Android Camera Intent is a cold splash of “what the eff is this?”

Really, gentle reader, I don’t want to bore you with all the gory details about camera handling. I’ll simply point you to one of the (sadly) more widely read threads on the topic (http://code.google.com/p/android/issues/detail?id=1480, a classic) and let all the other tortured souls in Android Camera Purgatory speak for themselves.

Given all of the preceding, there is still much to be admired about Android. The fact that new releases can be put into the wild as fast as my fat little fingers can bang out and QA code, rather than the Apple “submit and pray” model, is more than a little awesome. It’s very awesome.

The metrics I get back concerning who has bought (or bought and unbought) compared to what I get from Apple’s Financial Reports is also a great plus for the platform. I’m never really sure with Apple what these numbers represent in terms of actual activity, whereas with Google Checkout I get a scary amount of transaction activity information.

I’ve said this more than a few times: I’m a platform agnostic. I live by a very hedonistic calculus with regard to where I spend my time developing mobile applications – if the platform pays, I play.

Being an aesthete about one platform or another when all I really want to do is pay my kid’s tuition and hopefully my mortgage is a total waste of time.

But it doesn’t mean that one can’t have strong likes and dislikes about the environments you’re working on. And then spending a good chunk of a Monday morning bitching about it.

There. That’s better.