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プロパティを確認して必要ならば回転させるということ。

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