ラベル android の投稿を表示しています。 すべての投稿を表示
ラベル android の投稿を表示しています。 すべての投稿を表示

2010年8月31日火曜日

AndroidとRTP

AndroidでRTP再生をするにはどうしたらいいのか調査中。
VideoViewはRTSPに対応しているけど、そうじゃなくて、直接RTPをさわりたいのです。
なんか支離滅裂なことを言っているかも。
調査継続です…。

2010年7月30日金曜日

AndroidでOAuth on twitter

twitter4jを利用してOAuth&twitterのタイムライン取得をするAndroidアプリを作ってみました。
テストアプリなのでアレですが、結構はまったので公開しておきます。

MainActivity.java

package com.example.atwitter;

import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;

import twitter4j.Paging;
import twitter4j.ResponseList;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.User;
import twitter4j.conf.ConfigurationBuilder;
import twitter4j.http.AccessToken;
import twitter4j.http.RequestToken;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
    // キー
    private static String CONSUMERKEY ="XXXXXX";
    private static String CONSUMERSECRET ="XXXXXX";
    private static String ACCESSTOKEN ="XXXXXX";
    private static String ACCESSSECRET ="XXXXXX";

    // コールバックURL
    private final String CALLBACK_URL="testapp://atwitter/";
    
    private static String SNL = System.getProperty("line.separator");
    private static String DNL = SNL + SNL;

    // Twitter
    private Twitter twitter;
    private AccessToken accessToken;

    // TextView
    private TextView textView;

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

        // Twitterの初期化
        initializeTwitter(null);

        // TextViewを保持
        textView = (TextView)findViewById(R.id.TextView01);

        // ボタンにコールバックを登録
        Button button = (Button)findViewById(R.id.Auth);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // twitterアクション実行
                String result = doOAuth(twitter);
                textView.setText(result);
            }
        });
        button = (Button)findViewById(R.id.Button01);
        button.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                // twitterアクション実行
                String result = userTimeline(twitter);
                textView.setText(result);
            }
        });
    }

    private void initializeTwitter(AccessToken accessToken) {

        ConfigurationBuilder confBuilder = new ConfigurationBuilder();
        confBuilder.setOAuthConsumerKey(CONSUMERKEY);
        confBuilder.setOAuthConsumerSecret(CONSUMERSECRET);

        TwitterFactory twitterFactory = new TwitterFactory(confBuilder.build());
        twitter = twitterFactory.getOAuthAuthorizedInstance(accessToken);
    }

    public String userTimeline(Twitter twitter){
        ResponseList statuses = null; //
        String result = "print userTimeline:" + DNL;
        int pageNumber =1;
        int count = 20;

        //Paging
        Paging paging = new Paging(pageNumber, count);

        try {
            statuses = twitter.getUserTimeline(paging);

            if (statuses != null) {
                for (Status status :statuses) {
                    result += status.getUser().getScreenName() +":"
                    + SNL + status.getText() + DNL;
                }
            }

        } catch (Exception e) {
            result += e.getMessage();
            e.printStackTrace();
        }
        return result;
    }

    private String doOAuth(Twitter twitter_) {
        String result = "doOAuth:" + DNL;
        Twitter twitter = new TwitterFactory().getInstance();
        twitter.setOAuthConsumer(CONSUMERKEY, CONSUMERSECRET);
        
        // RequestTokenを取得
        RequestToken requestToken = null;
        try {
            requestToken = twitter.getOAuthRequestToken(CALLBACK_URL);
        }
        catch (TwitterException e) {
            result += e.getMessage() + DNL;
            e.printStackTrace();
        }
        
        // RequestTokenを保存
        ObjectOutputStream oos;
        try {
            OutputStream out = openFileOutput("RequestToken", MODE_PRIVATE);
            oos = new ObjectOutputStream(out);
            oos.writeObject(requestToken);
        } catch (Exception e) {
            result += e.getMessage() + DNL;
            e.printStackTrace();
        }

        // 認証ページへ
        String strUrl = requestToken.getAuthorizationURL();
        Uri uri = Uri.parse(strUrl);
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        startActivity(intent);

        result += strUrl;
        
        return result;
    }

    // 起動時に呼ばれる(認証完了時も呼ばれる)
    @Override
    public void onResume() {
        super.onResume();

        String result = "";
        // extract the OAUTH access token if it exists
        Uri uri = this.getIntent().getData();
        if (uri != null && uri.toString().startsWith(CALLBACK_URL)) {
            RequestToken requestToken = null;
            try {
                InputStream in = openFileInput("RequestToken");
                ObjectInputStream ois = new ObjectInputStream(in);
                requestToken = (RequestToken)ois.readObject();
            } catch (Exception e) {
                result += e.getMessage() + DNL;
                e.printStackTrace();
            }
            String access_token = uri.getQueryParameter("oauth_verifier");
            try {
                accessToken = twitter.getOAuthAccessToken(requestToken, access_token);
                initializeTwitter(accessToken);
                // ここでAccessTokenを保存しておけば次回から認証不要となる
                textView.setText("認証完了");
            } catch (TwitterException e) {
                result += e.getMessage() + DNL;
                e.printStackTrace();
            }
        }
    }
}
ポイントは、
  • コールバックURLを使って、ブラウザでの認証後にAndroidアプリを起動させる(124行目)
  • 認証後、AccessTokenを生成するため、RequestTokenを保存しておく(131行目〜)
