2010年9月2日木曜日

クラスの調査

objective-cで、あるオブジェクトがどのクラスに属しているかを調べる方法です。


こんな感じのようです。
- (void)method:(id)object
{
    // objectがNSStringクラスかどうかを確認
    if ([object isKindOfClass:[NSString class]]) {
      // NSStringクラスの場合
      ...
    }

編集が終わったらキーボードを閉じる

iPhoneのソフトウェアキーボードを閉じる方法です。

UITextFieldにフォーカスが当たるとソフトウェアキーボードが出てきます。
キーボードの改行ボタンを押すと、UITextFieldにUIControlEventEditingDidEndOnExitイベントが飛んできますので、それを捕まえてUITextFieldからフォーカスを外してあげます。
フォーカスが外れるとキーボードが閉じ、めでたしめでたし。

コードはこんな感じです。resignFirstResponderメソッドを呼ぶことでUITextFieldからフォーカスを外してます。

- (void)viewDidLoad {
    UITextField *textField = [[[UITextField alloc]
                               initWithFrame:CGRectMake(10, 10, 200, 31)]
                              autorelease];
    textField.borderStyle = UITextBorderStyleRoundedRect;
    [textField addTarget:self
                  action:@selector(editingDidEnd:)
        forControlEvents:UIControlEventEditingDidEndOnExit];
    [self.view addSubview:textField];
}

- (void)editingDidEnd:(id)sender {
    UITextField *textField = (UITextField *)sender;
    [textField resignFirstResponder];
}

Interface Builderで同じ事を行う場合は、UITextFieldの「Did End on Exit」というイベントにアクションを設定してあげます。

2010年8月31日火曜日

AndroidとRTP

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

2010年8月26日木曜日

json-simple

JSON.simple。JSONを扱うためのシンプルなJavaツールキットです。
JSONテキストのエンコードとデコードができます。

上記ページに型の対応表もあります。

2010年8月23日月曜日

Silverlightのデバッグ

Visual Studio 2010でSilverlightのデバッグ実行をすると、google chromeが起動します。
google chromeだとデバッグができないみたいで、ブレークポイントに全く反応しません。
まぁ、それは当然だなと思うのですが、じゃぁどうやればIEで実行できるようになるのか。

  1. ソリューションエクスプローラーからプロジェクト(ツリーのトップ)を選択する
  2. メニューから「ファイル」→「ブラウザーの選択」を選択する
  3. Internet Explorerを選択する

ソリューションエクスプローラーでプロジェクトを選択しないと、ファイルメニューに「ブラウザーの選択」が出現しません。
なにその裏技。

2010年8月17日火曜日

iPhoneでtwitterのOAuth

前にAndroidでtwitterのOAuth認証をやりましたが、今度はiPhoneでやってみます。

ライブラリとしては、MGTwitterEngineoauthconsumerを利用すると良いみたいです。
で、これらをwrapしているiPhone向けのライブラリTwitter-OAuth-iPhoneを使うのが一番簡単そうです。

まずGitを使ってソースコードを取得します。

git clone http://github.com/bengottlieb/Twitter-OAuth-iPhone.git

Demoディレクトリの下にOAuthTwitterDemo.xcodeprojがあるので起動します。
ベースSDKが無いって言われているので「iPhoneデバイス4.0」に変更します。


Code/OAuthTwitterDemoViewController.mにConsumer Keyを埋め込みます。

#define kOAuthConsumerKey @"" //REPLACE ME
#define kOAuthConsumerSecret @"" //REPLACE ME

エミュレーターで実行すると。


すごい。動きました。

ブラウザは内部のビューで処理してくれるし、PINコードも勝手に解析してくれます。
至れり尽くせり。とにかく早く実装したいという場合には良いかもです。

2010年8月11日水曜日

UIViewの判断

UIViewを判断するにはtagを使えば良いんですね。今までUIViewインスタンスを比較していました。

UIViewにtagというプロパティが存在するのでViewの特定に利用できます。
default値が0みたいなので、それ以外の値を使えば良いみたいです。

2010年8月2日月曜日

ツールバーを隠す

ツールバーを隠します。
[UIView beginAnimations:nil context:nil];
{
    [UIView setAnimationDuration:0.2];
    toolbar.transform = CGAffineTransformMakeTranslation(0, 44);
}
[UIView commitAnimations];

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>
特別なことは何も。

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

vimでタブ・スペース変換

忘れるので自分用メモ。

タブ→スペース
:set ts=4
:set expandtab
:retab

スペース→タブ
:set ts=4
:set noexpandtab
:retab

2010年7月28日水曜日

objective-cのメモリ管理について

Cocoaメモリ管理プログラミングガイトを読んでみました。メモ。

  • 1つのコードブロック内では、copy、alloc、およびretainを使用した回数と、releaseおよび autoreleaseを使用した回数は等しくなければならない。
  • 「alloc」または「new」で始まる名前のメソッドや、「copy」を含む名前のメソッド(たとえ ば、alloc、newObject、mutableCopy)を使用して作成した場合、または オブジェクトに retainメッセージを送信した場合にのみ、そのオブジェクトを所有できる。
  • 自分が所有するインスタンス変数を解放するためにdeallocを実装する。
  • (独自のdeallocメソッド内で親の実装を呼び出す場合以外は)deallocを直接呼び出してはならない。
  • 配列、辞書、集合などのコレクションにオブジェクトを追加または設定すると、コレクションがそ のオブジェクトの所有権を取得します。オブジェクトがコレクションから削除されたりコレクショ ン自体が解放されたりすると、コレクションは所有権を放棄します。

CDをDVDにコピーしたい

CDをDVDにコピーできないなんて知らんかったです。どうしてもやる必要があったので調べてみました。

2010年7月27日火曜日

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

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

twitterでOAuth。まずはアプリケーションの登録から。

最近良く耳にするOAuth。twitterで体験してみようと思い調べてみました。
まずはtwitterへのアプリケーション登録が必要なようです。

twitter developersのページから。

Register an appをクリック。

サインインします。

アプリケーション登録申請画面へ進みます。適当に入力。日本なので何の問題もありません。

以上!
メニューYour appsから作成したアプリケーション情報を閲覧する事ができます。

ここで重要なのは、「Consumer key」と「Consumer secret」です。後々のために控えておきます。

2010年7月24日土曜日

UIViewの角を丸める

UIViewの角を丸めるには以下のようにします。
view.layer.masksToBounds = YES;
view.layer.cornerRadius = 10;

UILabelをタッチに反応させる

UILabelをタッチに反応するようにするには。
UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.userInteractionEnabled = YES;

2010年7月6日火曜日

iPhone4げっと!

iPhone4、昨日手に入りました!遊び倒してやる。

2010年7月5日月曜日

Google App Engineさわってみたかったけど

登録にケータイのメールアドレスが必要でした。
私のアドレスは@の前に.(ピリオド)があります。これってRFCに準拠していないんです。
それが原因だと思うんですが、Google App Engineの登録に失敗します orz


どうしてくれよう…

2010年7月3日土曜日

カメラロール写真の画像サイズ

UIImagePickerでカメラロールの写真を読み込むと、UIImageでは縦長なのに、実際に幅・高さを取得すると横長になってしまいます。これにえらいはまってしまいました。


ここに答えがありました。
ポイントは、UIImageのimageOrientationプロパティを確認して必要ならば回転させるということ。

うーん。時間をかけすぎた。

2010年6月29日火曜日

UIToolBarの透過処理

画面をタッチしたらツールバーの表示/非表示が切り替わるようにしました。アニメーション付きで。ひとまずこんな感じのコードになりました。

- (void)switchToolbar {
    // ツールバーの表示/非表示を切り替える
    [UIView beginAnimations:nil context:nil];
    {   
        // 時間を0.25とする
        [UIView setAnimationDuration:0.25];

        CGFloat toAlpha;
        if (toolbar_.hidden) {
            toolbar_.alpha = 0.0;
            toolbar_.hidden = NO;
            toAlpha = 1.0;
        }       
        else {  
            [UIView setAnimationDelegate:self];
            [UIView setAnimationDidStopSelector:@selector(animationDidStop)];
            toAlpha = 0.0;
        }       
        toolbar_.alpha = toAlpha;
    }   
    [UIView commitAnimations];
}

- (void)animationDidStop {
    toolbar_.hidden = YES;
}

toolbar_はIBOutletでUIToolBarにバインドしています。
beginAnimations:context:でアニメーションを開始し、alpha値を変化させることでフワッとツールバーの表示/非表示を切り替えています。

2010年6月27日日曜日

UIActionSheetにプログレスバーを表示する

iPhoneで処理をバックグラウンドで実行する方法を探していて辿り着きました。
今後参考にするかもしれないのでメモ。


ちなみに、バックグラウンド処理にはperformSelectorInBackground:withObject:メソッドを利用します。

Core DataのAttributeに配列を利用する

ちゃんと読んでないけど実現可能?メモ。

2010年6月26日土曜日

クラス定数はない

Objective-Cにクラス定数はないようです。
UIKitとかで使われている定数を見ると、typedef enumで定義されているみたい。
typedef enum {
    MyClassConstValue1,
    MyClassConstValue2,
} MyClassConstValue;

@interface MyClass : NSObject {

}

@end
あ、値を持つ定数はどうするのかな。

2010年6月25日金曜日

Apacheの設定メモ

ごくごく一部分のメモです。Debianでsites-enabled、mods-enabledを有効にするコマンド。

a2ensite

a2enmod

すみません。忘れがちなので…。

Core Data の migration

Core Dataのモデルを変更したときの対処方法です。以下のページ通りやればできるのではということでメモ。


ちなみに、.xcdatamodeldファイルは以下の方法で作成できます。
  1. .xcdatamodelを選択
  2. メニュー「設計」→「データモデル」→「モデルバージョンを追加」を選択

2010年6月24日木曜日

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

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

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

以上!

2010年6月22日火曜日

VirtualBoxのインストール

Windows Server 2003にVirtualBoxをインストールすることになりました。ゲストOSはDebianです。

  • VirtualBox 3.2.4をダウンロード。
  • デフォルトの設定でインストール。
  • インストール完了。おぉ、再起動を要求されなかった。
  • Debian 5.0.4のネットワークインストールCDイメージをダウンロード。
  • 新規で仮想マシンを作成。OS:Linux、バージョン:Debian。
  • ストレージ設定でダウンロードしたDebianのISOイメージを選択。
  • ネットワークの設定で「ブリッジアダプタ」に変更。
  • 起動。
  • 普通にインストール→完了。

とりあえず動いた。うん良いんじゃないかな。

2010年6月21日月曜日

Gitを使い始めるまで

いつも忘れるのでメモ。

設定


% git config --global user.name "Name"
% git config --global user.email "mail@example.com"
% git config --global color.ui auto

リモートリポジトリの作成


% mkdir /REPODIR/project.git
% cd project.git
% git init --bare
% cd WHERE
% git clone /REPODIR/project.git project

2010年6月20日日曜日

1日後のNSDateインスタンスを取得する方法

Objective-CのNSDateで日付を1日進めたいときにどうするかです。こうします。
// 日付のオフセットを生成
NSDateComponents *dateComp = [[NSDateComponents alloc] init];

// 1日後とする
[dateComp setDay:1];

// 1日後のNSDateインスタンスを取得する
NSDate *date = [[NSCalendar currentCalendar] dateByAddingComponents:dateComp toDate:[NSDate date] options:0];

[dateComp release];
1日前の日付を取得する場合は[dateComp setDay:-1]で、1ヶ月後とかだとsetMonthを利用します。

自作のUITableViewで編集モードに対応する

UITableViewControllerを使わず、UIViewControllerを継承してUITableViewを操作しています。
編集モード対応でちょっとはまったのでメモを残しておきます。

-tableView:commitEditingStyle:forRowAtIndexPath:を定義


これは当たり前。

-setEditing:animated:を定義


これが必要。こんな感じでUIViewControllerのメソッドをオーバーライドします。
- (void)setEditing:(BOOL)editing animated:(BOOL)animated {
    [super setEditing:editing animated:animated];

    // テーブルの編集モードを変更
    [myTableView setEditing:editing animated:animated];
}

2010年6月18日金曜日

pythonメモ

文字列を日付に変換

from datetime import datetime
date = datetime.datetime.strptime('2010/06/18 13:30', '%Y/%m/%d %H:%M')
# => datetime.datetime(2010, 6, 18, 13, 30)

iPhone4を予約

許可が得られたので今日(もう昨日か)地元のソフトバンクショップでiPhone4を予約してきました。
Wホワイトにばっちり強制加入させられたぜー。

iPhoneでSingleton

iPhoneでSingleton。あるインスタンスをたくさんのViewから参照したいです。URLをメモ。


以上です。

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月16日水曜日

起動サービスの変更

RedHat系のディストリビューションでサーバ起動時に起動するサービスを変更する方法。
いっっつもコマンドを忘れて調べてしまうのでメモしときます。

chkconfig

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月14日月曜日

数字を3桁ごとにカンマ区切りにする

こんな感じでやってみた。

ヘッダ。
@interface MyViewController : UIViewController {
    // 3桁NumberFormatter
    NSNumberFormatter *formatter;
}
@property (nonatomic, assign) NSNumberFormatter *formatter;

コード。
- (NSNumberFormatter *)formatter {
    if (formatter == nil) {
        formatter = [[NSNumberFormatter alloc] init];
        [formatter setPositiveFormat:@"#,##0"];
    }

    return formatter;
}

- (void)dealloc {
    [formatter release];
    [super dealloc];
}

使ってみる。
str = [self.formatter stringFromNumber:[NSNumber numberWithInt:2010]];

NSTimerを停止する

参照したページ
えーと、こんな感じで良いのかな。

  • retainではなくassignで
  • (void)invalidate を使う
  • 使わないタイマーにはnilを
// ヘッダ
@property (nonatomic, assign) NSTimer *timer;

// 停止コード
[timer invalidate];
timer = nil;

2010年6月12日土曜日

Core Data デフォルトのデータを用意する

サンプルCoreDataBooksは初期起動時にいくつかの本の情報が入っています。あんな感じで、デフォルトのデータを提供したいのです。

CoreDataBooksはどうやってDocumentsディレクトリにCoreDataBooks.sqliteをコピーしているのかなーと「プロジェクト情報」を見たりなんなりで30分。結局ソースコードでコピーしている事が判明。最初に気づけと。

以下、手順です。

適当なプロジェクト、適当なデフォルトデータを作成する


ここでは、「Navigation-based Application」テンプレート、「Use Core Data for storage」付きでプロジェクトを作成。
アプリケーション起動後、+ボタンを連打し適当なデータを作成しました。

デフォルトデータをプロジェクトに追加する


さっき作った適当データは

/Users/ユーザ名/Library/Application Support/iPhone Simulator/バージョン/Applications/アプリ/Documents

あたりにあるので、それをXcodeのResourceグループあたりに放り込んでやります。


これでアプリケーションバンドルにデータが含まれるようになります。

データをコピーするようにコードを修正


AppDelegateのpersistentStoreCoordinator()というメソッドの中でデータをロードしているはずです。
そこを書き換えます。

変更前。
NSURL *storeUrl = [NSURL fileURLWithPath: [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"CoreDataTest.sqlite"]];

変更後。
NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"CoreDataTest.sqlite"];
NSFileManager *fileManager = [NSFileManager defaultManager];
// データがなければデフォルトデータをコピーする
if (![fileManager fileExistsAtPath:storePath]) {
    NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:@"CoreDataTest" ofType:@"sqlite"];
    if (defaultStorePath) {
        [fileManager copyItemAtPath:defaultStorePath toPath:storePath error:NULL];
    }
}
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];

