2011/02/25

MercurialEclipse が dotencode なんちゃらってエラーを吐くようになった

あるプロジェクトを Eclipse にインポートしたら、
requirement 'dotencode' not supported!.
ってエラーが出るようになってしまいました。


dotencode Mercurial でググってみると、どうも Mercurial 1.7.0 以降で追加された新しいリポジトリ形式らしいです。
Tortoise HG では問題なく動作しているので、
ウィンドウ → 設定 → チーム → Mercurial
  • Use default (built-in) Mercurial executable のチェックを外す
  • Mercurial 実行ファイルに  C:\Program Files\TortoiseHg\hg.exe  と入力
とすると、とりあえず動作するようにはなりました。

2011/02/08

Android のデフォルトアイコンを調べる

Android アプリケーションでは
android.R.drawable.xxx
と書くことでデフォルトで用意されているアイコンなどを使用することができます。
(Rの前に android とついているところがポイントです)

どのようなアイコンが使えるのか調べるためには、

android-sdk-dir/platforms/android-X/data/res

を見れば良いのですが、このディレクトリにあるアイコン全部を使えるわけではないのです。
どうも、「layout で使う用」と「一般に公開する用」で出しわけている模様。

「デフォルトで使えるアイコンの一覧が欲しいなぁ」と思って探してみたものの、見つかりませんでした。(あんまり本気で探してないから、本当はあるかも)
プログラマーたるもの、無いものは作れば良いのです。
というわけで、作ってみました。
意外とあっさり。リフレクションって素晴らしい!

2011/02/04

Eclipse に既存の Android プロジェクトをインポートする際、はまるポイント3

Eclipse に既存の Android プロジェクトをインポートする際、はまるポイント1
Eclipse に既存の Android プロジェクトをインポートする際、はまるポイント2
に続いて、もう一点はまったのでご報告します。

インポートした際、@override などのアノテーションでひっかかる場合は、以下の方法で治るかもしれません。

2011/10/11 追記
最近、Javaのアップデートが激しいので、以下のようなエラーが出ました。
Android requires compiler compliance level 5.0 or 6.0. Found '1.7' instead. Please use Android Tools > Fix Project Properties.
これ、指示されたように Fix Project Properties を選択してもうまくいかない場合があります。
というのも、Fix Project Properties は以下のコンパイラ選択を自動でやり直してくれるツール(?)なのですが、何故か1.5にしちゃうんです。
もとが1.6で作成されているプロジェクトの場合、Fix Project Properties では直りません。以下の手順を手動で実行して1.6にしてあげましょう。


プロジェクト右クリック → プロパティー → Java コンパイラー

プロジェクト固有の設定を可能にするにチェックを入れて、コンパイラ準拠レベルを変更します。
ちょっと前までは、1.5 でないとコンパイルできなかったのですが、最近になって 1.6 でもコンパイルが通るようになったみたいです。(あんまり調べてませんが…)
どちらか、プロジェクトで採用しているコンパイラを選ばないとうまくいきません。


また、デフォルトのコンパイラから変更してしまうことも出来ます。

ウィンドウ → 設定 → Java → コンパイラ

この方法で変更すると、同じワークスペースのプロジェクト全部に影響するので気をつけてください。


2011/02/03

EGIT をインストール

@ Eclipse 3.6 Helios Service Release 1

EclipseにGitの機能を追加するプラグインEGIT
ちょっと古いサイトを見ると、EGITはアップデートサイトからインストールできますって書いてあって、以下のアドレスが示されている。
http://www.jgit.org/update-site/
で、いつものように、
ヘルプ → 新規ソフトウェアのインストール
ってやると普通にインストール出来ちゃったりする。

でも、よく見てみるとバージョンが 0.4.9 とかで古い…
(EGIT 関連のサイトを巡ってて気づいたのです)

本家の EGITのサイト に行ってみると、すでに 0.10.1 がリリースされているのだという。
むー、大分古いのを入れてしまった。
(といっても、まだメジャーバージョンアップはしてないのね)
というわけで、アンインストールして、本家のEGITを入れることにしました。

ところが!!
リポジトリーの追加をしようとすると 「ロケーションの重複」 と出て追加が出来ないのです。

どういうこと?
と思ったら、EGIT のサイトはデフォルト(?)でプラグインのリストに入ってるみたいですね。
"使用可能なソフトウェア・サイト"というリンクをクリックして

