Tuesday, January 31, 2012

Animated GIF image Field With Text

This is a sample code for an animated GIF image field with a text at the bottom:
import net.rim.device.api.ui.Color;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.system.EncodedImage;
import net.rim.device.api.system.GIFEncodedImage;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.component.BitmapField;

//A field that displays an animated GIF.

public class ImageTextField extends BitmapField
{
private GIFEncodedImage gifimage; //The image to draw.
private String text;
private int currentFrame; //The current frame in the animation sequence.
private AnimatorThread animatorThread;

private final int vSpace = 6;
private final int padding = 5;

private int xImage = 0;
private int yImage = 0;
private int xText = 0;
private int yText = 0;

public ImageTextField(EncodedImage image, String txt)
{
super(image.getBitmap(), 0);
byte[] arr = image.getData();
gifimage = (GIFEncodedImage) EncodedImage.createEncodedImage(arr, 0, arr.length);
text = txt;

animatorThread = new AnimatorThread(this);
animatorThread.start();
}

public ImageTextField(EncodedImage image, String txt, long style)
{
super(image.getBitmap(), style);
byte[] arr = image.getData();
gifimage = (GIFEncodedImage) EncodedImage.createEncodedImage(arr, 0, arr.length);
text = txt;

animatorThread = new AnimatorThread(this);
animatorThread.start();
}

public int getPreferredWidth() {
return (gifimage==null ? 0 : gifimage.getWidth()) + 50;
}


public int getPreferredHeight() {
int imageHeight = gifimage==null ? 0 : gifimage.getHeight();
int textHeight = getFont().getHeight();
return imageHeight + vSpace + textHeight + 2 * padding;
}


protected void layout(int width, int height) {
width = Math.min(width, getPreferredWidth());
height = Math.min(getPreferredHeight(), height);

int imageWidth = (gifimage==null ? 0 : gifimage.getWidth());
int imageHeight = (gifimage==null ? 0 : gifimage.getHeight());

xImage = (width - imageWidth) / 2;
yImage = padding;

xText = (width - getFont().getAdvance(text)) / 2;
yText = yImage + imageHeight + vSpace;

setExtent(width, height);
}

protected void paint(Graphics graphics)
{

graphics.drawImage(xImage, yImage,
gifimage.getFrameWidth(currentFrame), gifimage.getFrameHeight(currentFrame), gifimage, currentFrame, 0, 0);

int oldColor = graphics.getColor();
graphics.setColor(Color.WHITE);
graphics.drawRoundRect(0, 0, getWidth(), getHeight(), 5, 5);
graphics.drawText(text, xText, yText);
graphics.setColor(oldColor);
}

//Stop the animation thread when the screen the field is on is
//popped off of the display stack.
protected void onUndisplay()
{
animatorThread.stop();
super.onUndisplay();
}


//A thread to handle the animation.
private class AnimatorThread extends Thread
{
private ImageTextField theImageField;
private boolean keepGoing = true;
private int totalFrames; //The total number of frames in the image.
private int loopCount; //The number of times the animation has looped (completed).
private int totalLoops; //The number of times the animation should loop (set in the image).

public AnimatorThread(ImageTextField theField)
{
theImageField = theField;
totalFrames = gifimage.getFrameCount();
totalLoops = gifimage.getIterations();

}

public synchronized void stop()
{
keepGoing = false;
}

public void run()
{
while(keepGoing)
{
//Invalidate the field so that it is redrawn.
UiApplication.getUiApplication().invokeAndWait(new Runnable()
{
public void run()
{
theImageField.invalidate();
}
});

try
{
//Sleep for the current frame delay before
//the next frame is drawn.
sleep(gifimage.getFrameDelay(currentFrame) * 10);
}
catch (InterruptedException iex)
{} //Couldn't sleep.

//Increment the frame.
++currentFrame;

if (currentFrame == totalFrames)
{
//Reset back to frame 0 if we have reached the end.
currentFrame = 0;

++loopCount;

//Check if the animation should continue.
if (loopCount == totalLoops)
{
keepGoing = false;
}
}
}
}
}
}
Now this animated image field can be added to a manager:
VerticalFieldManager mainLayout = getCustomManager();
mainLayout.setBackground(BackgroundFactory.createSolidBackground(Color.BLACK));
EncodedImage ec = EncodedImage.getEncodedImageResource("myimage.gif");
ImageTextField ag = new ImageTextField(ec, "My Text", FIELD_HCENTER);
mainLayout.add(ag);

