package net.sharenav.sharenav.ui;
/*
* ShareNav - Copyright (c) 2008 Harald Mueller apm at users dot sourceforge dot net
* See Copying
*/
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
//#if polish.api.fileConnection
import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
//#endif
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Choice;
import javax.microedition.lcdui.ChoiceGroup;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Item;
import javax.microedition.lcdui.ItemCommandListener;
import javax.microedition.lcdui.TextField;
//#if polish.api.mmapi
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.media.control.VideoControl;
//#if polish.api.advancedmultimedia
import javax.microedition.amms.control.ImageFormatControl;
import javax.microedition.amms.control.camera.CameraControl;
import javax.microedition.amms.control.camera.FocusControl;
import javax.microedition.amms.control.camera.SnapshotControl;
//#endif
//#endif
import net.sharenav.sharenav.data.Configuration;
import net.sharenav.sharenav.data.Position;
import net.sharenav.sharenav.ui.ShareNavMenu;
import net.sharenav.midlet.ui.SelectionListener;
import net.sharenav.util.HelperRoutines;
import net.sharenav.util.Logger;
import net.sharenav.util.MoreMath;
import net.sharenav.util.StringTokenizer;
import de.enough.polish.util.Locale;
//#if polish.android
import android.content.Context;
import android.graphics.Color;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnKeyListener;
import android.view.ViewGroup;
import de.enough.polish.android.midlet.MidletBridge;
import java.lang.reflect.Method;
//#endif
/**
*
* This class provides a camera capture form based on jsr-135 and jsr-234
* It allows the user to capture pictures with a builtin camera
* directly from with in ShareNav.
*
* The idea is to directly be able to geotag images from within ShareNav
* for example when survaying for OSM.
*
*/
public class GuiCamera extends Canvas implements CommandListener, ItemCommandListener, GuiCameraInterface, SelectionListener, ShareNavDisplayable
//#if polish.android
, Camera.PictureCallback
//#endif
{
private final Command CANCEL_CMD = new Command(Locale.get("generic.Cancel")/*Cancel*/, ShareNavMenu.BACK, 5);
private final Command OK_CMD = new Command(Locale.get("generic.OK")/*Ok*/, ShareNavMenu.OK, 5);
private final Command OK2_CMD = new Command(Locale.get("generic.OK")/*Ok*/, ShareNavMenu.OK, 5);
private final Command CAPTURE_CMD = new Command(Locale.get("guicamera.Capture")/*Capture*/, Command.OK, 5);
private final Command STORE_CMD = new Command(Locale.get("guicamera.SelectDir")/*Select directory*/, Command.ITEM, 5);
public final Command SETUP_CMD = new Command(Locale.get("guicamera.Setup")/*Setup*/, Command.ITEM, 6);
private volatile static GuiCamera instance;
private final static Logger logger = Logger.getInstance(GuiCamera.class,
Logger.DEBUG);
//#if polish.api.mmapi
//#if polish.android
private Camera camera;
private Player mPlayer;
private VideoControl video;
//#else
private Player mPlayer;
private VideoControl video;
//#endif
//#if polish.api.advancedmultimedia
private FocusControl focus;
//#endif
//#endif
private String encoding;
// if parent == null, called from GuiDiscover
// FIXME Maybe can be simplifies with setting up a simple setup/init ShareNavDiscoverable instead of separate
// Trace and GuiDiscover variables
private Trace parent;
private GuiDiscover setupParent;
private String basedirectory;
private ChoiceGroup selectJsrCG;
private ChoiceGroup selectExifCG;
private TextField encodingTF;
private ChoiceGroup encodingCG;
private byte[] photo;
//#if polish.android
private SurfaceHolder surfaceHolder;
private SurfaceView surfaceView = null;
private View uiView = null;
//#endif
public void init(Trace parent) {
this.parent = parent;
addCommand(CANCEL_CMD);
addCommand(CAPTURE_CMD);
//addCommand(STORE_CMD);
//#if polish.android
//#else
addCommand(SETUP_CMD);
//#endif
setCommandListener(this);
setUpCamera();
setFullScreenMode(Configuration.getCfgBitState(Configuration.CFGBIT_FULLSCREEN));
instance = this;
}
public static GuiCamera getInstance() {
return instance;
}
//#if polish.android
protected void setDisplayOrientation(Camera camera, int angle){
Method downPolymorphic;
try
{
downPolymorphic = camera.getClass().getMethod("setDisplayOrientation", new Class[] { int.class });
if (downPolymorphic != null) {
if (angle == 270) {
angle = 360 - angle;
}
downPolymorphic.invoke(camera, new Object[] { angle });
}
}
catch (Exception e1)
{
}
}
SurfaceHolder.Callback surfaceCallback=new SurfaceHolder.Callback() {
public void surfaceCreated(SurfaceHolder holder) {
}
public void surfaceChanged(SurfaceHolder holder,
int format, int width,
int height) {
startPreview();
surfaceView.setFocusable(true);
surfaceView.setFocusableInTouchMode(true);
surfaceView.requestFocus();
surfaceView.setOnKeyListener(new OnKeyListener()
{
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
keyReleased(keyCode);
return true;
}
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
}
return false;
}
});
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
};
public void startPreview() {
try {
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException ioe) {
//
}
surfaceView.requestFocus();
setDisplayOrientation(camera, (360 + Trace.getInstance().getAndroidRotationAngle() - 90) % 360);
camera.startPreview();
}
//#endif
/*
* This sets up the basic parameters of the camera and initialises
* the view finder of the camera
*/
private void setUpCamera() {
//#if polish.api.mmapi
try {
basedirectory = Configuration.getPhotoUrl();
//#debug debug
logger.debug("Storing photos at " + basedirectory);
//#if polish.android
// open the first camera
// between 0 and getNumberOfCameras()-1.
try {
camera = Camera.open(0);
} catch (RuntimeException re) {
logger.error(Locale.get("guicamera.CouldntInitializeCameraPlayer")/*Could not initialize camera player*/);
return;
}
uiView = MidletBridge.instance.getWindow().getCurrentFocus();
surfaceView = new SurfaceView(MidletBridge.instance.getWindow().getContext());
Camera.Parameters params = camera.getParameters();
surfaceHolder = surfaceView.getHolder();
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
surfaceHolder.addCallback(surfaceCallback);
MidletBridge.instance.getWindow().setContentView(surfaceView);
//#else
try {
/**
* Nokia seems to have used a non standard locator to specify
* the Player for capturing photos
*/
//#if polish.android
// Android doesn't work currently (2012-03-29) at all, but if it will be added to J2MEPolish,
// it probably won't be the non-Standard Nokia way
mPlayer = Manager.createPlayer("capture://video");
//#else
mPlayer = Manager.createPlayer("capture://image");
//#endif
} catch (MediaException me) {
/**
* This is the standard locator.
* As Nokia also supports the capture://video, but does not
* allow to take snapshots with it, need to use this ordering
*/
mPlayer = Manager.createPlayer("capture://video");
}
if (mPlayer == null) {
logger.error(Locale.get("guicamera.CouldntInitializeCameraPlayer")/*Could not initialize camera player*/);
return;
}
// Then again Samsung (Xcover 271) doesn't complain on creating capture://image, but barfs later, so we'll try to catch that one here
try {
mPlayer.realize();
video = (VideoControl) mPlayer
.getControl("VideoControl");
video.initDisplayMode(VideoControl.USE_DIRECT_VIDEO, this);
video.setDisplayFullScreen(true);
video.setVisible(true);
mPlayer.start();
} catch (MediaException me) {
mPlayer = Manager.createPlayer("capture://video");
mPlayer.realize();
video = (VideoControl) mPlayer
.getControl("VideoControl");
video.initDisplayMode(VideoControl.USE_DIRECT_VIDEO, this);
video.setDisplayFullScreen(true);
video.setVisible(true);
mPlayer.start();
}
if (mPlayer == null) {
logger.error(Locale.get("guicamera.CouldntInitializeCameraPlayer")/*Could not initialize camera player*/);
return;
}
//#endif
//#if polish.api.advancedmultimedia
CameraControl camera = (CameraControl) mPlayer
.getControl("CameraControl");
//#debug trace
logger.trace("Initialised Camera: " + camera);
if (camera != null) {
int[] res = camera.getSupportedStillResolutions();
for (int i = 0; i < res.length; i++) {
//#debug debug
logger.debug("res: " + res[i]);
}
camera.setStillResolution(res.length / 2 - 1);
//#debug debug
logger.debug("Resolution: " + camera.getStillResolution());
} else {
logger.error(Locale.get("guicamera.CantGetAccessToCameraProp")/*Can not get access to camera properties*/);
}
ImageFormatControl format = (ImageFormatControl) mPlayer
.getControl("ImageFormatControl");
if (format != null) {
//#mdebug
logger.debug("Format: " + format.getFormat());
logger.debug("MeteDataSupportMode: "
+ format.getMetadataSupportMode());
String[] formats = format.getSupportedFormats();
for (int i = 0; i < formats.length; i++) {
logger.debug("Supported format: " + formats[i]);
}
String[] metaData = format.getSupportedMetadataKeys();
if (metaData != null) {
for (int i = 0; i < metaData.length; i++) {
logger.debug("Supported metadata: " + metaData[i]);
}
}
//#enddebug
} else {
logger.debug("Device does not support ImageFormatControl");
}
//#endif
} catch (SecurityException se) {
logger.exception(Locale.get("guicamera.SecurityException")/*Security Exception: */, se);
//#if polish.android
//#else
mPlayer = null;
video = null;
//#endif
//#if polish.android
//#else
} catch (IOException e) {
logger.exception(Locale.get("guicamera.IOexception")/*IOexception*/, e);
//#if polish.android
//#else
mPlayer = null;
video = null;
//#endif
} catch (MediaException e) {
logger.exception(Locale.get("guicamera.MediaException")/*MediaException*/, e);
//#if polish.android
//#else
mPlayer = null;
video = null;
//#endif
logger.error(Locale.get("guicamera.CameraControlNotSupported")/*Camera control is not supported by this device*/);
//#endif polish.android
}
//#endif
}
//#if polish.android
private void exitPreview() {
if (uiView != null) {
surfaceView.setVisibility(View.GONE);
if (uiView.getParent() != null) {
((ViewGroup)uiView.getParent()).removeView(uiView);
}
if (surfaceView != null && surfaceView.getParent() != null) {
((ViewGroup)surfaceView.getParent()).removeView(surfaceView);
}
MidletBridge.instance.getWindow().setContentView(uiView);
uiView = null;
}
}
public void sizeChanged(int w, int h) {
Trace.getInstance().sizeChanged(w, h);
if (camera != null) {
setDisplayOrientation(camera, (360 + Trace.getInstance().getAndroidRotationAngle() - 90) % 360);
}
}
public void onPictureTaken(byte[] data, Camera camera) {
photo = data;
addCommand(OK2_CMD);
//ShareNav.getInstance().show(this);
try {
int idx = 0;
//repaint();
if (Configuration.getCfgBitState(Configuration.CFGBIT_ADD_EXIF)) {
photo = addExifEncoding(photo);
}
//#debug debug
logger.debug("Captured photo of size : " + photo.length);
FileConnection fc = (FileConnection)Connector.open(basedirectory + "ShareNav-" + HelperRoutines.formatInt2(idx) + "-" + HelperRoutines.formatSimpleDateSecondNow() + ".jpg");
while (fc.exists()) {
fc = (FileConnection)Connector.open(basedirectory + "ShareNav-" + HelperRoutines.formatInt2(idx) + "-" + HelperRoutines.formatSimpleDateSecondNow() + ".jpg");
idx++;
}
fc.create();
OutputStream fos = fc.openOutputStream();
fos.write(photo, 0, photo.length);
fos.close();
//video.setVisible(true);
} catch (IOException e) {
logger.exception(Locale.get("guicamera.IOExceptionCapturingPhoto")/*IOException capturing the photo*/, e);
} catch (NullPointerException npe) {
logger.exception(Locale.get("guicamera.FailedToTakePicture")/*Failed to take a picture*/, npe);
}
cameraOff(null);
parent.show();
}
//#endif
private void takePicture135() throws SecurityException {
logger.info("Captureing photo with jsr 135");
//#if polish.api.mmapi
//#if polish.android
//#else
if (mPlayer == null || video == null) {
logger.error(Locale.get("guicamera.mPlayerNotInitedCaptureFail")/*mPlayer is not initialised, could not capture photo*/);
return;
}
//#endif
try {
//#if polish.android
camera.takePicture(null, null, null, this);
} finally {
}
//#else
int idx = 0;
byte [] photo = video.getSnapshot(Configuration.getPhotoEncoding());
repaint();
if (Configuration.getCfgBitState(Configuration.CFGBIT_ADD_EXIF)) {
photo = addExifEncoding(photo);
}
//#debug debug
logger.debug("Captured photo of size : " + photo.length);
FileConnection fc = (FileConnection)Connector.open(basedirectory + "ShareNav-" + HelperRoutines.formatInt2(idx) + "-" + HelperRoutines.formatSimpleDateSecondNow() + ".jpg");
while (fc.exists()) {
fc = (FileConnection)Connector.open(basedirectory + "ShareNav-" + HelperRoutines.formatInt2(idx) + "-" + HelperRoutines.formatSimpleDateSecondNow() + ".jpg");
idx++;
}
fc.create();
OutputStream fos = fc.openOutputStream();
fos.write(photo, 0, photo.length);
fos.close();
video.setVisible(true);
} catch (MediaException e) {
logger.exception(Locale.get("guicamera.CouldntTakePicture")/*Could not take picture*/, e);
} catch (IOException e) {
logger.exception(Locale.get("guicamera.IOExceptionCapturingPhoto")/*IOException capturing the photo*/, e);
} catch (NullPointerException npe) {
logger.exception(Locale.get("guicamera.FailedToTakePicture")/*Failed to take a picture*/, npe);
}
//#endif
//#endif
}
private void takePicture234() throws SecurityException {
logger.info("Captureing photo with jsr 234");
//#if polish.api.mmapi && polish.api.advancedmultimedia
if (mPlayer == null) {
logger.error(Locale.get("guicamera.mPlayerNotInitedCaptureFail")/*mPlayer is not initialised, could not capture photo*/);
return;
}
try {
if (focus == null) {
focus = (FocusControl) mPlayer.getControl("FocusControl");
}
if (focus != null) {
if (focus.isAutoFocusSupported()) {
focus.setFocus(FocusControl.AUTO);
} else {
// Otherwise, try the "mountain" or infinity setting. Find out what was actually set.
int focusSet = focus.setFocus(Integer.MAX_VALUE);
}
} else {
//#debug debug
logger.debug("Could not get focus control");
}
//#debug trace
logger.trace("Finished focusing");
SnapshotControl snapshot = (SnapshotControl) mPlayer
.getControl("SnapshotControl");
if (snapshot == null) {
logger.info("Could not aquire SnapshotControl, falling back to jsr135");
takePicture135();
return;
}
String basedir = basedirectory.substring(8,basedirectory.length());
try {
snapshot.setDirectory(basedir);
snapshot.setFilePrefix("ShareNav-"
+ HelperRoutines.formatSimpleDateSecondNow() + "-");
snapshot.setFileSuffix(".jpg");
} catch (Exception e) {
logger.exception(Locale.get("guicamera.FailedSettingDirectory")/*Failed to set directory*/, e);
}
//#debug
logger.debug("About to capture to file " + snapshot.getDirectory() + snapshot.getFilePrefix());
// Take one picture and allow the user to keep or discard it
snapshot.start(SnapshotControl.FREEZE);
//#if polish.api.pdaapi
if (Configuration.getCfgBitState(Configuration.CFGBIT_ADD_EXIF)) {
try {
Thread.sleep(3000);
FileConnection fcDir = (FileConnection)Connector.open(basedirectory);
Enumeration filelist = fcDir.list();
while (filelist.hasMoreElements()) {
String fileName = (String) filelist.nextElement();
logger.debug("File in camera dir: " + fileName);
if (fileName.indexOf(snapshot.getFilePrefix()) >= 0) {
logger.debug("Found photo, adding Exif: " + fileName);
FileConnection fcIn = (FileConnection)Connector.open(basedirectory + fileName);
InputStream is = fcIn.openInputStream();
byte [] image = new byte[(int)fcIn.fileSize()];
is.read(image);
FileConnection fcOut = (FileConnection)Connector.open(basedirectory + fileName + "-exif.jpg");
fcOut.create();
OutputStream os = fcOut.openOutputStream();
os.write(addExifEncoding(image));
os.close();
is.close();
return;
}
}
logger.info("Could not find the image file to add exif information");
} catch (IOException ioe) {
logger.exception(Locale.get("guicamera.TryingToReadFileAfterPicture")/*Trying to read file after picture*/, ioe);
} catch (InterruptedException ie) {
logger.info("Sleep was interupted");
}
}
//#endif
} catch (MediaException e) {
logger.exception(Locale.get("guicamera.CouldntCapturePhoto")/*Could not capture photo*/, e);
}
//#else
logger.error(Locale.get("guicamera.JSR234SupportNotAvailable")/*JSR-234 support is not compiled into this MIDlet. Please use JSR-135*/);
//#endif
}
private void takePicture() {
try {
if (Configuration.getCfgBitState(Configuration.CFGBIT_USE_JSR_234)) {
takePicture234();
} else {
takePicture135();
}
} catch (SecurityException se) {
logger.error(Locale.get("guicamera.PermissionDeniedPhoto")/*Permission denied to take a photo*/);
}
}
protected void paint(Graphics g) {
//It looks like it is necessary to do something
//in this method, otherwise the video display gets
//confused after taking a picture. So display a simple
//"Progress Display"
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(0);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(255,0,0);
g.drawRect(5, 5, getWidth() - 10, getHeight() - 10);
g.drawRect(6, 6, getWidth() - 12, getHeight() - 12);
g.drawRect(7, 7, getWidth() - 14, getHeight() - 14);
g.setColor(0,255,0);
//#if polish.android
g.drawString(Locale.get("guicamera.SavingPhotoAndroid")/*Saving photo...OK to continue*/, getWidth()/2, getHeight()/2, Graphics.BASELINE | Graphics.HCENTER);
//#else
g.drawString(Locale.get("guicamera.SavingPhoto")/*Saving photo...*/, getWidth()/2, getHeight()/2, Graphics.BASELINE | Graphics.HCENTER);
//#endif
}
public void keyPressed(int keyCode) {
logger.info("Pressed key code " + keyCode + " in Camera GUI");
if ((getGameAction(keyCode) == FIRE) || (keyCode == Configuration.KEYCODE_CAMERA_CAPTURE)) {
commandAction(CAPTURE_CMD, (Displayable) null);
}
if (keyCode == Configuration.KEYCODE_CAMERA_COVER_CLOSE) {
//#if polish.api.mmapi
if (mPlayer != null) {
mPlayer.close();
}
//#endif
parent.show();
}
}
//#if polish.android
// See http://developer.android.com/sdk/android-2.0.html
// for a possible Native Android problem & workaround
public void keyReleased(int keyCode) {
logger.info("Released key code " + keyCode + " in Camera GUI");
if (keyCode == KeyEvent.KEYCODE_BACK) {
cameraOff(null);
parent.show();
}
}
//#endif
public void cameraOff(Displayable disp) {
//#if polish.android
if (camera != null) {
camera.stopPreview();
camera.release();
camera = null;
}
exitPreview();
//#endif
if (disp == this || disp == null) {
//#if polish.api.mmapi
if (mPlayer != null) {
mPlayer.close();
}
//#endif
}
}
public void commandAction(Command c, Item i) {
// forward item command action to form
commandAction(c, (Displayable) null);
}
public void commandAction(Command c, Displayable disp) {
if (c == CANCEL_CMD || c == OK2_CMD) {
cameraOff(disp);
if (disp == this) {
parent.show();
} else {
if (parent == null) {
// return to setup
setupParent.show();
} else {
this.show();
}
}
}
//#if polish.api.mmapi
if (c == CAPTURE_CMD) {
if ((basedirectory == null) ||(!basedirectory.startsWith("file:///"))) {
logger.error(Locale.get("guicamera.SelectDirFirst")/*You need to select a directory where to save first*/);
return;
}
if (parent != null) {
takePicture();
}
}
if (c == STORE_CMD) {
//#if polish.api.fileConnection
if (parent == null) {
// return to setup
setupParent.show();
} else {
this.show();
}
new FsDiscover(parent == null ? (ShareNavDisplayable) setupParent : (ShareNavDisplayable) this,
this, basedirectory,FsDiscover.CHOOSE_DIRONLY,null,"Media Store Directory");
//#endif
}
if (c == SETUP_CMD) {
logger.info("Starting Setup dialog");
if ((mPlayer != null) && (video != null)) {
try {
mPlayer.stop();
video.setVisible(false);
} catch (MediaException e) {
logger.exception(Locale.get("guicamera.CouldNotStopCameraViewer")/*Could not stop camera viewer*/, e);
}
}
basedirectory = Configuration.getPhotoUrl();
Form setupDialog = new Form(Locale.get("guicamera.Setup")/*Setup*/);
setupDialog.addCommand(CANCEL_CMD);
setupDialog.addCommand(OK_CMD);
setupDialog.addCommand(STORE_CMD);
setupDialog.setCommandListener(this);
/**
* Setup JSR selector
*/
String [] selectJsr = {"JSR-135", "JSR-234"};
selectJsrCG = new ChoiceGroup(Locale.get("guicamera.PicturesVia")/*Pictures via...*/, Choice.EXCLUSIVE, selectJsr ,null);
if (Configuration.getCfgBitState(Configuration.CFGBIT_USE_JSR_234)) {
selectJsrCG.setSelectedIndex(1, true);
} else {
selectJsrCG.setSelectedIndex(0, true);
}
/**
* Setup Exif selector
*/
String [] selectExif = {Locale.get("guicamera.AddExif")/*Add exif*/};
selectExifCG = new ChoiceGroup(Locale.get("guicamera.Geocoding")/*Geocoding*/, Choice.MULTIPLE,selectExif,null);
boolean [] selExif = new boolean[1];
selExif[0] = Configuration.getCfgBitState(Configuration.CFGBIT_ADD_EXIF);
selectExifCG.setSelectedFlags(selExif);
/**
* Setup Encoding
*/
encodingTF = new TextField(Locale.get("guicamera.EncodingString")/*Encoding string: */, Configuration.getPhotoEncoding() , 100 ,TextField.ANY);
String encodings = null;
try {
encodings = System.getProperty("video.snapshot.encodings");
logger.debug("Encodings: " + encodings);
} catch (Exception e) {
logger.info("Device does not support the encoding property");
}
String [] encStrings = new String[0];
String setEnc = Configuration.getPhotoEncoding();
if (setEnc == null) {
setEnc = "";
}
int encodingSel = -1;
if (encodings != null) {
encStrings = StringTokenizer.getArray(encodings, " ");
for (int i = 0; i < encStrings.length; i++) {
logger.debug("Enc: " + encStrings[i]);
if (setEnc.equalsIgnoreCase(encStrings[i])) {
encodingSel = i;
logger.debug("Enc Sel: " + encStrings[i]);
}
}
} else {
encStrings = new String [0];
}
if (encodingSel == -1) {
encodingSel = encStrings.length;
}
String [] tmp = new String[encStrings.length + 1];
System.arraycopy(encStrings, 0, tmp, 0, encStrings.length);
tmp[encStrings.length] = Locale.get("guicamera.Custom")/*Custom*/;
encStrings = tmp;
encodingCG = new ChoiceGroup(Locale.get("guicamera.SelectEncoding")/*Select encoding: */, Choice.EXCLUSIVE, encStrings, null);
encodingCG.setSelectedIndex(encodingSel, true);
/**
* Setup custom encoding text field
*/
TextField storageDir = new TextField(Locale.get("guicamera.store")/*store: */, basedirectory, 100, TextField.UNEDITABLE);
storageDir.setDefaultCommand(STORE_CMD);
storageDir.setItemCommandListener(this);
setupDialog.append(selectJsrCG);
setupDialog.append(selectExifCG);
setupDialog.append(encodingCG);
setupDialog.append(encodingTF);
setupDialog.append(storageDir);
ShareNav.getInstance().show(setupDialog);
//#debug trace
logger.trace("Showing Setup dialog");
}
/**
* OK command for the setup dialog
*/
if (c == OK_CMD) {
if (selectJsrCG.getSelectedIndex() == 1) {
Configuration.setCfgBitState(Configuration.CFGBIT_USE_JSR_234, true, true);
} else {
Configuration.setCfgBitState(Configuration.CFGBIT_USE_JSR_234, false, true);
}
boolean [] selExif = new boolean[1];
selectExifCG.getSelectedFlags(selExif);
if (selExif[0]) {
Configuration.setCfgBitState(Configuration.CFGBIT_ADD_EXIF, true, true);
} else {
Configuration.setCfgBitState(Configuration.CFGBIT_ADD_EXIF, false, true);
}
String encType = encodingCG.getString(encodingCG.getSelectedIndex());
if (encType.equals(Locale.get("guicamera.Custom")/*Custom*/)) {
encType = encodingTF.getString();
}
Configuration.setPhotoEncoding(encType);
if (parent == null) {
// return to setup
setupParent.show();
} else {
this.show();
}
}
//#endif
}
/**
* Add a EXIF header to a JPEG byte array containing information about the current
* GPS position and height. This is currently not particularly smart, as it does
* not try and parse any information, but simply copies in a binary blob containing
* the exif header to the start of the jpg file. It does not check if the data
* already contains an exif header.
* @param jpgImage
* @return
*/
private byte [] addExifEncoding(byte [] jpgImage) {
byte [] newImage = new byte[jpgImage.length + 201];
/**
* Exif binary blob:
* byte value comment
* 0 - 1 0xff 0xd8: Start of image marker. Must be the first 2 bytes in a jpeg file
* 2 - 3 0xff 0xe1: App1 (exif) marker
* 4 - 5 : length of the exif block including these two bytes (199 in the case of this block)
*/
byte [] tmp = {
(byte) 0xff, (byte) 0xd8, (byte)0xff,(byte)0xe1,0x00,(byte)0xc7,0x45,0x78,0x69,0x66,0x00,0x00,0x49,0x49,
0x2a, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x25, (byte)0x88, 0x04, 0x00, 0x01, 0x00, 0x00, 0x00,
0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x04, 0x00,
0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4e, 0x00,
0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00, 0x00, (byte)0x80, 0x00, 0x00, 0x00, 0x03, 0x00,
0x02, 0x00, 0x02, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x00, 0x04, 0x00, 0x05, 0x00, 0x03, 0x00,
0x00, 0x00, (byte)0x98, 0x00, 0x00, 0x00, 0x05, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x06, 0x00, 0x05, 0x00, 0x01, 0x00, 0x00, 0x00, (byte)0xb0, 0x00, 0x00, 0x00, 0x12, 0x00,
0x02, 0x00, 0x07, 0x00, 0x00, 0x00, (byte)0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2e, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, (byte)0xa9, 0x31, 0x3b, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x00, 0x00,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x07, 0x78,
0x72, 0x00, 0x40, 0x42, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, (byte)0xd2, 0x04,
0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x57, 0x47, 0x53, 0x2d, 0x38, 0x34, 0x00};
/**
* The exif header must be directly after the Start of image marker. and is 201 bytes long
*/
System.arraycopy(jpgImage, 2, newImage, 203, jpgImage.length - 2);
System.arraycopy(tmp,0,newImage,0,203);
Position pos = parent.getCurrentPosition();
int altitude = (int)pos.altitude;
HelperRoutines.copyInt2ByteArray(newImage, 188, altitude);
float lat = pos.latitude;
float lon = pos.longitude;
if (lat > 0) {
newImage[60] = 0x4e; //N orth
} else {
newImage[60] = 0x53; //S outh
lat *= -1;
}
if (lon > 0) {
newImage[84] = 0x45; //E ast
} else {
newImage[84] = 0x57; //W est
lon *= -1;
}
/**
* The exif encoding for positions are three "Rationals"
* one for degree, one for minutes and one for seconds.
* Each rational is 8bytes long. The first 4 are the numerator
* the last are the denominator
*
* We only use Degrees and Minutes and set Seconds to 0.
* Instead the Minutes are devided by 1000000, to get 6 decimal
* places.
*/
HelperRoutines.copyInt2ByteArray(newImage, 140, (int)Math.abs(lat));
float lat_min = (float)(lat - Math.floor(lat)) * 60000000.0f;
HelperRoutines.copyInt2ByteArray(newImage, 148, (int)lat_min);
HelperRoutines.copyInt2ByteArray(newImage, 164, (int)Math.abs(lon));
float lon_min = (float)(lon - Math.floor(lon)) * 60000000.0f;
HelperRoutines.copyInt2ByteArray(newImage, 172, (int)lon_min);
return newImage;
}
public void setup(GuiDiscover parent) {
ShareNav.getInstance().show(this);
this.parent = null;
setupParent = parent;
commandAction(SETUP_CMD, (Displayable) this);
}
public void show() {
ShareNav.getInstance().show(this);
//#if polish.api.mmapi
if ((mPlayer != null) && (video != null)) {
try {
mPlayer.start();
video.setVisible(true);
} catch (MediaException e) {
logger.exception(Locale.get("guicamera.CouldNotShowCameraViewer")/*Could not show camera viewer*/, e);
}
}
//#endif
//Display.getDisplay(parent.getParent()).setCurrent(this);
}
public void selectionCanceled() {
/**
* Nothing to do at the moment
*/
}
public void selectedFile(String url) {
logger.info("Setting picture directory to " + url);
Configuration.setPhotoUrl(url);
basedirectory = url;
}
}