あたりでしょうか。

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.example.atwitter"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <data android:scheme="testapp" android:host="atwitter" android:path="/"/>
            </intent-filter>
        </activity>

    </application>
    <uses-sdk android:minSdkVersion="4" />

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

</manifest>
24行目でuses-permissionを使ってINTERNETを許可しています。あと、17行目でコールバックURLをフックしています。

main.xml(layout)

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout android:id="@+id/FrameLayout01" android:layout_width="fill_parent" android:layout_height="fill_parent" xmlns:android="http://schemas.android.com/apk/res/android">
    <TextView android:id="@+id/TextView01" android:layout_height="fill_parent" android:layout_width="fill_parent" android:text="Hello, twitter!"></TextView>
    <LinearLayout android:id="@+id/LinearLayout01" android:layout_height="wrap_content" android:layout_gravity="bottom" android:layout_width="wrap_content">
        <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="認証" android:id="@+id/Auth"></Button>
        <Button android:id="@+id/Button01" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="タイムラインとか"></Button>
    </LinearLayout>
</FrameLayout>
特別なことは何も。

実行例です。認証画面に遷移したところ。

2010年7月27日火曜日

twitterでOAuth。ちょっと色々はまってます。

android & twitter4jを利用しているのですが、色々はまっています。
だんだんと光が見えてきたので、参考になったページをメモ。

2010年6月24日木曜日

LogCatに何も表示されなくなった時の対応

たまーにLogCatに何も表示されなくなります。そのときの対応方法です。

  1. eclipseを終了
  2. ターミナルで以下のコマンドを実行する
    % adb kill-server
    % adb start-server
  3. eclipseを起動する

以上!

2010年6月17日木曜日

カメラのプレビューでFaceDetector

カメラのプレビュー画像で顔認識をやってみました。はまった点は

  • FaceDetector#findFaces()に渡すBitmapはARGB_8888じゃだめ

ってとこです。
RGB_565を使いました。

もっと言うと、モノクロ画像でも顔認識ができるっぽいので、プレビュー画像(YUV420)をRGB変換するのではなく、Y値だけ取り出した画像をfindFaces()に食わせました。

private Bitmap yuv2monochrome(byte data[], int width, int height, int[] buffer) {
    int y;

    for (int i = 0; i < width * height; i++) {
        y = data[i];
        buffer[i] = 0xff000000 | y << 16 | y << 8 | y;
    }

    return Bitmap.createBitmap(buffer, width, height, Config.RGB_565);
}

まぁ、FaceDetectorの処理にとても時間がかかるので、リアルタイムで顔認識はできないんですけど。
んー、微妙...

2010年6月15日火曜日

