Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Java Development

Capture Signature on the BlackBerry Storm smartphone

by BlackBerry Development Advisor on ‎02-12-2010 03:20 PM - edited on ‎11-15-2011 11:26 AM by BlackBerry Development Advisor (Retired) (5,022 Views)

Summary

 

This article applies to the following:

  • BlackBerry® Device Software 4.7
  • BlackBerry® Storm™ smartphone

Details

 

The BlackBerry Storm incorporates a touchscreen which can be used to capture and collect signatures. The following sample application demonstrates how to accomplish this by using the TouchEvent API to capture touch points and then using a BitmapField to draw the signature. A complete working sample can be downloaded using the links at the bottom of this article.

 

package com.rim.samples.signaturecapture;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;

import net.rim.device.api.system.Bitmap;
import net.rim.device.api.system.Display;
import net.rim.device.api.system.EncodedImage;
import net.rim.device.api.ui.Graphics;
import net.rim.device.api.ui.MenuItem;
import net.rim.device.api.ui.TouchEvent;
import net.rim.device.api.ui.TouchGesture;
import net.rim.device.api.ui.Ui;
import net.rim.device.api.ui.UiApplication;
import net.rim.device.api.ui.component.BitmapField;
import net.rim.device.api.ui.component.Dialog;
import net.rim.device.api.ui.component.Menu;
import net.rim.device.api.ui.container.MainScreen;

/**
 * This application demonstrates the feasibility of signature capture on a
 * capacitive touch screen used in Thunder.
 * 
 * 
 */
public class SignatureCapture extends UiApplication {

	public static void main(String[] args) {
		SignatureCapture app = new SignatureCapture();
		app.enterEventDispatcher();
	}

	public SignatureCapture() {
		pushScreen(new SignatureScreen());
	}

}

/**
 * A simple screen that handles signature capture
 *
 */
final class SignatureScreen extends MainScreen {

	//Current Bitmap instance
	private Bitmap _bitmap;
	
	//A custom BitmapField for drawing a signature 
	private SignatureField _signatureField;

	/**
	 * Constructor
	 */
	public SignatureScreen() {
		super();
		setTitle("Please Sign Your Name");
		Ui.getUiEngineInstance().setAcceptableDirections(Display.DIRECTION_NORTH);
		try {
			
			//Get the local resource background image to initialize the signature field
			Class cl = Class
					.forName("com.rim.samples.signaturecapture.SignatureCapture");
			InputStream is = cl.getResourceAsStream("/background.png");
			ByteArrayOutputStream bas = new ByteArrayOutputStream();
			byte[] buffer = new byte[1024];
			while (is.read(buffer) != -1) {
				bas.write(buffer, 0, buffer.length);
			}
			EncodedImage image = EncodedImage.createEncodedImage(bas.toByteArray(), 0, bas.size());
			//Set the background as the current bitmap
			_bitmap = image.getBitmap();
		} catch (IOException e) {
			Dialog.alert(e.getMessage());
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			Dialog.alert(e.getMessage());
			e.printStackTrace();
		}
		//Create a new SignatureField with current _bitmap instance
		_signatureField = new SignatureField(_bitmap);
		add(_signatureField);
	}

	/** 
	 * MenuItem to Capture the signature and save it to an image file
	 */
	private MenuItem _captureItem = new MenuItem("Capture", 200000, 10) {
		public void run() {
			//Create a new instance of the encoder to encode the bitmap into an image
			PNGEncoder encoder = new PNGEncoder(_bitmap, true);
			try {
				byte[] imageBytes = encoder.encode(true);
				EncodedImage image = EncodedImage.createEncodedImage(imageBytes, 0, imageBytes.length);
				try {
					FileConnection fconn = (FileConnection) Connector.open(
					new StringBuffer().append("file:///store/home/user/").append("Signature-").append(System.currentTimeMillis()).append(".png").toString());
					if (!fconn.exists())
						fconn.create();
					DataOutputStream ds = fconn.openDataOutputStream();
					ds.write(image.getData());
					ds.close();
					fconn.close();
				} catch (IOException e) {
					Dialog.alert(e.getMessage());
					e.printStackTrace();
				}
			} catch (IOException e) {
				Dialog.alert(e.getMessage());
				e.printStackTrace();
			}
		}
	};