Tuesday, January 10, 2012

Calculate Distance on Sphere ( Given latitude & longitude values)

This is the code for calculating the distance between two given points by their latitude and longitude values.
public static double getOrthodromicDistance(
double lat1,
double lon1,
double lat2,
double lon2)
{
double R = 6371.0; // km
double dLat = convertDegreeToRadians((lat2 - lat1));
double dLon = convertDegreeToRadians((lon2 - lon1));
lat1 = convertDegreeToRadians(lat1);
lat2 = convertDegreeToRadians(lat2);

double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.sin(dLon / 2) * Math.sin(dLon / 2) * Math.cos(lat1) * Math.cos(lat2);
double c = 2 * MathUtilities.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = R * c;
return distance;
}

The formula was taken from here.

The MathUtilities.atan2() method is found only in BlackBerry API 4.6 or higher See The definition of atan2 here
This can also be calculated like this:
double atan2t(double y, double x) {
double lo, hi, res, val, resval;
double ERR = 1e-9;
if (Math.abs(x) < ERR) {
if (Math.abs(y) >= ERR) {
return ((y / Math.abs(y)) * PI / 2.0);
} else {
return (0.0);
}
}

val = Math.abs(y / x);
lo = 0.0;

hi = PI / 2;
res = (lo + hi) / 2.0;
resval = Math.tan(res);

while (Math.abs(hi - lo) > ERR) {
if (resval > val) {
hi = res;
} else {
lo = res;
}

res = (hi + lo) / 2.0;
resval = Math.tan(res);
}

if (x < 0.0) {
res = PI - res;
}

if (Math.abs(y) >= ERR) {
return ((y / Math.abs(y)) * res);
} else {
return res;
}
}

Tuesday, May 31, 2011

BlackBerry Connectivity Issues

Search for Available Transports on the Device:

In order to connect to the internet from our blackberry applications we first need to search for the available transports. For a transport to be available we need to check whether there is sufficient coverage for that transport on the device and that there is a ServiceRecord available for that transport.

Searching for ServiceRecords:

The ServiceBook object should be used to get the ServiceRecords:
ServiceBook sb = ServiceBook.getSB();
ServiceRecord[] records = sb.getRecords();
Now that we have got all the service records in the records[]array we can traverse all the records and identify the available transports by checking the cid and uid of each ServiceRecords as follows:
/** Stores transport ServiceBooks if found. Otherwise, null */
private ServiceRecord srMDS, srBIS, srWAP, srWAP2, srWiFi, srUnit;
/** Flags indicating the coverage status of each transport */
private boolean
coverageTCP = false,
coverageMDS = false,
coverageBIS = false,
coverageWAP = false,
coverageWAP2 = false,
coverageWiFi = false,
coverageUnite = false;

for (int i = 0; i < records.length; i++) {
ServiceRecord myRecord = records[i];
      String cid, uid;

      if (myRecord.isValid() && !myRecord.isDisabled()) {
          cid = myRecord.getCid().toLowerCase();
          uid = myRecord.getUid().toLowerCase();
          // BIS
          if (cid.indexOf("ippp") != -1 && uid.indexOf("gpmds") != -1) {
              srBIS = myRecord;
          }

          // BES
          if (cid.indexOf("ippp") != -1 && uid.indexOf("gpmds") == -1) {
              srMDS = myRecord;
          }
          // WiFi
          if (cid.indexOf("wptcp") != -1 && uid.indexOf("wifi") != -1) {
              srWiFi = myRecord;
          }
          // Wap1.0
          if (getConfigType(myRecord) == CONFIG_TYPE_WAP && cid.equalsIgnoreCase("wap")) {
              srWAP = myRecord;
          }
          // Wap2.0
          if (cid.indexOf("wptcp") != -1 && uid.indexOf("wifi") == -1 && uid.indexOf("mms") == -1) {
              srWAP2 = myRecord;
          }
          // Unite
          if (getConfigType(myRecord) == CONFIG_TYPE_BES && myRecord.getName().equals(UNITE_NAME)) {
              srUnite = myRecord;
          }
      }
}