フルスクリーン

Androidアプリケーションの画面をフルスクリーンにする方法です。

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    // タイトルバーを消す
    requestWindowFeature(Window.FEATURE_NO_TITLE);

    setContentView(R.layout.main);

    // ステータスバーを消す
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}

2010年6月11日金曜日

カメラのプレビューにOpenGLを重ねてみる

カメラのプレビューにOpenGLのモデルを重ねてみました。OpenGLのモデルは、ApiDemosのcom.example.android.apis.graphics.TranslucentGLSurfaceViewActivityで使われているCubeRendererを利用しました。
というか、むしろTranslucentGLSurfaceViewActivityにカメラのプレビューを突っ込んだ感じ。

まずは、layoutのXML。
<RelativeLayout
    android:id="@+id/RelativeLayout01"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    xmlns:android="http://schemas.android.com/apk/res/android"
    >
    <android.opengl.GLSurfaceView
        android:id="@+id/GlView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:keepScreenOn="true">
    </android.opengl.GLSurfaceView>
    <com.example.CameraView
        android:id="@+id/CameraPreview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent">
    </com.example.CameraView>
</RelativeLayout>
SurfaceViewを継承したCameraViewを追加しています。

次に、Activity。
package com.example;

import android.app.Activity;
import android.graphics.PixelFormat;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

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

        // 画面を構築
        setContentView(R.layout.main);

        // GL
        GLSurfaceView glSurfaceView = (GLSurfaceView)findViewById(R.id.GlView);
        glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0); 
        glSurfaceView.setRenderer(new CubeRenderer(true));
        glSurfaceView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
    }
}
ポイントはGLSerfaceView#setEGLConfigChooser()とSurfaceHolder#setFormat()です。
CubeRendererクラスは、ApiDemosそのままです。

最後に、CameraView.java
package com.example;

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

public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
    private SurfaceHolder _holder;
    private Camera _camera;

    public CameraView(Context context) {
        super(context);

        // サーフェイスホルダーの生成
        _holder = getHolder();
        _holder.addCallback(this);
        
        // プッシュバッッファの指定
        _holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public CameraView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // サーフェイスホルダーの生成
        _holder = getHolder();
        _holder.addCallback(this);
        
        // プッシュバッッファの指定
        _holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
        // カメラのプレビュー開始
        Camera.Parameters parameters = _camera.getParameters();
        parameters.setPreviewSize(width, height);
        _camera.setParameters(parameters);
        _camera.startPreview();
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        // カメラの初期化
        try {
            _camera = Camera.open();
            _camera.setPreviewDisplay(holder);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        // カメラのプレビュー停止
        _camera.setPreviewCallback(null);
        _camera.stopPreview();
        _camera.release();
        _camera = null;
    }
}

実現はできたのですが、いまいち良く分かっていません。これ以上進むには、OpenGLの勉強が必要ですね...

2010年6月9日水曜日

カメラのプレビュー画像をいじってみる(2)

数日前にやっていたカメラのプレビュー画像操作。実機で色々試したあげく、以下のコードに落ち着きました。他にやりようあるかしら。

private class PreviewDraw implements Runnable {
    private byte[] _data;

    @Override
    public void run() {
        bitmap = BitmapUtil.yuv2rgb(_data, _previewSize.width, _previewSize.height, _rgbBuffer);

        // 描画
        if (bitmap != null) {
            Canvas canv = holder.lockCanvas();
            if (canv != null) {
                canv.drawBitmap(bitmap, 0, 0, null);
                holder.unlockCanvasAndPost(canv);
            }
        }
    }

    public void setData(byte[] data) {
        _data = data;
    }
}

private final Camera.PreviewCallback previewCallback =
    new Camera.PreviewCallback() {
        public void onPreviewFrame(byte[] data, Camera camera) {
            _previewDraw = new PreviewDraw();
            _previewDraw.setData(data);
            Thread thread = new Thread(_previewDraw);
            thread.start();
        }
    };