これでデフォルトデータを提供できるようになりました。

2010年6月11日金曜日

Provisioning Profilesの作成とインストール

とうとう最後です。Provisioning Profileを作成します。手順の詳細はProvisioning Portal左上「How-To's」の「Creating Provisioning Profiles」に書いてあります。

Development Provisioning Profileの作成

  • 「Provisioning」→「Development」の「New Profile」ボタンを押下します。
  • 「Profile Name」を適当に入力します
  • その他適切に。

submitすると一覧に登録したProvisioning Profileが表示されます。

Development Provisioning Profileのインストール


作成したProfileをXcodeで使えるようにします。
  • Development Provisioning Profileの一覧にダウンロードボタンがあるので、押下しダウンロードする。
  • Xcodeのオーガナイザの「デバイス」→「Provisioning」にある「+ボタン」を押下し、ダウンロードしたProfileを選択する。

以上!さぁ、実機で動かしてみよう。

実機でアプリを動かす

  • Xcodeの左上を「iPhoneシミュレータ」から「iPhoneデバイス」にする。

これだけでOKなはず。実機で動いた。感動。いや、ほんとに長かった。

App IDの作成

実機でアプリケーションを動かすまであとちょっとです。今度はApp IDなるものを作成します。手順の詳細はProvisioning Portal左上「How-To's」の「Creating App ID」に書いてあります。

