@blog.justoneplanet.info

日々勉強

androidでカメラを使う

凄く基本的なコード。別にすごいことはしてない。備忘録程度。

■カメラを起動する

AndroidManifest.xml

以下の記述をManifestファイルに記述してカメラを使えるようにする。

<uses-permission android:name="android.permission.CAMERA" />

以下の記述をactivityの属性に追記し、カメラの向きを端末の向きに合わせる。

<activity android:name=".MainActivity" android:label="@string/app_name" android:screenOrientation="landscape">

MainActivity.java

まぁ、いつも通りベタに書いておく。ちょっと違うのはCameraPreviewクラスをインスタンス化して使用する部分。

package jp.info.justoneplanet.android.usecamera;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        requestWindowFeature(Window.FEATURE_NO_TITLE);// titleの表示分で縦横比が崩れる
        mCameraPreview = new CameraPreview(this);
        setContentView(mCameraPreview);
    }
}

CameraPreview.java

SurfaceViewを継承してカメラを実際に扱う部分を作る。

package jp.info.justoneplanet.android.usecamera;

import java.io.IOException;

import android.content.Context;
import android.hardware.Camera;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback
{
    SurfaceHolder mHolder;
    Camera mCamera;
    public CameraPreview(Context context)
    {
        super(context);
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPreviewSize(width, height);
        mCamera.setParameters(parameters);
        mCamera.startPreview();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        if(mCamera == null){
            mCamera = Camera.open();
            try{
                mCamera.setPreviewDisplay(holder);
            }
            catch(IOException e){
                mCamera.release();
                mCamera = null;
            }
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }
}

■カメラの画像を保存してみる

上述のコードを改造して楽しむことにする。

AndroidManifest.xml

以下の記述をManifestファイルに記述してSDカードにアクセスできるようにする。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

MainActivity.java

onTouchEventが実装されたのと、カメラのインスタンスをMainActivityクラスでも使用する点が変更点である。

package jp.info.justoneplanet.android.usecamera;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.ShutterCallback;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity {
    private CameraPreview mCameraPreview;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        
        mCameraPreview = new CameraPreview(this);
        setContentView(mCameraPreview);
    }

    private static final File OUTPUT_FILE = new File("/sdcard/usecamera.jpg");
    @Override
    public boolean onTouchEvent(MotionEvent event){
        if(event.getAction() == MotionEvent.ACTION_DOWN){
            Camera mCamera = mCameraPreview.getCamera();
            mCamera.takePicture(
                    new ShutterCallback(){
                        @Override
                        public void onShutter()
                        {
                        }
                    },
                    new PictureCallback(){
                        @Override
                        public void onPictureTaken(byte[] data, Camera camera)
                        {
                        }
                    },
                    new PictureCallback(){
                        @Override
                        public void onPictureTaken(byte[] data, Camera camera)
                        {
                            FileOutputStream out = null;
                            try{
                                out = new FileOutputStream(OUTPUT_FILE);
                                out.write(data);
                                out.flush();
                                out.close();
                            }
                            catch(FileNotFoundException e){
                                e.printStackTrace();
                            }
                            catch(IOException e){
                                e.printStackTrace();
                            }
                        }
                    }
            );
        }
        return false;
    }
}

CameraPreview.java

MainActivityクラスでCameraのインスタンスを使用するために、getterメソッドを追記した。それ以外は何も変わっていない。

package jp.info.justoneplanet.android.usecamera;

import java.io.IOException;