YUV420からRGBの変換で1.2秒くらい時間がかかってます。はっきり言って現実的ではないです。
モノクロ画像の表示(Y成分だけ抜き出す)でも0.3秒くらいかかります。うーん。

2010年6月6日日曜日

xperia googleアカウントの削除

会社のxperiaに個人のgoogleアカウントを設定してしまいました。
メニューなどから簡単に変更ができる訳ではないみたい。
以下、googleアカウントの削除方法です。

  1. 「設定」→「アプリケーション」→「アプリケーションの管理」
  2. 「google apps」を選択
  3. 「データの削除」を選択

これでgoogleアカウントの情報が削除されるみたいです。
必要ならば、「Gmail」なども同様に「データの削除」をしとくと良いかも。

ApiDemos(1.6)のビルド

Android 1.6のApiDemosがエラーになってビルドできませんでした。
原因は「IRemoteServiceを解決できない」とか「Rを解決できない」とかとか。
確かにgenの下にR.javaができていません。
R.javaは勝手に作成されるのでは?と思いつつ調べても解決できず。

ふとコンソールをみると、string.xmlにエラーがあるとのこと。

ApiDemos/res/values/strings.xml:365: エラー: Apostrophe not preceded by \ (in I'm on! :))
ApiDemos/res/values/strings.xml:366: エラー: Apostrophe not preceded by \ (in I'm off! :()
ApiDemos/res/values/strings.xml:643: エラー: Apostrophe not preceded by \ (in The Android platform is a software stack for mobile devices including an

言われた通りに、'(アポストロフィ)の前に\(バックスラッシュ)を入れてみると。。。

ビルドできました!

2010年6月2日水曜日

カメラのプレビュー画像をいじってみる(1)

Androidカメラのプレビュー画像をいじってみたいと思っています。リアルタイムで白黒反転したりしたいなーと。

単純にカメラのプレビュー画像をアプリケーション上で表示したいなら、SurfaceViewを生成してCamera#setPreviewDisplay()でSurfaceHolderを渡してあげれば良いんだけど、それだとプレビュー画像をいじれない。

プレビュー画像を横取りするには、Camera.PreviewCallbackを利用します。
紆余曲折いろいろなサイトを参考にこんなん作ってみました。

layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<framelayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<com.example.CameraView
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"
    android:id="@+id/SurfaceView01" 
/>
</FrameLayout>

CameraTest.java
package com.example;

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

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

CameraView.java
package com.example;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.hardware.Camera;
import android.view.SurfaceView;
import android.view.SurfaceHolder;
import android.util.AttributeSet;

public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
 private SurfaceHolder holder;
 private Camera camera;
 private Bitmap bitmap;
 private int[] rgb;
 private int width, height;

 /**
  * プレビューコールバック
  *   prepareSavePreviewImageコールバックで登録され、プレビュー画像を取得する
  */
 private final Camera.PreviewCallback _previewCallback =
  new Camera.PreviewCallback() {
  public void onPreviewFrame(byte[] data, Camera camera) {
   decodeYUV420SP(rgb, data, width, height);
   bitmap.setPixels(rgb, 0, width, 0, 0, width, height);

   // 描画
   Canvas canv = holder.lockCanvas();
   canv.drawBitmap(bitmap, 0, 0, null);
   holder.unlockCanvasAndPost(canv);
  }
 };
 
 // コンストラクタ
 public CameraView(Context context) {
  super(context);
  this.initialize();
 }

 // コンストラクタ
 public CameraView(Context context, AttributeSet attrs) {
  super(context, attrs);
  this.initialize();
 }

 // 初期化処理
 private void initialize() {
  // サーフェイスホルダーの生成
  holder=getHolder();
  holder.addCallback(this);

  // サーフェイスホルダーのタイプを設定
  holder.setType(SurfaceHolder.SURFACE_TYPE_NORMAL);

  // 画像を読み込んでおく
 }

 // サーフェイス生成イベントの処理
 public void surfaceCreated(SurfaceHolder holder) {
  // カメラの初期化
  try {
   camera=Camera.open();
   camera.setPreviewCallback(_previewCallback);
  } catch (Exception e) {
  }
 }

 // サーフェイス変更イベントの処理
 public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
  // 描画データの準備
  width = w;
  height = h;
  bitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  rgb = new int[w * h];

  // カメラのプレビュー開始
  Camera.Parameters parameters = camera.getParameters();
  parameters.setPreviewSize(w, h);
  camera.setParameters(parameters);
  camera.startPreview();
 }

 //サーフェイス解放イベントの処理
 public void surfaceDestroyed(SurfaceHolder holder) {
  // カメラのプレビュー停止
  camera.stopPreview();
  camera.setPreviewCallback(null);
  camera.release();
  camera = null;
 }

 // YUV420 to BMP
 static public void decodeYUV420SP(int[] rgb, byte[] yuv420sp, int width, int height) {
  final int frameSize = width * height;

  for (int j = 0, yp = 0; j < height; j++) {
   int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
   for (int i = 0; i < width; i++, yp++) {
    int y = (0xff & ((int) yuv420sp[yp])) - 16;
    if (y < 0) y = 0;
    if ((i & 1) == 0) {
     v = (0xff & yuv420sp[uvp++]) - 128;
     u = (0xff & yuv420sp[uvp++]) - 128;
    }

    int y1192 = 1192 * y;
    int r = (y1192 + 1634 * v);
    int g = (y1192 - 833 * v - 400 * u);
    int b = (y1192 + 2066 * u);

    if (r < 0) r = 0; else if (r > 262143) r = 262143;
    if (g < 0) g = 0; else if (g > 262143) g = 262143;
    if (b < 0) b = 0; else if (b > 262143) b = 262143;

    rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
   }
  }
 }
}

少し動かすとOutOfMemoryになってちゃんと動かない。Camera.Parameters#setPreviewFrameRate(1)とかやってやると落ちずに動く。
実機でやるとまた違う結果が出るかしら。

2010年5月31日月曜日

Android SDKのインストール on Mac

Mac OS X (snow leopard)にAndroid SDKをインストールします。

Eclipseのインストール

1.Eclipseのダウンロードページ「Eclipse IDE for Java EE Developers」からmac版のEclipseをダウンロードします

2.ダウンロードしたアーカイブを/Applicationsに展開します

3.Pleiadesで日本語化をします

Android SDKのインストール

1.Android Developersからandroid-sdk_r06-mac_86.zipをダウンロードします

2.ダウンロードしたアーカイブを任意の場所(私の場合はホームディレクトリ$HOME)に展開します

3.toolsディレクトリにパスを通しておきます
export $PATH=$PATH:$HOME/android-sdk-mac_86/tools

4.androidコマンドを以下のオプションで起動し、SDKを更新します
android update sdk

ADT (Android Development Tool)

EclipseにADTを追加します。

1.Eclipseメニューの「ヘルプ」→「新規ソフトウェアのインストール」を選択します

2.追加ボタンを押下します

3.名前は任意、ロケーションはhttps://dl-ssl.google.com/android/eclipseとします
4.「Developer Tools」をチェックし、「次へ」を押下します

5.インストールを完了し、Eclipseを再起動します(途中、セキュリティ警告が出るが気にしません)

6.再起動後、設定を行います。Eclipseメニューの「Eclipse」→「環境設定」を選択します

7.Androidセクションの「SDKロケーション」を設定します

AVD (Android Virtual Device)

Androidエミュレータの実行に必要なVirtual Deviceを作成します。

1.Eclipseメニューの「ウィンドウ」→「Android SDK and AVD Manager」を選択します。Managerが起動します。
2.「新規」ボタンを押下します

3.以下の情報を入力し、「create AVD」ボタンを押下します。
名前Android_1_6
ターゲットGoogle APIs - API Level 4
SD Card32MiB
SkinDefault(HVGA)
ハードウェア(変更なし)
4.「開始」ボタンでエミュレータが起動することを確認します

以上!
これでAndroidの開発環境が整いました。
まずは、Hello, Worldからやってみようかしら。