DurianBerry Digest March 2011
DurianBerry Digest March 2011
DurianBerry Digest
March, 2011
HOW TO SHOW THE TIME IN OUR BLACKBERRY APP LIKE FACEBOOK DID? 11
1|DurianBerry Digest
How to solve “Attempt to push Screen
while already displayed!”?
I tried to implement a waiting screen for my Blackberry application. I wrote a class namedViewLoading.java. This
class extends PopupScreen and I put in it a LabelField with “Loading…” text and an AnimatedGIFField. For the sake
of memory usage, I just want to instantiate this screen only once for the entire application execution.
So, this are what I did:
1. Make a private variable in my ViewController (this is
my controller class for all Views to controll all screens), named loading.
2. Make two public method to show the waiting screen,
called showLoading(), and to hide it, calledhideLoading().
3. In the showLoading() method I wrote code like this:
I think that is the best practice to do it. Unfortunately it’s not. When my code tried to call this method from a
background thread, there’s an IllegalArgumentException occure. This is happen when there are more than one
background thread tried to call showLoading() at nearly the same time. I think what happen is:
Thread A called showLoading
Thread B called showLoading, at nearly the same time with (but after) A
Thread A check if loading is displayed and it received false
Thread B doing so, and received false also
Thread A trying to push loading into the screen stack, it works
Thread B trying to push loading into the screen stack (because B knows that loading is not displayed, but it’s
wrong), it failed because loading has been displayed and throw IllegalArgumentException
Here is what I did to my code to solve the problem.
2|DurianBerry Digest
Yes, I catch the Exception without do anything there. I did relatively the same for hideLoading(). Of course without the
first line. It works.
I once add System.out.println(“Ex: “+e.getMessage()); in the catch block, then I found the message is “Attempt to
push Screen while already displayed!“. Make sense isn’t it?
Although this code works, I doubt that this is the best practice for this case. Is there anyone know how to implement it
better?
I use Onyx as the standard. Onyx’s screen dimension is 480 x 360. I will design all the images used in my application
using 480 x 360. The magic number 9708 came from Onyx’s vertical resolution.
Then I will scale down the design based on device’s vertical resolution. So I need a ratio.
3|DurianBerry Digest
return f.derive(f.getStyle(), Fixed32.toInt(f.getHeight() * RATIO32),
Ui.UNITS_px);
}
Well, that’s what I do. What about you? Do you have any solution better than this?
If so, share it here!
<?php
define("FAILED_TO_POST_DATA", -1);
define("INVALID_LAC_OR_CID", -2);
define("CANNOT_DETERMINE_LOCATION", -3);
if (isset($_REQUEST["lac"]) && isset($_REQUEST["cid"])) {
$data =
"x00x0e".
"x00x00x00x00x00x00x00x00".
"x00x00".
"x00x00".
"x00x00".
"x1b".
4|DurianBerry Digest
"x00x00x00x00".
"x00x00x00x00".
"x00x00x00x00".
"x00x00".
"x00x00x00x00".
"x00x00x00x00".
"x00x00x00x00".
"x00x00x00x00".
"xffxffxffxff".
"x00x00x00x00";
$is_umts_cell = ($cid > 65535);
if ($is_umts_cell) // GSM: 4 hex digits, UTMS: 6 hex digits
$data[0x1c] = 5;
else
$data[0x1c] = 3;
$hexlac = substr("00000000".dechex($_REQUEST["lac"]),-8);
$hexcid = substr("00000000".dechex($_REQUEST["cid"]),-8);
$data[0x1f] = pack("H*",substr($hexcid,0,2));
$data[0x20] = pack("H*",substr($hexcid,2,2));
$data[0x21] = pack("H*",substr($hexcid,4,2));
$data[0x22] = pack("H*",substr($hexcid,6,2));
$data[0x23] = pack("H*",substr($hexlac,0,2));
$data[0x24] = pack("H*",substr($hexlac,2,2));
$data[0x25] = pack("H*",substr($hexlac,4,2));
$data[0x26] = pack("H*",substr($hexlac,6,2));
/* I used file_get_contents() at my laptop webserver, but it seems
like the PHP version
* at my hosting company is old and it is not supporting that.
* For the hosting company, here we're using cURL.
*/
$use_curl = false;
if ($use_curl) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, "http://www.google.com/glm/mmap");
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-type:
application/binary"));
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_POST, 1);
$response = curl_exec($ch);
if (curl_errno($ch))
exit(FAILED_TO_POST_DATA);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$str = substr($response, $header_size);
curl_close($ch);
} else {
$context = array (
'http' => array (
5|DurianBerry Digest
'method' => 'POST',
'header'=> "Content-type: application/binaryrn"
. "Content-Length: " . strlen($data) . "rn",
'content' => $data
)
);
$xcontext = stream_context_create($context);
$str=file_get_contents("http://www.google.com/glm/mmap", FALSE,
$xcontext);
}
$opcode1 = ((ord($str[0]) << 8)) | ord($str[1]);
$opcode2 = ord($str[2]);
if (($opcode1 != 0x0e) || ($opcode2 != 0x1b))
exit(INVALID_LAC_OR_CID);
$retcode = ((ord($str[3]) << 24) | (ord($str[4]) << 16) |
(ord($str[5]) << 8) | (ord($str[6])));
if ($retcode != 0)
exit(INVALID_LAC_OR_CID);
$lat = ((ord($str[7]) << 24) | (ord($str[8]) << 16) | (ord($str[9]) <<
8) | (ord($str[10]))) / 1000000;
$lon = ((ord($str[11]) << 24) | (ord($str[12]) << 16) | (ord($str[13])
<< 8) | (ord($str[14]))) / 1000000;
// exit script if cannot geocode cell e.g. not on google's database
if ($lat == 0 and $lon == 0)
exit(CANNOT_DETERMINE_LOCATION);
echo $lat . ',' . $lon;
}
?>
6|DurianBerry Digest
How to use Blackberry camera
programmatically?
Some days ago, one of Indonesia Blackberry Developer community member ask about how to capture image using
Blackberry’s camera, and then send it using email. Actually, I have a simple reusable code to do this.
Cameras are often used in today’s application since the 2010′s trend of picture sharing led by Facebook as well as
Instagram (iPhone app). So, in my humble opinion, the existance of a ready made and simple to use API for taking
picture with Blackberry is a must. I wonder why noone shared this simple code yet.
Basically, there are 2 ways to capture image with Blackberry:
1. Use native Blackberry camera application by using Invoke API.
2. Use J2ME Video Control.
So, here is my code to capture image using Blackberry. I’ll share the second way. I hope this would be usefull for
anyone need it.
package com.durianberry.nc.view;
import javax.microedition.media.Manager;
import javax.microedition.media.Player;
import javax.microedition.media.control.VideoControl;
import net.rim.device.api.system.EncodedImage;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.MainScreen;
import net.rim.device.api.ui.container.VerticalFieldManager;
/**
* NcCamera is a simple yet ready made screen to capture picture using
* Blackberry.
* @author Amri Shodiq
*/
public class NcCamera extends MainScreen {
private VideoControl videoControl;
private Field videoField;
private EncodedImage pictureTaken = null;
private boolean initialized = false;
private boolean result;
public static boolean CAPTURING_DONE = true;
public static boolean CAPTURING_CANCELED = false;
private VerticalFieldManager main;
private static NcCamera instance;
public NcCamera() {
super(NO_VERTICAL_SCROLL);
main = new VerticalFieldManager(USE_ALL_WIDTH | USE_ALL_HEIGHT) {
7|DurianBerry Digest
// I wan't to force the backgound to black
public void paintBackground(Graphics g) {
g.setBackgroundColor(0x000000);
g.clear();
}
};
super.add(main);
}
public void add(Field field) {
main.add(field);
}
public EncodedImage getPictureTaken() {
return pictureTaken;
}
public Screen getThisScreen() {
return this;
}
public void reset() {
pictureTaken = null;
result = CAPTURING_CANCELED;
}
public boolean showDialog() {
result = CAPTURING_CANCELED;
UiApplication.getUiApplication().invokeAndWait(new Runnable() {
public void run() {
UiApplication.getUiApplication().pushModalScreen(getThisScreen
());
if (pictureTaken != null) result = CAPTURING_DONE;
}
});
return result;
}
public static NcCamera getInstance() {
if (instance == null) instance = new NcCamera();
return instance;
}
public void onDisplay() {
if (!initialized) {
initializeCamera();
if (videoField!=null) add(videoField);
else {
LabelField sorry = new LabelField("Sorry, we cannot use camera
right now.") {
// I need to force the label to be white colored to be
visible above
// it's underlying manager (that is black).
public void paint(Graphics g) {
int color = g.getColor();
g.setColor(0xffffff);
super.paint(g);
8|DurianBerry Digest
g.setColor(color);
}
};
add(sorry);
}
initialized = true;
}
}
private void initializeCamera() {
try {
// Create a player for the Blackberry's camera
Player player = Manager.createPlayer("capture://video?
encoding=jpeg&width=1024&height=768");
// Set the player to the REALIZED state (see Player javadoc)
player.realize();
videoControl = (VideoControl) player.getControl("VideoControl");
if (videoControl != null) {
videoField = (Field) videoControl.initDisplayMode(
VideoControl.USE_GUI_PRIMITIVE,
"net.rim.device.api.ui.Field");
videoControl.setDisplayFullScreen(true);
videoControl.setVisible(true);
}
player.start();
} catch (Exception e) {
}
}
protected boolean invokeAction(int action) {
boolean handled = super.invokeAction(action);
if (!handled) {
switch (action) {
case ACTION_INVOKE: // Trackball click
{
takePicture();
return true;
}
}
}
return handled;
}
public boolean keyDown(int keycode,
int time) {
if (keycode == 1769472) { // escape pressed
reset();
close();
return true;
}
return super.keyDown(keycode, time);
}
9|DurianBerry Digest
public void takePicture() {
try {
// Retrieve the raw image from the VideoControl and
// create a screen to display the image to the user.
byte[] raw = videoControl.getSnapshot(null);
// save the picture taken into pictureTaken variable
pictureTaken = EncodedImage.createEncodedImage(raw, 0,
raw.length);
result = CAPTURING_DONE;
close();
} catch (Exception e) {
}
}
}
Use this code using it’s static method getInstance() that will make an instance (and only one for the entire
application, so this way should make the application memory friendly), and then call showDialog() that will push this
screen. The next step is, if the dialog returning CAPTURING_DONE constant, then we could retrieve the previous
picture taken with camera using method getPictureTaken().
Here is an example of how to use it:
if (NcCamera.getInstance().showDialog() == NcCamera.CAPTURING_DONE) {
Dialog.alert("Image captured.");
EncodedImage result = NcCamera.getInstance().getPictureTaken();
} else
Dialog.alert("Capturing canceled.");
10 | D u r i a n B e r r y D i g e s t
How to get URL picture that we post
via Graph API?
Last night I’m struggling with Facebook API for Blackberry (that is written by Eki Baskoro). Well, I need to upload a
photo to Facebook (since I have no budget to host my own photos). Unfortunately, the API (version 4.5) has not
ready yet with photo upload. So I need to implement some methods on it to make it work.
This noon I found it works (on a simulator, not tested on real device yet). Then, came the second problem (the first
was how to upload the photo). The second problem is how to get the picture’s URL, since Graph API only returns the
photo ID. The return is just like this:
{"id":1850561300489}
11 | D u r i a n B e r r y D i g e s t
4. When difference less than DateTimeUtilities.ONEMINUTE, that means the event T occured less than one
minute ago. Then, we need to know the exact number of seconds elapsed. We will divide difference
with DateTimeUtilities.ONESECOND. We save the number in avariable named timeInt. We could do the rest for
another unit of time with relatively the same way. See the code for details.
5. If the value of timeInt less than or equal to DateTimeUtilities.ONESECOND, than we could say the time is
‘just now’ or ‘one second ago’. Else, we could say ‘<timeInt> seconds ago’.
Just like that. Easy huh?
And, here is as I promise, the code:
12 | D u r i a n B e r r y D i g e s t
timeStr = " a year ago";
else
timeStr = String.valueOf(timeInt) + " years ago";
}
return timeStr;
}
13 | D u r i a n B e r r y D i g e s t
// do nothing
} else if (!path.startsWith("/"))
path = "file:///" + path;
else if (path.startsWith("//"))
path = "file:/" + path;
else
path = "file://" + path;
try {
FileConnection file = (FileConnection) Connector.open(path,
Connector.READ_WRITE);
if (file.exists()) {
file.setWritable(true);
file.delete();
}
file.close();
} catch (IOException ex) {
System.out.println("File cannot be deleted");
}
}
/**
* Method to get all bytes from a file.
*
* @param path
* @return
* @throws IOException
*/
public static byte[] getFileContent(String path) throws IOException {
FileConnection file = null;
if (path.startsWith("file://"))
path = path.substring(7);
else if (!path.startsWith("/"))
path = "/" + path;
try {
file = (FileConnection) Connector.open("file://" + path,
Connector.READ);
14 | D u r i a n B e r r y D i g e s t
int fileSize = (int) file.fileSize();
if (fileSize > 0) {
byte[] data = new byte[fileSize];
InputStream input = file.openInputStream();
input.read(data);
Thread.yield();
input.close();
return data;
} else {
throw new NullPointerException("File " + path + " is empty.");
}
} catch (IOException e) {
throw e;
} finally {
if (file != null && file.isOpen())
file.close();
}
}
/**
* To calculate how many rows needed to draw for a given text, assumed not
* contain any carriage return (enter).
* @param text
* @param font
* @param width
*/
public static int calculateRowNeeded(String text, Font font, int width) {
if (text.length() > 0) {
String[] texts = split(text, " ");
int len = texts.length;
if (len > 0) {
int i = 0;
int row = 1, left = 0, w;
while (i<len) {
w = font.getAdvance(texts[i]);
if (left + w < width) {
left += w + font.getAdvance(" ");
} else {
15 | D u r i a n B e r r y D i g e s t
left = 0;
row ++;
}
i++;
}
return row;
} else return 0;
} else return 0;
}
I know. I know.
You’ll absolutely ask me what is split() anyway? Yes, split is just another method to split a String for a given delimiters
returning array of String. Here is the code.
16 | D u r i a n B e r r y D i g e s t
}
// Declare the array with the correct
// size.
String[] strArray = new String[iOccurrences];
// Reset the indices.
iIndexOfInnerString = 0;
iIndexOfDelimiter = 0;
// Walk across the string again and this
// time add the
// strings to the array.
while ((iIndexOfDelimiter = strString.indexOf(strDelimiter,
iIndexOfInnerString)) != -1) {
// Add string to
// array.
strArray[iCounter] = strString.substring(iIndexOfInnerString,
iIndexOfDelimiter);
// Increment the
// index to the next
// character after
// the next
// delimiter.
iIndexOfInnerString = iIndexOfDelimiter + strDelimiter.length();
// Inc the counter.
iCounter += 1;
}
return strArray;
}
17 | D u r i a n B e r r y D i g e s t
potentially cause a very bad and deadly TooManyThreadErrorException. Beware, you should stop any Timer when
you no longer use it.
I wrote my own class to implement tooltip functionality. It’s not a really pretty on but you can have the idea from this
implementation. And the tooltip will not autohide, but when you leave the field, the tooltip will dissapear of course.
You can have the class as you like:
package com.durianberry.tutorial;
import net.rim.device.api.system.Display;
import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Font;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.Manager;
import net.rim.device.api.ui.Screen;
import net.rim.device.api.ui.XYRect;
import net.rim.device.api.ui.component.ButtonField;
import net.rim.device.api.ui.component.LabelField;
import net.rim.device.api.ui.container.HorizontalFieldManager;
import net.rim.device.api.ui.container.MainScreen;
/**
* You can extends BasicScreen to implement your own screens
* that will have tooltip functionality. Of course, you need to
* remove the lines inside BasicScreen's constructor.
*
* @author Amri Shodiq
*
*/
public class BasicScreen extends MainScreen {
public BasicScreen() {
/**
* Here is example of how to use it. Pay attention to
* the first LabelField (labelled with 'B') and
* the ButtonField. We need to override their onFocus()
* and onUnfocus() method to get this work.
*
* Remove these lines if you wan't to use it in your
* projects.
*/
HorizontalFieldManager h = new HorizontalFieldManager(
USE_ALL_WIDTH);
h.setPadding(10, 10, 10, 10);
h.add(new LabelField(" A ", FOCUSABLE));
h.add(new LabelField(" B ", FOCUSABLE) {
protected void onFocus(int direction) {
super.onFocus(direction);
showTooltip("B focused", this);
}
protected void onUnfocus() {
super.onUnfocus();
hideTooltip();
}
});
18 | D u r i a n B e r r y D i g e s t
h.add(new LabelField(" C ", FOCUSABLE) {
protected void onFocus(int direction) {
super.onFocus(direction);
showTooltip("C focused", this);
}
protected void onUnfocus() {
super.onUnfocus();
hideTooltip();
}
});
add(h);
ButtonField button = new ButtonField("Focus this to show " +
"tooltip") {
protected void onFocus(int direction) {
super.onFocus(direction);
showTooltip("Now, the button is focused", this);
}
protected void onUnfocus() {
super.onUnfocus();
hideTooltip();
}
};
// make distance with the labels above
button.setMargin(50, 10, 10, 10);
add(button);
add(new LabelField("And another thing", FOCUSABLE));
}
public void paint(Graphics g) {
super.paint(g);
// Three lines below to show tooltip as needed.
if (showTooltip) {
drawTooltip(g);
}
}
/*************************************************************
*
* This lines below used to implement tooltip functionality. *
*
*************************************************************/
/**
* Getting exact location and dimension of a given field.
* We need this to place our tooltip below the given field.
*/
protected XYRect getFieldExtent(Field field) {
int cy = field.getTop();
int cx = field.getLeft();
Manager m = field.getManager();
while (m != null) {
cy += m.getTop() - m.getVerticalScroll();
19 | D u r i a n B e r r y D i g e s t
cx += m.getLeft() - m.getHorizontalScroll();
if (m instanceof Screen)
break;
m = m.getManager();
}
return new XYRect(cx, cy, field.getWidth(), field
.getHeight());
}
private Font tooltipFont = getFont(); // initialized with default screen's
font
private String tooltipText;
private int tooltipX, tooltipY, count, tooltipYDistance =
ScaleTools.scaleInt(4);
private boolean showTooltip;
private int tooltipBgColor = 0x000000, tooltipBorderColor = 0x666666,
tooltipFontColor = 0xFFFFFF, tooltipAlpha = 0xAA;
protected void hideTooltip() {
showTooltip = false;
invalidate();
}
protected void showTooltip(String text, Field attachedTo) {
if (showTooltip) hideTooltip();
tooltipText = text;
XYRect fieldPosition = getFieldExtent(attachedTo);
// to get tooltip's center point
tooltipX = fieldPosition.x + fieldPosition.width / 2;
// this line make tooltip placed under the attached field
tooltipY = fieldPosition.y + attachedTo.getHeight();
showTooltip = true;
invalidate();
}
protected void drawTooltip(Graphics g) {
int oldColor = g.getColor();
Font font = tooltipFont;
int padding = ScaleTools.scaleInt(8);
int borderSize = ScaleTools.scaleInt(2);
g.setFont(font);
int x = tooltipX -
(g.getFont().getAdvance(tooltipText) + padding) / 2;
int y = tooltipY + tooltipYDistance;
int width = font.getAdvance(tooltipText) + 2 * padding;
int height = font.getHeight() + 2 * padding;
// keep the tooltip inside screen's width range
if ((x + width) > Display.getWidth()) {
x = Display.getWidth() - padding - width;
}
20 | D u r i a n B e r r y D i g e s t
if (x < 0) {
x = padding;
}
// we make a semi transparent rounded rectangle with border
g.setGlobalAlpha(tooltipAlpha);
g.setColor(tooltipBorderColor);
g.fillRoundRect(x, y, width, height, font.getHeight(),
font.getHeight());
g.setColor(tooltipBgColor);
g.fillRoundRect(x + borderSize, y + borderSize,
width - 2 * borderSize, height - 2 * borderSize,
font.getHeight() - borderSize,
font.getHeight() - borderSize);
// we make the text's color solid so user will see it clearly
g.setGlobalAlpha(0xFF);
g.setColor(tooltipFontColor);
g.drawText(tooltipText, x + padding, y + padding);
g.setFont(font);
g.setColor(oldColor);
}
// This lines provide functionalities to customize tooltip's
// appearance
public void setTooltipYDistance(int tooltipYDistance) {
this.tooltipYDistance = tooltipYDistance;
}
public void setTooltipBgColor(int tooltipBgColor) {
this.tooltipBgColor = tooltipBgColor;
}
public void setTooltipBorderColor(int tooltipBorderColor) {
this.tooltipBorderColor = tooltipBorderColor;
}
public void setTooltipFontColor(int tooltipFontColor) {
this.tooltipFontColor = tooltipFontColor;
}
public void setTooltipAlpha(int tooltipAlpha) {
this.tooltipAlpha = tooltipAlpha;
}
public void setTooltipFont(Font tooltipFont) {
this.tooltipFont = tooltipFont;
}
}
21 | D u r i a n B e r r y D i g e s t
package com.durianberry.tutorial;
import net.rim.device.api.ui.UiApplication;
public class AppMain extends UiApplication {
public AppMain() {
pushScreen(new BasicScreen());
}
public static void main(String[] args) {
new AppMain().enterEventDispatcher();
}
}
What do you think? Is it helpful? Do not hesitate to press Like button below or post any comment.
22 | D u r i a n B e r r y D i g e s t