import android.content.Context;
import android.hardware.Camera;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback
{
    SurfaceHolder mHolder;
    Camera mCamera;
    public CameraPreview(Context context)
    {
        super(context);
        
        mHolder = getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height)
    {
        Camera.Parameters parameters = mCamera.getParameters();
        parameters.setPreviewSize(width, height);
        mCamera.setParameters(parameters);
        mCamera.startPreview();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder)
    {
        if(mCamera == null){
            mCamera = Camera.open();
            
            try{
                mCamera.setPreviewDisplay(holder);
            }
            catch(IOException e){
                mCamera.release();
                mCamera = null;
            }
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder)
    {
        mCamera.stopPreview();
        mCamera.release();
        mCamera = null;
    }
    
    public Camera getCamera(){
        return mCamera;
    }
}

やっぱandroidを触るのって楽しい((o(^-^)o))ワクワク

androidのViewGroup

ViewGroupは子Viewを内包できる特別なViewである。

LinearLayout
水平方向・垂直方向に子Viewを配置
RelativeLayout
子View同士・親Viewとの位置関係で配置
FrameLayout
左上を原点としてViewを配置(重なる)
TableLayout
テーブルの行(TableRow)の中にViewを配置。
GridView
画像などを格子状に配置
AbsoluteLayout
非推奨。ピクセル指定の絶対値配置
GridView
画像などを格子状に配置
Gallery
画像を水平方向に配置
ListView
スクロール可能なリスト
ScrollView
スクロール可能なView
Spinner
selectboxみたいなやつ
SurfaceView
描画用View
TabHost
タブ
ViewFlipper
Slideshow的なやつ

どうしてプログラマに・・・プログラムが書けないのか?

どうしてプログラマに・・・プログラムが書けないのか?という事でJavaScriptで書いてみた。

for(var i = 1; i < 101; i++){
	if(i % 3 == 0 && i % 5 == 0){
		document.write('FizzBuzz(' + i + ')<br />\n');
	}
	else if(i % 3 == 0){
		document.write('Fizz(' + i + ')<br />\n');
	}
	else if(i % 5 == 0){
		document.write('Buzz(' + i + ')<br />\n');
	}
	else{
		document.write(i + "<br />\n");
	}
}

書けてよかった。。。

例外をキャッチ

try{
    window.addEventListener(
        'load',
        function(){
            alert('test');
        },
        false
    );
}
catch(e){
    alert(e.constructor);
    alert(e.message);
    alert(e.name);
    alert(e.fileName);//mozilla
    alert(e.lineNumber);//mozilla
    alert(e.stack);//mozilla
    alert(e.description);//ie
    alert(e.number);//ie
}

androidで端末の情報にアクセスする

■SIMのシリアル番号

以下のようにすることでSIMのシリアル番号にアクセスできる。

package info.justoneplanet.android.sample.id;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;

public class AndroidIdActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        Log.e("SIM", telephonyManager.getSimSerialNumber());
    }
}

パーミッション

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

■UUID

UUIDは不変で128bitの固有のIDである。

一元管理していたり,重複をチェックする仕組みがないのに,世界でただ一つのIDを自由にいくらでも作って自分の機器を管理・識別するために使える

package info.justoneplanet.android.sample.id;

import java.util.UUID;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

/**
 * AppUUID
 * @author justoneplanet
 */
public class AppUUID {
    private static String uuid = null;
    private static final String UUID_KEY = "UUID_KEY";
    
    public static String get(Context context) {
        if (uuid != null) {// 既にapp内からinvokeされている場合
            return uuid;
        }
        
        SharedPreferences sharedPreferences = context.getSharedPreferences(UUID_KEY, Context.MODE_PRIVATE);
        uuid = sharedPreferences.getString(UUID_KEY, null);
        if (uuid == null) {// 何も設定されていない場合
            uuid = UUID.randomUUID().toString();// randomな文字列を生成
            Editor editor = sharedPreferences.edit();
            editor.putString(UUID_KEY, uuid);
            editor.commit();// 保存
        }
        return uuid;
    }
}

パーミッションは必要ない。

Log.e("AppUUID", AppUUID.get(getApplicationContext()));

アプリ間の固有の値であり再インストールすれば値は変わるので潔い。

■ANDROID_ID

package info.justoneplanet.android.sample.id;

import android.app.Activity;
import android.os.Bundle;
import android.provider.Settings.Secure;
import android.util.Log;

public class AndroidIdActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        String androidId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
        Log.e("ANDROID_ID", androidId);
    }
}

■IMEI

package info.justoneplanet.android.sample.id;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;

public class AndroidIdActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
        Log.e("IMEI", telephonyManager.getDeviceId());
    }
}

パーミッション

<uses-permission android:name="android.permission.READ_PHONE_STATE" />

PHPでデザインパターン:Builder(ビルダー)

Item.class.php

<?php
class Item
{
    private $_title;
    private $_url;
    private $_date;
    public function __construct($title, $url, $date)
    {
        $this->_title = $title;
        $this->_url  = $url;
        $this->_date = $date;
    }
    public function getTitle()
    {
        return $this->_title;
    }
    public function getUrl()
    {
        return $this->_url;
    }
    public function getDate()
    {
        return $this->_date;
    }
}