App IDの生成

  • 「App IDs」→「Manage」の「New App ID」ボタンを押下。
  • 「Description」を適当に入力。
  • 「App ID」を適切に入力。こちらの説明がわかりやすかったです。

Submitして完了!リストに追加したApp IDが表示されるはずです。

カメラのプレビューに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の勉強が必要ですね...

デバイスの登録

少しずつ進んで行きます。次はデバイス(iPhone)を登録します。手順の詳細は「How-To's」の「Assigning Devices」に書いてあります。

Unique Device Identifier(UDID)の確認


UDIDとやらがデバイス登録に必要なので確認しておきます。
  • デバイス(iPhoneなど)を接続する
  • Xcodeを起動する
  • メニューの「ウィンドウ」→「オーガナイザ」を選択
  • デバイスを選択し表示される「Identifier」の値が次の手順で必要となる

デバイスを追加する


  • iPhone Provisioning Portalの「Device」→「Add Devices」を押下
    • 「Device Name」を適当に入力する
    • 「Device ID」に先ほど確認したUDIDを入力する
  • submit

証明書の取得

iPhone Developer Programへの登録が完了すると「iPhone Provisioning Portal」が利用できるようになります。
実機でのアプリ開発を目指し、このページで証明書の取得、デバイスの登録などを行っていきます。