出てきた一覧から、本家のサイト http://download.eclipse.org/egit/updates を探します。

チェックして、OKを押せば作業対象のドロップダウンリストに表示されるようになります。
めでたしめでたし。

ちなみに、0.4.9 と 0.10.1 ではかなりインターフェースに差がありました。

2011/02/02

Android で Googleドキュメントの一覧を取得する

@ Xperia (Android 2.1 Eclair)

Googleドキュメントやその他のGoogleのサービスは、その多くがHTTPアクセスでXMLを取得できるAPIを用意しているため、比較的容易にその通信を実装することができます。

↓ Google Docs のプロトコル
http://code.google.com/intl/en/apis/documents/docs/3.0/developers_guide_protocol.html

しかし、定型文のプロトコルを毎回ユーザが実装するのは面倒くさい。ってことで、Google は各言語からアクセス可能なラッパーAPIを用意しています。

↓ Google Data API
http://code.google.com/intl/en/apis/gdata/docs/client-libraries.html

ちょっと前まではこの Google Data API で Android用のAPIも定義されていたのですが、最近になって分離したみたいです。というわけで、上記の Google Data API では Android用のプログラムは書けません。
トライはしてみたのですが、どうもうまくいきませんでした。API としては結構良い出来なんですがねぇ。
とはいえ、GoogleがAndroidを見捨てるはずがありません。現在は以下のAPIを使うことができます。

↓ Google API Client Library for Java
http://code.google.com/p/google-api-java-client/

というより、今後Java用のAPIはこちらに移行するんでしょうかね。 このAPIはまだアルファバージョンのため、まだ不完全感が漂っていますが、使ってみると結構良い感じです。
以下にAndroidアプリケーションから Google API Client Library for Java を使って Googleドキュメント の一覧を取得するサンプルを挙げておきます。
  1. いつも通り Eclipse上で Android プロジェクトを作成します
  2. Google API Client Library for Java から google-api-client-x.x.x-java.zip をダウンロードして解凍します。今回は、google-api-client-1.2.2-alpha-java.zip を使用しました。
  3. プロジェクト直下に lib ディレクトリを作成し、先のライブラリをおきます。少なくともこのバージョンでは google-api-client-1.2.2-alpha.jar という1ファイルにまとめられているようです。
    (その他のファイルはソースコード等です。デバッグには使えますが、コンパイルするだけなら必要ありません。)
  4. Eclipse上でプロジェクトをリフレッシュ(F5)します。lib ディレクトリが表示されるので、ライブラリを選択して、
    右クリック→ビルド・パス→ビルド・パスに追加
    としてライブラリを登録します。
  5. MainActivity.java、 AndroidManifest.xml を以下のように修正します。
    MainActivity.java
    package com.kokufu.test;
    
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    import android.accounts.Account;
    import android.accounts.AccountManager;
    import android.accounts.AuthenticatorException;
    import android.accounts.OperationCanceledException;
    import android.app.Activity;
    import android.content.Intent;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.ViewGroup.LayoutParams;
    import android.widget.TextView;
    
    import com.google.api.client.googleapis.GoogleHeaders;
    import com.google.api.client.googleapis.GoogleTransport;
    import com.google.api.client.googleapis.GoogleUrl;
    import com.google.api.client.http.HttpRequest;
    import com.google.api.client.http.HttpTransport;
    import com.google.api.client.util.Key;
    import com.google.api.client.xml.XmlNamespaceDictionary;
    import com.google.api.client.xml.atom.AtomParser;
    
    public class MainActivity extends Activity {
        private static final String TAG = "GoogleDocsTest";
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
    
            // AccountManager を通じてGoogleアカウントを取得
            AccountManager manager = AccountManager.get(this);
            Account[] accounts =
                        manager.getAccountsByType("com.google");
            Bundle bundle = null;
            try {
                bundle = manager.getAuthToken(
                        accounts[0], // テストなので固定
                        "writely",   // ※1
                        null,
                        this,
                        null,
                        null).getResult();
            } catch (OperationCanceledException e) {
                Log.e(TAG, "", e);
                return;
            } catch (AuthenticatorException e) {
                Log.e(TAG, "", e);
                return;
            } catch (IOException e) {
                Log.e(TAG, "", e);
                return;
            }
    
            String authToken = "";
            if (bundle.containsKey(AccountManager.KEY_INTENT)) {
                // 認証が必要な場合
                Intent intent = bundle.getParcelable(AccountManager.KEY_INTENT);
                int flags = intent.getFlags();
                flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK;
                intent.setFlags(flags);
                startActivityForResult(intent, 0);
                // 本当はResultを受けとる必要があるけど割愛
                return;
            } else {
                // 認証用トークン取得
                authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
            }
    
            // 送信準備
            HttpTransport transport = GoogleTransport.create();
            GoogleHeaders headers = (GoogleHeaders) transport.defaultHeaders;
            headers.setApplicationName("Kokufu-GoogleDocsTest/1.0");
            headers.gdataVersion = "3";
            headers.setGoogleLogin(authToken); // 認証トークン設定
    
            // Parser を準備して Transport にセットする
            AtomParser parser = new AtomParser();
            // 空の Dictionary でとりあえず問題なさげ
            parser.namespaceDictionary =
                       new XmlNamespaceDictionary();
            transport.addParser(parser);
    
            // 送信
            Feed feed = null;
            try {
                HttpRequest request = transport.buildGetRequest();
                request.url = new GoogleUrl("https://docs.google.com/feeds/default/private/full"); // ※2 
                feed = request.execute().parseAs(Feed.class);
            } catch (IOException e) {
                Log.e(TAG, "", e);
                return;
            }
    
            // 結果を表示
            String tmp = "";
            for (Entry entry : feed.entries) {
                tmp += entry.title + "\n";
            }
            TextView v = new TextView(this);
            v.setText(tmp);
            this.addContentView(
                    v,
                    new LayoutParams(LayoutParams.WRAP_CONTENT,
                                     LayoutParams.WRAP_CONTENT));
        }
    
        /**
         * Feed タグ
         */
        private class Feed {
            @Key("entry")
            public List entries = new ArrayList();
        }
    
        /**
         * Entry タグ
         */
        private class Entry {
            @Key
            public String summary;
    
            @Key
            public String title;
    
            @Key
            public String updated;
          }
    }
    

    AndroidManifest.xml に以下のパーミッションを追加
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="android.permission.USE_CREDENTIALS" />
    <uses-permission android:name="android.permission.INTERNET" />
    