Searching For Coverages:

Now by using the CoverageInfo class (net.rim.device.api.system.CoverageInfo) we have to determine the transports for which we have sufficient coverage.
if (CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_BIS_B)) {
coverageBIS = true;
}

if (CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_MDS)) {
    coverageMDS = true;
    coverageUnite = true;
}
/**
 * In JDE 4.5 CoverageInfo changed the name of COVERAGE_CARRIER to COVERAGE_DIRECT
 * The constant value for both is the same, '1', so we can ALSO use 1 to avoid any
 * dependency on JDE 4.5
 */
if (CoverageInfo.isCoverageSufficient(1)) {
    coverageTCP = true;
    coverageWAP = true;
    coverageWAP2 = true;
}

//if(WLANInfo.getWLANState()==WLANInfo.WLAN_STATE_CONNECTED){  // <-- This line requires JDE 4.5 or later (WLANInfo)
if (CoverageInfo.isCoverageSufficient(1, RadioInfo.WAF_WLAN, false)) {
    coverageWiFi = true;
}

Necessary Utilities Methods are given below:

/**
     * Gets the config type of a ServiceRecord using getDataInt below
     * @param record    A ServiceRecord
     * @return  configType of the ServiceRecord
     */
    private int getConfigType(ServiceRecord record) {
        return getDataInt(record, 12);
    }

    /**
     * Gets the config type of a ServiceRecord. Passing 12 as type returns the configType.  
     * @param record    A ServiceRecord
     * @param type      dataType
     * @return  configType
     */
    private int getDataInt(ServiceRecord record, int type) {
        DataBuffer buffer = null;
        buffer = getDataBuffer(record, type);

        if (buffer != null) {
            try {
                return ConverterUtilities.readInt(buffer);
            } catch (EOFException e) {
                return -1;
            }
        }
        return -1;
    }

    /**
     * Utility Method for getDataInt()
     */
    private DataBuffer getDataBuffer(ServiceRecord record, int type) {
        byte[] data = record.getApplicationData();
        if (data != null) {
            DataBuffer buffer = new DataBuffer(data, 0, data.length, true);
            try {
                buffer.readByte();
            } catch (EOFException e1) {
                return null;
            }
            if (ConverterUtilities.findType(buffer, type)) {
                return buffer;
            }
        }
        return null;
    }
Now that we have got ServiceRecords and Coverage information for all the available transports, we can use these information to connect to the internet.

http://www.blackberry.com/developers/docs/4.7.0api/javax/microedition/io/Connector.html

Adding Connection Parameter String with the URL:

For each type of connection method specific connection parameters should be appended with the base url. The connection parameters for each transport are as follows:
MDS (BES):
connection_parameter = ";deviceside=false";

BIS:
connection_parameter = ";deviceside=false;ConnectionType=" + "mds-public";

Direct TCP:
connection_parameter = ";deviceside=true";

WAP 1.0:
connection_parameter =
  ";WapGatewayIP=" + gatewayIP
+ ";WapGatewayPort=" + gatewayPort
+ ";WapSourceIP=" +
+ ";WapSourcePort=" + sourcePort
+ ";TunnelAuthUsername=" + username
+ ";TunnelAuthPassword=" + password
+ ";WapEnableWTLS=true";

WAP 2.0:
connection_parameter = ";deviceside=true" + ((srWAP2 == null) ? "" : (";ConnectionUID=" + srWAP2.getUid()));

WiFi:
connection_parameter = ";interface=wifi";

Appending this connection_parameter  with the base url we have got the final URL (suppose full_url). An HttpConnection object can be made with this URL to start an Http Connection.

Useful links:
supportforums.blackberry.com - Different ways to make an HTTP or socket connection