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.

Advertisements
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.