onCreate の中に全部書いちゃうという酷いプログラムですが…まぁ可読性は良いってことで。これくらいシンプルにかけちゃうっていうのが、ライブラリを使う魅力ですね。

わかりやすさを重視して書いているので、Google Account が登録されていないような端末で動作させるとアプリが落ちます。その他、通信状況が悪かったりしてもうまくいかないことがあると思いますが、その辺ご容赦ください。

また、このやり方で、Google Docs 以外のGoogleサービスにもほとんどアクセスが可能です。
ポイントは ※1 に書いてある"writely"という文字列(authTokenType)。この"writely"が、Google Docsに対するアクセス権の認証を意味しています。
その他の authTokenType 一覧は無いものかと探してみたら、まさにタイムリー!!
Mine さんの AndroidのGoogle Authenticatorを解析(?)してみた という投稿に詳しく書いてあります。
素晴らしい!

また、※2の Query についてはGoogle Docs のプロトコル
http://code.google.com/intl/en/apis/documents/docs/3.0/developers_guide_protocol.html
に詳しく載っています。
この辺を読まなくても良いくらいに隠蔽してくれるとAPIとして使いやすいんですけどねぇ。
まぁ、アルファ版なんで仕方がありません。API仕様もこれからどんどん変わっていく可能性があるので、この記事の賞味期限は短めだろうなぁ…
2011/02/01

hg から git へ変換

最近、gitの出番が多くなってきました。
今まで、自分のソースの管理は hg (Mercurial) でやってきたのですが、git の操作に慣れるためにも、これまでの履歴を git で管理しようかと思いまして、こちらのサイトを参考に変換してみました。

$ git clone git://repo.or.cz/fast-export.git

$ mkdir git

$ cd git; git init; cd ..

$ mkdir hg

$ cd hg; hg clone ********; cd ..

$ ls
fast-export  git  hg

$ cd git/

$ ../fast-export/hg-fast-export.sh -r ../hg/********

  1. fast-export をとってきます
  2. git の空レポジトリを作成します
  3. 変換元の hg レポジトリをクローン
  4. git のトップディレクトリで hg-fast-export.sh を実行すれば、変換完了です

とりあえず、私の環境では、ブランチもきちんと変換されました。
個人用のレポジトリなので、あまり複雑ではないですが。