まずは証明書の取得です。手順の詳細は、「How-To's」の「Obtaining your Certificate」に書いてあります。
以下に私の作業メモを残しておきます。

1. Certificate Signing Request(CSR)を生成

  • アプリケーション→ユーティリティ→キーチェーンアクセスを起動
  • メニュー「設定」→「証明書」の「OCR」と「CRL」が「切」になっていることを確認
  • メニュー「キーチェーンアクセス」→「証明書アシスタント」→「認証局に証明書を要求」を選択
    • メールアドレスと通称は、iPhone Developerに登録しているやつを入力
    • CAメールアドレスは空でOK
    • 「ディスクに保存」「鍵ペア情報を指定」をチェック
  • 任意の場所に保存する。2048bit、RSAで。

2. CSRを登録

  • 「iPhone Provisioning Portal」の「Certificates」→「Development」→「Request Certificate」を選択
  • さっき保存したCSRを選択し、submit

3. 証明書のダウンロードとインストール

しばらく待ってページをリロードすると、証明書一覧がこんな感じになるはず。
2つの証明書(.cer)をダウンロードします。
実行するとキーチェーンアクセスが起動し、証明書が登録されます。

4. 秘密鍵のバックアップ

How-To'sによるとしておくべきらしい。
  • キーチェーンアクセスでさっき作成した秘密鍵を選択
  • メニュー「ファイル」→「書き出す」
  • フォーマットは「個人情報交換(.p12)」
  • 保存が完了したら他のマシンで保管しておこう