	/**
	 * MenuItem to clear the signature field
	 */
	private MenuItem _clearItem = new MenuItem("Clear", 200000, 10) {
		public void run() {
			_signatureField.clear();
		}

	};

	/**
	 * MenuItem to close the application
	 */
	private MenuItem _closeItem = new MenuItem("Close", 200000, 10) {
		public void run() {
			onClose();
		}
	};

	/**
	 * Create the menu
	 */
	protected void makeMenu(Menu menu, int instance) {
		menu.add(_captureItem);
		menu.add(_clearItem);
		menu.add(_closeItem);
	}

	final class SignatureField extends BitmapField {

		private Graphics _graphics;
		
		/**
		 * Constructor
		 */
		public SignatureField(Bitmap b) {
			this.setBitmap(b);
			_graphics = new Graphics(b);
		}

		/**
		 * Handle touch events
		 */
		protected boolean touchEvent(TouchEvent message) {
			try {
				if (message.getEvent() == TouchEvent.MOVE) { //Move event fired
					//Get the move points
					int pointsSize = message.getMovePointsSize();
					
					if (pointsSize > 1)
					{
						int[] xPoints = new int[pointsSize];
						int[] yPoints = new int[pointsSize];
						message.getMovePoints(1, xPoints, yPoints, null);
						drawPath(xPoints,yPoints);
					}
				} else if (message.getEvent() == TouchEvent.GESTURE) { //Gesture event fired
					TouchGesture gesture = message.getGesture();
					if (gesture.getEvent() == TouchGesture.TAP) { //Tap Gesture
						//Since we have a tap only draw a single point
						int xPoint = message.getX(1);
						int yPoint = message.getY(1);
						drawPoint(xPoint,yPoint);
					} else if (gesture.getEvent() == TouchGesture.SWIPE) { //Swipe Gesture
						//Get the move points
						int pointsSize = message.getMovePointsSize();
						int[] xPoints = new int[pointsSize];
						int[] yPoints = new int[pointsSize];
						message.getMovePoints(1, xPoints, yPoints, null);
						drawPath(xPoints,yPoints);
					}
				}
			} catch (Throwable e) {
				throw new RuntimeException(e.toString());
			}
			return true;
		}

		/**
		 * Draw a path through the set of points
		 */
		private void drawPath(int[] xPoints, int[] yPoints) {
			int oldColor = _graphics.getColor();
			//Draw a path through the points 
			_graphics.setColor(0x000000);
			_graphics.drawPathOutline(xPoints,yPoints, null, null, false);
			_graphics.setColor(oldColor);
			//Repaint
			invalidate();
		}
		
		/**
		 * Draw a point
		 */
		private void drawPoint(int xPoint, int yPoint) {
			int oldColor = _graphics.getColor();
			_graphics.setColor(0x000000);
			_graphics.drawPoint(xPoint, yPoint);
			_graphics.setColor(oldColor);
			//Repaint
			invalidate();
		}
		
		/**
		 * Clear the field
		 */
		private void clear() {
			int oldColor = _graphics.getColor();
			_graphics.setColor(0xFFFFFF);
			_graphics.fillRect(0, 0, _bitmap.getWidth(), _bitmap.getHeight());
			_graphics.setColor(oldColor);
			invalidate();
		}
	}
}

 

Comments
by New Developer on ‎12-03-2010 12:11 PM

This code only works if the field is at the top of the screen and is non-scrollabe. Reason is the GESTURE is getting mapped field coordinates, whereas MOVE is getting global coordinates, and there's no easy way (that I can see) of getting mapped coordinates for the getMovePoints function.