カメラを起動して写真を撮影し、GPS情報を取得するS!アプリ

メール宛に質問が来て、その回答に作ったプログラムを公開します。表題通り、カメラを起動して写真を撮影し、GPS情報を取得しています。実機で動かしたところ、GPS情報の更新(DeviceControl#updateLocationInfo)に処理がブロックされないようなので、適宜その後2秒程度スリープするか、撮影前に更新処理をキックするのがいいと思います。

あと、応用が効くように写真データの読み込み、先頭256BのHexデータのダンプも行います。EXIFを操作する機能をつけてやればGoogle MAPとかとマッシュアップ(もう死語?)できそうです。だれかJ2MEのEXIFライブラリとか知りませんか?前に作ろうとしましたが作りきらなかったです。今調べてたらこことかここのAlbumMakerとか参考になるかもしれませんね。ソースも含まれているみたいですし。

とりあえず本日は以上で。

NetBeansのプロジェクトファイル

ソースファイル(CameraSample.java

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

import com.j_phone.io.CameraConnection;
import com.j_phone.system.DeviceControl;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import javax.microedition.io.Connector;
import javax.microedition.io.InputConnection;
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import org.netbeans.microedition.lcdui.WaitScreen;
import org.netbeans.microedition.util.SimpleCancellableTask;

/**
 * @author g026817
 */
public class CameraSampleMIDlet extends MIDlet implements CommandListener, ItemCommandListener {

    private boolean midletPaused = false;

    //<editor-fold defaultstate="collapsed" desc=" Generated Fields ">//GEN-BEGIN:|fields|0|
    private Form mainForm;
    private StringItem button;
    private StringItem filename;
    private StringItem filesize;
    private StringItem hexdata;
    private StringItem longitude;
    private StringItem latitude;
    private WaitScreen cameraWaitScreen;
    private Form errorForm;
    private StringItem errorMessage;
    private Command itemCommand;
    private SimpleCancellableTask cameraTask;
    //</editor-fold>//GEN-END:|fields|0|

    /**
     * The CameraSampleMIDlet constructor.
     */
    public CameraSampleMIDlet() {
    }

    //<editor-fold defaultstate="collapsed" desc=" Generated Methods ">//GEN-BEGIN:|methods|0|
    //</editor-fold>//GEN-END:|methods|0|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: initialize ">//GEN-BEGIN:|0-initialize|0|0-preInitialize
    /**
     * Initilizes the application.
     * It is called only once when the MIDlet is started. The method is called before the <code>startMIDlet</code> method.
     */
    private void initialize() {//GEN-END:|0-initialize|0|0-preInitialize
        // write pre-initialize user code here
//GEN-LINE:|0-initialize|1|0-postInitialize
        // write post-initialize user code here
    }//GEN-BEGIN:|0-initialize|2|
    //</editor-fold>//GEN-END:|0-initialize|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: startMIDlet ">//GEN-BEGIN:|3-startMIDlet|0|3-preAction
    /**
     * Performs an action assigned to the Mobile Device - MIDlet Started point.
     */
    public void startMIDlet() {//GEN-END:|3-startMIDlet|0|3-preAction
        // write pre-action user code here
        switchDisplayable(null, getMainForm());//GEN-LINE:|3-startMIDlet|1|3-postAction
        // write post-action user code here
    }//GEN-BEGIN:|3-startMIDlet|2|
    //</editor-fold>//GEN-END:|3-startMIDlet|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: resumeMIDlet ">//GEN-BEGIN:|4-resumeMIDlet|0|4-preAction
    /**
     * Performs an action assigned to the Mobile Device - MIDlet Resumed point.
     */
    public void resumeMIDlet() {//GEN-END:|4-resumeMIDlet|0|4-preAction
        // write pre-action user code here
//GEN-LINE:|4-resumeMIDlet|1|4-postAction
        // write post-action user code here
    }//GEN-BEGIN:|4-resumeMIDlet|2|
    //</editor-fold>//GEN-END:|4-resumeMIDlet|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: switchDisplayable ">//GEN-BEGIN:|5-switchDisplayable|0|5-preSwitch
    /**
     * Switches a current displayable in a display. The <code>display</code> instance is taken from <code>getDisplay</code> method. This method is used by all actions in the design for switching displayable.
     * @param alert the Alert which is temporarily set to the display; if <code>null</code>, then <code>nextDisplayable</code> is set immediately
     * @param nextDisplayable the Displayable to be set
     */
    public void switchDisplayable(Alert alert, Displayable nextDisplayable) {//GEN-END:|5-switchDisplayable|0|5-preSwitch
        // write pre-switch user code here
        Display display = getDisplay();//GEN-BEGIN:|5-switchDisplayable|1|5-postSwitch
        if (alert == null) {
            display.setCurrent(nextDisplayable);
        } else {
            display.setCurrent(alert, nextDisplayable);
        }//GEN-END:|5-switchDisplayable|1|5-postSwitch
        // write post-switch user code here
    }//GEN-BEGIN:|5-switchDisplayable|2|
    //</editor-fold>//GEN-END:|5-switchDisplayable|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: commandAction for Displayables ">//GEN-BEGIN:|7-commandAction|0|7-preCommandAction
    /**
     * Called by a system to indicated that a command has been invoked on a particular displayable.
     * @param command the Command that was invoked
     * @param displayable the Displayable where the command was invoked
     */
    public void commandAction(Command command, Displayable displayable) {//GEN-END:|7-commandAction|0|7-preCommandAction
        // write pre-action user code here
        if (displayable == cameraWaitScreen) {//GEN-BEGIN:|7-commandAction|1|19-preAction
            if (command == WaitScreen.FAILURE_COMMAND) {//GEN-END:|7-commandAction|1|19-preAction
                // write pre-action user code here
                switchDisplayable(null, getErrorForm());//GEN-LINE:|7-commandAction|2|19-postAction
                // write post-action user code here
            } else if (command == WaitScreen.SUCCESS_COMMAND) {//GEN-LINE:|7-commandAction|3|18-preAction
                // write pre-action user code here
                switchDisplayable(null, getMainForm());//GEN-LINE:|7-commandAction|4|18-postAction
                // write post-action user code here
            }//GEN-BEGIN:|7-commandAction|5|7-postCommandAction
        }//GEN-END:|7-commandAction|5|7-postCommandAction
        // write post-action user code here
    }//GEN-BEGIN:|7-commandAction|6|
    //</editor-fold>//GEN-END:|7-commandAction|6|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: mainForm ">//GEN-BEGIN:|14-getter|0|14-preInit
    /**
     * Returns an initiliazed instance of mainForm component.
     * @return the initialized component instance
     */
    public Form getMainForm() {
        if (mainForm == null) {//GEN-END:|14-getter|0|14-preInit
            // write pre-init user code here
            mainForm = new Form("title", new Item[] { getButton(), getFilename(), getFilesize(), getLongitude(), getLatitude(), getHexdata() });//GEN-LINE:|14-getter|1|14-postInit
            // write post-init user code here
        }//GEN-BEGIN:|14-getter|2|
        return mainForm;
    }
    //</editor-fold>//GEN-END:|14-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: cameraWaitScreen ">//GEN-BEGIN:|15-getter|0|15-preInit
    /**
     * Returns an initiliazed instance of cameraWaitScreen component.
     * @return the initialized component instance
     */
    public WaitScreen getCameraWaitScreen() {
        if (cameraWaitScreen == null) {//GEN-END:|15-getter|0|15-preInit
            // write pre-init user code here
            cameraWaitScreen = new WaitScreen(getDisplay());//GEN-BEGIN:|15-getter|1|15-postInit
            cameraWaitScreen.setTitle("title");
            cameraWaitScreen.setCommandListener(this);
            cameraWaitScreen.setText("\u3044\u308D\u3044\u308D\u51E6\u7406\u4E2D");
            cameraWaitScreen.setTask(getCameraTask());//GEN-END:|15-getter|1|15-postInit
            // write post-init user code here
        }//GEN-BEGIN:|15-getter|2|
        return cameraWaitScreen;
    }
    //</editor-fold>//GEN-END:|15-getter|2|
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: cameraTask ">//GEN-BEGIN:|20-getter|0|20-preInit
    /**
     * Returns an initiliazed instance of cameraTask component.
     * @return the initialized component instance
     */
    public SimpleCancellableTask getCameraTask() {
        if (cameraTask == null) {//GEN-END:|20-getter|0|20-preInit
            // write pre-init user code here
            cameraTask = new SimpleCancellableTask();//GEN-BEGIN:|20-getter|1|20-execute
            cameraTask.setExecutable(new org.netbeans.microedition.util.Executable() {
                public void execute() throws Exception {//GEN-END:|20-getter|1|20-execute
                    // write task-execution user code here
                    CameraConnection cc = (CameraConnection)Connector.open("camera:");
                    cc.capture();
                    getFilename().setText(cc.getFileName());
                    cc.close();
                    InputConnection ic = (InputConnection)Connector.open("file://" + getFilename().getText());
                    InputStream is = ic.openInputStream();
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    byte[] buff=new byte[1024];
                    int size;
                    while ((size=is.read(buff))!=-1) {
                            baos.write(buff,0,size);
                    }
                    baos.flush();
                    baos.close();
                    is.close();
                    ic.close();
                    byte[] data = baos.toByteArray();
                    getFilesize().setText(data.length+" Bytes");

                    StringBuffer sb = new StringBuffer();
                    char[] hex = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F',};
                    for (int i=0;i<256;i++){
                        int n = 0xFF & data[i];
                        //System.out.println(n/16+" "+n%16);
                        sb.append(hex[n/16]);
                        sb.append(hex[n%16]);
                        sb.append(" ");
                        if(i%8==7){sb.append("\n");}
                    }
                    getHexdata().setText(sb.toString());

                    DeviceControl dc = DeviceControl.getDefaultDeviceControl();
                    try{
                        dc.updateLocationInfo();
                        getLongitude().setText(String.valueOf(dc.getLongitude()));
                        getLatitude().setText(String.valueOf(dc.getLatitude()));
                    }catch(Throwable t){
                        getLongitude().setText(t.getClass().getName());
                        getLatitude().setText(t.getMessage());
                    }

                }//GEN-BEGIN:|20-getter|2|20-postInit
            });//GEN-END:|20-getter|2|20-postInit
            // write post-init user code here
        }//GEN-BEGIN:|20-getter|3|
        return cameraTask;
    }
    //</editor-fold>//GEN-END:|20-getter|3|
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: button ">//GEN-BEGIN:|22-getter|0|22-preInit
    /**
     * Returns an initiliazed instance of button component.
     * @return the initialized component instance
     */
    public StringItem getButton() {
        if (button == null) {//GEN-END:|22-getter|0|22-preInit
            // write pre-init user code here
            button = new StringItem(null, "\u30AB\u30E1\u30E9\u306E\u8D77\u52D5", Item.BUTTON);//GEN-BEGIN:|22-getter|1|22-postInit
            button.addCommand(getItemCommand());
            button.setItemCommandListener(this);
            button.setDefaultCommand(getItemCommand());
            button.setLayout(ImageItem.LAYOUT_DEFAULT | ImageItem.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_2);//GEN-END:|22-getter|1|22-postInit
            // write post-init user code here
        }//GEN-BEGIN:|22-getter|2|
        return button;
    }
    //</editor-fold>//GEN-END:|22-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Method: commandAction for Items ">//GEN-BEGIN:|8-itemCommandAction|0|8-preItemCommandAction
    /**
     * Called by a system to indicated that a command has been invoked on a particular item.
     * @param command the Command that was invoked
     * @param displayable the Item where the command was invoked
     */
    public void commandAction(Command command, Item item) {//GEN-END:|8-itemCommandAction|0|8-preItemCommandAction
        // write pre-action user code here
        if (item == button) {//GEN-BEGIN:|8-itemCommandAction|1|24-preAction
            if (command == itemCommand) {//GEN-END:|8-itemCommandAction|1|24-preAction
                // write pre-action user code here
                switchDisplayable(null, getCameraWaitScreen());//GEN-LINE:|8-itemCommandAction|2|24-postAction
                // write post-action user code here
            }//GEN-BEGIN:|8-itemCommandAction|3|8-postItemCommandAction
        }//GEN-END:|8-itemCommandAction|3|8-postItemCommandAction
        // write post-action user code here
    }//GEN-BEGIN:|8-itemCommandAction|4|
    //</editor-fold>//GEN-END:|8-itemCommandAction|4|
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: filename ">//GEN-BEGIN:|25-getter|0|25-preInit
    /**
     * Returns an initiliazed instance of filename component.
     * @return the initialized component instance
     */
    public StringItem getFilename() {
        if (filename == null) {//GEN-END:|25-getter|0|25-preInit
            // write pre-init user code here
            filename = new StringItem("\u30D5\u30A1\u30A4\u30EB\u540D", "\n");//GEN-BEGIN:|25-getter|1|25-postInit
            filename.setLayout(ImageItem.LAYOUT_DEFAULT | ImageItem.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_2);//GEN-END:|25-getter|1|25-postInit
            // write post-init user code here
        }//GEN-BEGIN:|25-getter|2|
        return filename;
    }
    //</editor-fold>//GEN-END:|25-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: itemCommand ">//GEN-BEGIN:|23-getter|0|23-preInit
    /**
     * Returns an initiliazed instance of itemCommand component.
     * @return the initialized component instance
     */
    public Command getItemCommand() {
        if (itemCommand == null) {//GEN-END:|23-getter|0|23-preInit
            // write pre-init user code here
            itemCommand = new Command("", Command.ITEM, 0);//GEN-LINE:|23-getter|1|23-postInit
            // write post-init user code here
        }//GEN-BEGIN:|23-getter|2|
        return itemCommand;
    }
    //</editor-fold>//GEN-END:|23-getter|2|
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: filesize ">//GEN-BEGIN:|26-getter|0|26-preInit
    /**
     * Returns an initiliazed instance of filesize component.
     * @return the initialized component instance
     */
    public StringItem getFilesize() {
        if (filesize == null) {//GEN-END:|26-getter|0|26-preInit
            // write pre-init user code here
            filesize = new StringItem("\u30D5\u30A1\u30A4\u30EB\u30B5\u30A4\u30BA", "\n");//GEN-BEGIN:|26-getter|1|26-postInit
            filesize.setLayout(ImageItem.LAYOUT_DEFAULT | ImageItem.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_2);//GEN-END:|26-getter|1|26-postInit
            // write post-init user code here
        }//GEN-BEGIN:|26-getter|2|
        return filesize;
    }
    //</editor-fold>//GEN-END:|26-getter|2|
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: hexdata ">//GEN-BEGIN:|27-getter|0|27-preInit
    /**
     * Returns an initiliazed instance of hexdata component.
     * @return the initialized component instance
     */
    public StringItem getHexdata() {
        if (hexdata == null) {//GEN-END:|27-getter|0|27-preInit
            // write pre-init user code here
            hexdata = new StringItem("Hex\u30C7\u30FC\u30BF\uFF08\u5148\u982D256B\uFF09", "\n");//GEN-LINE:|27-getter|1|27-postInit
            // write post-init user code here
        }//GEN-BEGIN:|27-getter|2|
        return hexdata;
    }
    //</editor-fold>//GEN-END:|27-getter|2|

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: errorForm ">//GEN-BEGIN:|30-getter|0|30-preInit
    /**
     * Returns an initiliazed instance of errorForm component.
     * @return the initialized component instance
     */
    public Form getErrorForm() {
        if (errorForm == null) {//GEN-END:|30-getter|0|30-preInit
            // write pre-init user code here
            errorForm = new Form("title", new Item[] { getErrorMessage() });//GEN-LINE:|30-getter|1|30-postInit
            // write post-init user code here
        }//GEN-BEGIN:|30-getter|2|
        return errorForm;
    }
    //</editor-fold>//GEN-END:|30-getter|2|
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: errorMessage ">//GEN-BEGIN:|32-getter|0|32-preInit
    /**
     * Returns an initiliazed instance of errorMessage component.
     * @return the initialized component instance
     */
    public StringItem getErrorMessage() {
        if (errorMessage == null) {//GEN-END:|32-getter|0|32-preInit
            // write pre-init user code here
            errorMessage = new StringItem("\u30A8\u30E9\u30FC\u30E1\u30C3\u30BB\u30FC\u30B8", getCameraTask().getFailureMessage());//GEN-LINE:|32-getter|1|32-postInit
            // write post-init user code here
        }//GEN-BEGIN:|32-getter|2|
        return errorMessage;
    }
    //</editor-fold>//GEN-END:|32-getter|2|
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: longitude ">//GEN-BEGIN:|33-getter|0|33-preInit
    /**
     * Returns an initiliazed instance of longitude component.
     * @return the initialized component instance
     */
    public StringItem getLongitude() {
        if (longitude == null) {//GEN-END:|33-getter|0|33-preInit
            // write pre-init user code here
            longitude = new StringItem("\u7D4C\u5EA6", "\n");//GEN-BEGIN:|33-getter|1|33-postInit
            longitude.setLayout(ImageItem.LAYOUT_DEFAULT | ImageItem.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_2);//GEN-END:|33-getter|1|33-postInit
            // write post-init user code here
        }//GEN-BEGIN:|33-getter|2|
        return longitude;
    }
    //</editor-fold>//GEN-END:|33-getter|2|
    //</editor-fold>

    //<editor-fold defaultstate="collapsed" desc=" Generated Getter: latitude ">//GEN-BEGIN:|34-getter|0|34-preInit
    /**
     * Returns an initiliazed instance of latitude component.
     * @return the initialized component instance
     */
    public StringItem getLatitude() {
        if (latitude == null) {//GEN-END:|34-getter|0|34-preInit
            // write pre-init user code here
            latitude = new StringItem("\u7DEF\u5EA6", "\n");//GEN-BEGIN:|34-getter|1|34-postInit
            latitude.setLayout(ImageItem.LAYOUT_DEFAULT | ImageItem.LAYOUT_NEWLINE_AFTER | Item.LAYOUT_2);//GEN-END:|34-getter|1|34-postInit
            // write post-init user code here
        }//GEN-BEGIN:|34-getter|2|
        return latitude;
    }
    //</editor-fold>//GEN-END:|34-getter|2|

    /**
     * Returns a display instance.
     * @return the display instance.
     */
    public Display getDisplay () {
        return Display.getDisplay(this);
    }

    /**
     * Exits MIDlet.
     */
    public void exitMIDlet() {
        switchDisplayable (null, null);
        destroyApp(true);
        notifyDestroyed();
    }

    /**
     * Called when MIDlet is started.
     * Checks whether the MIDlet have been already started and initialize/starts or resumes the MIDlet.
     */
    public void startApp() {
        if (midletPaused) {
            resumeMIDlet ();
        } else {
            initialize ();
            startMIDlet ();
        }
        midletPaused = false;
    }

    /**
     * Called when MIDlet is paused.
     */
    public void pauseApp() {
        midletPaused = true;
    }

    /**
     * Called to signal the MIDlet to terminate.
     * @param unconditional if true, then the MIDlet has to be unconditionally terminated and all resources has to be released.
     */
    public void destroyApp(boolean unconditional) {
    }

}

JADファイル(CameraSample.jad

MIDlet-1: CameraSampleMIDlet, , CameraSampleMIDlet
MIDlet-Jar-Size: 43041
MIDlet-Jar-URL: CameraSample.jar
MIDlet-Name: CameraSample
MIDlet-Permissions: com.j_phone.io.Connector.CameraConnection.capture, com.j_phone.io.Connector.StorageConnection.read, com.j_phone.system.DeviceControl.Location, com.j_phone.system.DeviceControl.updateLocation
MIDlet-Vendor: Vendor
MIDlet-Version: 1.0
MIDxlet-API: MEXA
MicroEdition-Configuration: CLDC-1.1
MicroEdition-Profile: MIDP-2.0