2010年6月10日木曜日

iPhone Developer Program登録完了!

夜、サポートからメールがきました。
「もう1回アクティベーションコードをクリックしてみて」


無事、登録完了しました!
サポートの対応が早くてびっくりです。

続続・iPhone Developer Programに登録

あー、タイトルうぜーですね。
夕方、Apple Developer Supportからメールが来ました。対応が早いな。すごい。

アップルストアの購入履歴を見て
  1. 『ご請求、ご連絡先』
  2. 注文番号 (W+数字8桁)
を教えてくれとのこと。メール送った。

続・iPhone Developer Programに登録

昼頃にiPhone Developer Programのアクティベーションコードがメールで送られてきました。


早速メールのリンクをクリックすると、、、


エラー... orz
素直に「Contact Us」します。
「Your Region」を「Japan」にすれば日本語でメッセージを書いても大丈夫みたい。

送信したら「受理したからしばし待て」的なメールが来た。
待ちますとも。

iPhone Developer Programに登録

今までエミュレータでアプリケーションを作り込んできたけど、そろそろ実機で動かしたくなりました。
iPhone4も出るしここいらでiPhone Developer Programに登録してしまえってことで登録してみました。

登録方法についてはネット上のいたるところに情報が転がっているので割愛しますが、
アカウント情報に2バイト文字が含まれないように注意し、手続きを進め、
無事にApple Online Storeで購入するところまでいきました。