NewsDirector.class.php

<?php
require_once 'class/NewsDirector.class.php';

class NewsDirector
{
    private $_builder;
    private $_url;
    public function __construct(NewsBuilder $builder, $url)
    {
        $this->_builder = $builder;
        $this->_url     = $url;
    }
    public function getNews()
    {
        $list = $this->_builder->parse($this->_url)
        return $list
    }
}

NewsBuilder.class.php

<?php
interface NewsBuilder
{
    public function parse($data);
}

FeedNewsBuilder.class.php

<?php
require_once 'Item.class.php';
require_once 'NewsBuilder.class.php';

class FeedNewsBuilder implements NewsBuilder
{
    public function parse($url)
    {
        $data = new SimpleXmlElement($url, null, true);
        if($data){
            foreach($data->item as $item){
                $dc = $item->children('http://purl.org/dc/elements/1.1/');
                $list[] = new News($item->title, $item->link, $dc->date);
            }
            return $list;
        }
    }
}

index.php

クライアントとやり取りするフロント部分だ。

<?php
require_once 'class/NewsDirector.class';
require_once 'class/FeedNewsBuilder.class.php';

$builder = new FeedNewsBuilder();
$url = 'http://blog.justoneplanet.info/feed/';
$director = new NewsDirector($builder, $url);
foreach($director->getNews() as $article){
    $data = array(
        'date'  => $article->getDate(),
        'url'   => $article->getUrl(),
        'title' => $article->getTitle()
    );
}

特定のチェックボックスをクリックすると指定のチェックボックスが全てチェックされるスクリプト

イベントリスナを使ってます。一応、IEとFFでは動作チェックしてます。

<script type="text/javascript">
//<[CDATA[
var switcherBoxId = 'switcher';
var targetBoxName = 'targets';
var addListen = (function(){
    if(window.addEventListener){
        return function(elm, type, func){
            elm.addEventListener(type, func, false);
        }
    }
    else if(window.attachEvent){
        return function(elm, type, func){
            elm.attachEvent('on' + type, func);
        }
    }
    else{
        return new Function;
    }
})();
addListen(
    window,
    'load',
    function(){
        addListen(
            document.getElementById(switcherBoxId),
            'click',
            function(e){
                if(e.target){
                    var target = e.target;
                }
                else{
                    var target = window.event.srcElement;
                }
                if(!target.checked){
                    var checkboxes = document.forms[0].elements[targetBoxId];
                    for(var i = 0, n = checkboxes.length; i < n; i++){
                        if(checkboxes[i].checked){
                            checkboxes[i].checked = false;
                        }
                    }
                }
                else{
                    var checkboxes = document.forms[0].elements[targetBoxId];
                    for(var i = 0, n = checkboxes.length; i < n; i++){
                        if(!checkboxes[i].checked){
                            checkboxes[i].checked = true;
                        }
                    }
                }
            }
        );
    }
);
//>>
</script>

ちなみにIEではメモリリークする気がする。

Shift-JISでPHPをコーディングするとパースエラーになる

以下のように記述するとパースエラーとなる。

<?php
define('CONSTANT', '機能');
?>

■原因

2バイト目に5C16を含む文字が使用されているため。

■詳細

「能」はダメ文字と呼ばれ2バイト目に5C16を含む。つまり「能」の後に続くシングルクォーテーションがエスケープされて、文字列を正しく括れない。

■対策

以下のように\を記述するとパースエラーを防ぐ事ができる。

<?php
define('CONSTANT', '機能\');
?>

個人的にはShift-JISを使わないことを勧める。

サーバ設定メモ

Pleskを使用。

■ドキュメントルートの設定

=Web公開ディレクトリ。

DocumentRoot /var/www/vhosts/example.org/subdomains/dev/task/web

■PHPがアクセス可能なディレクトリ

<Directory  /var/www/vhosts/example.org/subdomains/sub/httpdocs>
php_admin_value open_basedir "/var/www/vhosts/example.org/subdomains/sub:/tmp:/usr/local/ZendFramework/library"
</Directory>

ディレクトリ(=この場合公開ディレクトリ)にあるPHPスクリプトから、PHPがアクセス可能なディレクトリ。noneとすることでサーバ上の全てのファイルにアクセスが出来るようになるが、セキュリティリスクが下がるので、無意味にnoneとしてはならない。