24時間以内にメールが届くって事なので待っときます。
以下、この先の作業で参考にしようと思っているページです。


明日が楽しみだ。

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月8日火曜日

git archive

いつも忘れるのでメモしておきます。gitでtar.gzを作成する方法です。

git archive --format=tar --prefix=project-dir/ HEAD | gzip > project.tgz

2010年6月7日月曜日

結局Hummings2に

mac版のtwitterクライアントを色々試していたのですが、結局、初めて使ったHummings2に落ち着きました。マウスを使わずに操作できる点が秀逸です!

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日水曜日

Blogger Syntax Highlighterを導入しました

以下のページを参考にし、ソースコードをハイライトする「Blogger Syntax Highlighter」を導入しました。
以下、導入の手順です。

1.FaziBear's Blogger Widgetsのページにアクセスするも...

なんか「添付ファイルをダウンロード」となってしまい、期待するページに行きませんでした。


しょうがないので素直にblogger.htmlをダウンロードしました。

2.blogger.htmlをブラウザで表示

WEBブラウザで表示してみました。


お、それっぽい。「Add to Blogger」をクリック。

3.Bloggerに追加

ログインすると以下の画面に。


タイトルを空にしてから「ウィジェットを追加」をクリックします。そうしないと、ブログのウィジェット欄に入力したタイトルが表示されてしまいます。


保存すれば終了。

4.これで使えるはず

試してみます。

<pre name="code" class="c">
int main() {
  printf("Hello, World!");

  exit(0);
}
</pre>
int main() {
  printf("Hello, World!");

  exit(0);
}

できた!

カメラのプレビュー画像をいじってみる(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)とかやってやると落ちずに動く。
実機でやるとまた違う結果が出るかしら。

Project2003を使ってみました

線表を作成する必要があったのでMicrosoftのProject2003を使ってみました。初めて使うに当たってまったく初めて使う人のための Project 2003 入門を参考に基本的な使い方を学びました。いくつかポイントを紹介したいと思います。

ビューバーの表示

メニューの「表示」→「ビューバー」を選択するとビューバーが表示されます。ガントチャートやリソースシートを切り替えるのに便利です。

稼働時間の変更

「ツール」→「稼働時間の変更」で稼働時間を変更できます。例えば「土曜は働くよ!」みたいな。


プロジェクトの開始日

「プロジェクト」→「プロジェクト情報」でプロジェクトの開始日を設定できます。これを適切に設定しないでタスクを作成すると、開始日が2003年になってしまいました。7年前か。。。


マイルストーンの設定

タスクの期間を0にするとマイルストーンとなります。


期間の?

新規タスクを追加したときに「期間」の欄に表示される「?」は見積もり期間を表すそうです。これを表示しないようにするには。。。
「ツール」→「オプション」→「スケジュール」→「新しいタスクに見積もり期間を設定する」のチェックを外します。

アウトラインを設定する

「レベル下げ」を利用してタスクのアウトラインを行います。


タスクの依存関係を設定する

タスクを複数選択し、リンクボタンを押すことで依存関係を作成します。


担当者の設定

1.リソースシートを表示します

2.リソース名に名前を入力します


3.ガントチャートに戻って、「ツール」→「リソースの割り当て」でタスクに担当者を設定します

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からやってみようかしら。