<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
   <channel>
      <title>Flashマインドマップ開発ブログ</title>
      <link>http://www.plants-web.jp/flashmind/blog/?from=rss</link>
      <description>シリウステクノロジーズ という会社で、ラボの所長をやってます。

Flashでマインドマップを書けるツールを開発していましたが、最近全然やってません。。。

一応成果はFlashmind.jpにて公開しています。</description>
      <language>ja</language>
      <copyright>Copyright 2010</copyright>
      <lastBuildDate>Mon, 05 Jan 2009 12:46:39 +0900</lastBuildDate>
      <generator>http://www.sixapart.com/movabletype/?v=3.2-ja-2</generator>
      <docs>http://blogs.law.harvard.edu/tech/rss</docs> 

            <item>
         <title>2009年は色々復活したい</title>
         <description>明けましておめでとうございます。

昨年は、人生で初めて病気による入院を経験しました。
病気に関連して、なるべく残業を減らしたり、それまで「自分がやりたいこと」にこだわっていた働き方を少々変更して一歩引いた視点から仕事に取り組んでみたりと、色々な試行錯誤をした年でもありました。

今年こそは体を完治させ、マラソンやバンド活動なども復活させたいと思います。
仕事の上でも、去年よりはもう少しギアを上げていこう。
あとこのブログもちゃんと書く。いいかげんタイトルも変えなくては。

今年一つ目標を挙げるとするならば、英語。去年読み書きは少しは上達しましたが、喋る
シーンとなるとやっぱり苦手意識が出てしまう。
海外で働けるくらいのレベルを目指します。

</description>
         <link>http://www.plants-web.jp/flashmind/blog/2009/01/2009.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2009/01/2009.html</guid>
         <category>日記</category>
         <pubDate>Mon, 05 Jan 2009 12:46:39 +0900</pubDate>
      </item>
            <item>
         <title>最高! コクーン歌舞伎 夏祭浪速鑑</title>
         <description><![CDATA[コクーン歌舞伎 夏祭浪速鑑を見てきました。
<a href="http://www.bunkamura.co.jp/cocoon/lineup/shosai_08_kabuki9.html" target="_blank">http://www.bunkamura.co.jp/cocoon/lineup/shosai_08_kabuki9.html</a>

過去歌舞伎は数回見たことがあるのですが、今回は海外でも評判の高いコクーン歌舞伎。
オークションでも倍くらいの値段で取引されるくらい人気のチケットです。

夏祭浪速鑑は歌舞伎座で一度見たことのある題目でしたが、コクーン歌舞伎は串田和美による演出が斬新ということで、以前見たものとの違いも見所でした。

感想としては、とにかく最高! いままで見た歌舞伎や演劇の中でも一番感動した作品でした。
歌舞伎ならではの様式美と、串田さんによる陰影の濃い立体感のある演出が組み合わさり、とても素晴しい仕上りでした。
歌舞伎の中には、事前に筋書などを知っていないと十分楽しめないものもありますが、コクーン歌舞伎は歌舞伎について何も知らなくても十分楽しめるはずです。
伝統と様式美だけに縛られず、大切な物は残しながら新しい演出を取り入れていて、海外でも高い評価を得ている理由も納得です。

歌舞伎を観たことのない人でも違和感なく見れると思います。]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2008/06/post_93.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2008/06/post_93.html</guid>
         <category>日記</category>
         <pubDate>Sun, 22 Jun 2008 22:17:56 +0900</pubDate>
      </item>
            <item>
         <title>[Android] android.webkit.BrowserCallbackAdapter を m5 バージョンに対応させる</title>
         <description><![CDATA[<p>Android の SDK が m3 から m5 に変わり、API に大幅な変更が出ていますが、これまで使っていた、BrowserCallbackAdapter が使えなくなってしまったのでそれを変更するメモ。</p>

<p>まず、M5 の <a href="http://code.google.com/android/migrating/m3-to-m5/m5-api-changes.html#browser" target="_blank">Changes Overview のページ</a>には、</p>

<blockquote>BrowserCallback has been removed and the functionality has been separated with two new classes: android.webkit.WebViewClient and android.webkit.WebChromeClient.</blockquote>

<p>とあるので、WebViewClient と、WebChromeClient の2つのクラスを使うように変更すればいいみたい。</p>

<p>そもそもやっていたことは、サーバ上の HTML を取得してきた時点で、その HTML の一部を書き換える処理。<br/>
BrowserCallback クラスを拡張したクラスを作って、そのクラスを WebView に setBrowserCallback で渡していました。</p>

<pre class="prettyprint">
public class ContactView extends Activity {
	private WebView wv ; // WebView 
       (省略)
	/**
	 * callback Class for loading url
	 * @author hal
	 */
        public class MyCallback extends android.webkit.BrowserCallbackAdapter{
       (省略)
        public boolean overrideUrlLoading(String url){
    		this.myLoadUrl(url); // manual load
    		return false;
        }
        public void myLoadUrl(String url){
                // HttpClient クラスを使ってデータを取得して、書き換えて data 変数に格納する処理
                (省略)
                // WebView へ 表示
            	wv.loadDataWithBaseURL(url,data, "text/html", method.getResponseCharSet(),null);
        }
    }
    /**
     * initialize
     */
    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.view_contact);
    	// WebKitのViewを取得        
        wv = (WebView) findViewById(R.id.web);
        // コールバック用クラスを設定
        cb = new MyCallback();
        wv.setBrowserCallback(cb); 
      	// JavaScript の利用を許可
        wv.getSettings().setJavaScriptEnabled(true);
        
    }
       (省略)
}
</pre>
という処理ですね。

<p>で、BrowserCallbackAdapter が使えなくなり、WebViewClient と WebChromeClient に分割され使いかたも変わっています。WebViewClient が、キーイベントと URL ロード関連のイベントリスナ、WebChromeClient が ウィンドウ関連のイベントリスナになっている模様。<br/>
WebViewClient の リファレンスを見ると、shouldOverrideUrlLoading というメソッドがあるので、それを使えばいいのかな。第一引数に WebView のインスタンスが渡ってくるようです。</p>

<p>今回の場合、 BrowserCallbackAdapter の代わりに WebViewClient を継承したクラスを、 WebView::setWebViewClient でセットしてあげれば良さそう。</p>

<p>というわけで、以下のように変更してみたところ無事動きました。</p>
<pre class="prettyprint">
public class ContactView extends Activity {
       (省略)
	/**
	 * callback Class for loading url
	 * @author hal
	 */
        public class MyCallback extends android.webkit.WebViewClient{
       (省略)
    	/**
    	 * Handling method called when webkit loading url
    	 * @param wv WebView class instance
    	 * @param url loading url
    	 */
    	public boolean shouldOverrideUrlLoading(WebView wv,String url){
        	Log.d(TAG, "load start:" + url);
    		this.myLoadUrl(wv,url); // manual load
    		return false;
    	}
        public void myLoadUrl(String url){
                // HttpClient クラスを使ってデータを取得して、書き換えて data 変数に格納する処理
                (省略)
            	wv.loadDataWithBaseURL(url,data, "text/html", method.getResponseCharSet(),null);
        }
    }
    /**
     * initialize
     */
    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.view_contact);
    	// WebKitのViewを取得
        
        wv = (WebView) findViewById(R.id.web);
        // コールバック用クラスを設定
        cb = new MyCallback();
        wv.setWebViewClient(cb);
      	// JavaScript の利用を許可
        wv.getSettings().setJavaScriptEnabled(true);
        
    }
       (省略)
}
</pre>

実際には、バージョンアップに伴い AndroidManifest.xml やら layout 用の xml やらも修正する必要がありましたが本筋ではない部分なので割愛しました。]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2008/04/android_androidwebkitbrowserca.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2008/04/android_androidwebkitbrowserca.html</guid>
         <category>Android</category>
         <pubDate>Wed, 02 Apr 2008 11:11:17 +0900</pubDate>
      </item>
            <item>
         <title>ウノウさんの所へ遊びに行ってきた。</title>
         <description>昨日はウノウさんのとこで勉強会をしました。
ウチのエンジニアと先方のエンジニアとで軽くプレゼンをして、その後懇親会を行いました。

シリウス関：シリウスと、ラボの取り組みについて紹介
ウノウ尾藤さん：php4 -&gt; php5 へのソース変換器について
シリウス灰田：OpenID について
シリウス安藤：スクラムについて
ウノウ山本さん：QA3分間テスティング

yacc とか lex については、だいぶ昔に電卓を作ってみたりしましたが、久しぶりに聞いて新鮮だった。
尾藤さんには、CTO として見習うことが多いなぁと思いました。
といっても僕は今 CTO じゃないけど。
とにかく、もっと愚直に技術を極めていきたいと思います。
</description>
         <link>http://www.plants-web.jp/flashmind/blog/2008/03/post_92.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2008/03/post_92.html</guid>
         <category></category>
         <pubDate>Wed, 05 Mar 2008 09:31:01 +0900</pubDate>
      </item>
            <item>
         <title>久しぶりに更新してみる</title>
         <description>シリウスラボ ( http://lab.cirius.co.jp )の方が忙しいのと、体調不良などでいつのまにか更新が滞っていますが、ようやく元気になってきたので、ここらでエントリしておきます。

最近は、tumblr が面白いですね。soup.io も。
ちなみに僕のは
http://tumblr.plants-web.jp/
http://hal_sk.soup.io/
です。

このように reblog できるサービスのことを、ヒップホップのようなサービスだと誰かが書いていましたが、色んな人がpostした画像やテキスト、ムービーが渾然と流れている様は、確かにそんな側面があるなと思いました。

と、とりとめのない感じですが、生きてます。ということで。
</description>
         <link>http://www.plants-web.jp/flashmind/blog/2008/02/post_91.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2008/02/post_91.html</guid>
         <category>日記</category>
         <pubDate>Fri, 22 Feb 2008 00:43:44 +0900</pubDate>
      </item>
            <item>
         <title>初日の出@都庁の展望台</title>
         <description><![CDATA[あけましておめでとうございます。

<a href="http://www.metro.tokyo.jp/INET/OSHIRASE/2007/11/20hbj100.htm">都庁の展望台から初日の出を見れるイベント</a>というのがあったのですが、妻が抽選に応募したら見事当選したので行ってまいりました。

5:30 に入場開始でしたが、時間より10分早く到着したのに既に30名くらいの行列。
でもなんとか東側の窓際をゲットできました。
日の出は6:51だったので1時間以上あったわけですが、すばらしい夜景と、序々に変化していく空の色を見ているうちにあっというまに日の出の時間になりました。
<a href="/flashmind/blog/images/2008newyear.jpg" target="_blank"><img src="/flashmind/blog/images/2008newyear_t.jpg"></a>
天気が良く、夫婦で感動して帰ってきました。

去年は「自分は何ができるのか、すべきか」ということで色々と悩み、試行錯誤をした年でした。
後半、ようやく自分がやるべきことに真っ直ぐに取り組めるようになってきたように感じています。

今年は成果を出す年。これまでの自分の知識や経験を総動員して全力で仕事に取り組みます。
チームのみんな、頼りにしてます! 

]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2008/01/post_90.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2008/01/post_90.html</guid>
         <category>日記</category>
         <pubDate>Tue, 01 Jan 2008 21:22:20 +0900</pubDate>
      </item>
            <item>
         <title>ジオメディア新年会のお知らせ</title>
         <description><![CDATA[僕は会社では<a href="http://lab.cirius.co.jp/">シリウスラボ</a>なんてものをやってますが、その関連で<a href="http://lab.cirius.co.jp/index.php?%E3%82%B8%E3%82%AA%E3%83%A1%E3%83%87%E3%82%A3%E3%82%A2%E6%96%B0%E5%B9%B4%E4%BC%9A2008">ジオメディア新年会2008</a>なるものを企画しました。

位置情報関連のメディアに興味のある方は、是非ご参加ください!

<blockquote>シリウスラボの活動を通じて様々なジオメディア開発者やサービス提供者の方と交流させていただいてきましたが、皆とても魅力的な人ばかり。「ジオメディア開発者が集まって飲んだら絶対楽しいよね」という単純な動機の元、この度「ジオメディア新年会」という飲み会を企画させていただくこととなりました。

2008/1/11(金) 19:30 から、渋谷を予定しています。</blockquote>]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2007/12/post_89.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2007/12/post_89.html</guid>
         <category>シリウス</category>
         <pubDate>Fri, 28 Dec 2007 09:11:08 +0900</pubDate>
      </item>
            <item>
         <title>[Android] WebKit で JavaScript ブリッジ</title>
         <description><![CDATA[<p>JavaScript ブリッジを使って、 Android のオブジェクトをリモート側で操作してみましたのでまとめ。</p>

<p>WebKit とは、Android 上で 動作する Webブラウザです。<br>
これを使ってリモートサイトの HTML を表示し、Android 側のコンタクトリストを 表示させてみます。<br>
WebKit を使うには、WebView を layout/main.xml に定義します。<br>
</p><pre class="prettyprint">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    &gt;
&lt;TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="Hello World, WebKitTest"
    /&gt;
&lt;WebView 
    id="@+id/web"
	android:layout_width="fill_parent"
	android:layout_height="wrap_content"
	/&gt;
&lt;/LinearLayout&gt;
</pre>

<p>onCreate で、WebView に対して 読み込む URL を指定します。<br>
wv.addJavascriptInterface(user, "user");<br>
で、後ほど出てくる UserObj クラスのインスタンスを サーバ側の JavaScript で操作できるようにしています。<br>
JavaScript では、user という変数名で参照できます。<br>
</p><pre class="prettyprint">    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        // レイアウト用の xml を設定
        setContentView(R.layout.main);
        // バインド用の xml ファイルを設定
        UserObj user = new UserObj();
        // WebKitのViewを取得
        WebView wv = (WebView) findViewById(R.id.web);
        // ブリッジ設定
        wv.addJavascriptInterface(user, "user");
        // JavaScript の利用を許可
        wv.getSettings().setJavaScriptEnabled(true);
        // 最初のページを表示
        wv.loadUrl("http://www.plants-web.jp/android/index.html");
    }
</pre>

<p>UserObj クラスは、自分のUserID を返却する getUserId() というメソッドと、コンタクトリスト内の文字列を配列で返す getContactList() というメソッドが定義されています。<br>
コンタクトリスト内のデータを取得する方法は省略しますが、<a href="/flashmind/blog/2007/11/android_contentprovider.html">以前のエントリ</a>を参考にしてください。<br>
</p><pre class="prettyprint">    /**
     * ユーザの情報をサーバ側へ渡すためのブリッジクラス
     * @author hal
     *
     */
    class UserObj{
        /**
         * ユーザIDを返却する
         * @return ユーザID
         */
                public String getUserId(){
                        return "useridA";
                }
                /**
                 * コンタクトリストを返却する
                 * @return コンタクトリスト
                 */
            public List&lt;String&gt; getContactList(){
                ArrayList&lt;String&gt; items = new ArrayList&lt;String&gt;(); 

                // コンタクトリストから取得するカラムを指定。とりあえず全部取っとく
                ・・・(省略)・・・
                return items;
            }
    }
</pre>

<p>サーバ側の処理を書きます。wv.addJavascriptInterface(user, "user"); でブリッジを設定していますので、UserObj クラスのメソッド、user.getUserId() などが利用できます。<br>
</p><pre class="prettyprint">&lt;html&gt;
&lt;body&gt;
test
&lt;hr&gt;
&lt;script type="text/javascript"&gt;
document.write("UserID:" + user.getUserId());
document.write("&lt;hr&gt;[Contacts]&lt;br&gt;");
contacts=user.getContactList();
for(i = 0; i &lt; contacts.size(); i++) {
    document.write(contacts.get(i) + "&lt;br&gt;");
}
&lt;/script&gt;
&lt;hr&gt;
&lt;a href="page2.html"&gt;page2&lt;/a&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>

<p>これを実行すると、以下のような画面が表示されます。<br>
"test"行以下が、リモートサーバから取得されたHTML ファイル。
"UserID:UserIdA"部分がユーザID、Contacts から hr までが、実際にAndroid 内に入っていたコンタクトリストになります。
<img src="/flashmind/blog/images/android_bridge.png"><br>
このように、JavaScript ブリッジを使うことにより、リモートサーバ側のロジックで Android 内のデータを操作することが可能となります。
</p><p>
また、page2.html へのリンクがありますが、これを選択すると、page2.html が表示されます。<br>
現状のコードでは、全画面にpage2.html の内容が表示され、Hello World ... 部分などは消えてしまいます。
このまま、同じレイアウト内でページを遷移させる方法もありますが、長くなったのでそれは次回で。</p>



]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2007/12/android_webkit_javascript.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2007/12/android_webkit_javascript.html</guid>
         <category>Android</category>
         <pubDate>Tue, 25 Dec 2007 00:50:27 +0900</pubDate>
      </item>
            <item>
         <title>Android の GUI デザインツール</title>
         <description><![CDATA[Android で、View 部分を作る為の ツールについて。

Android の View を作る際には、res/layout/ 以下に view 用の xml ファイルを作成し、Activity  クラスから setContentView で呼び出します。

ただし、レイアウトを xml ファイルで作成するのは結構大変。
GUI でデザインレイアウトを作成する"DroidDraw"というサービスが公開されていますので、使ってみました。

<a href="http://www.droiddraw.org/" target="_blank">http://www.droiddraw.org/</a>
<img src="/flashmind/blog/images/t_droiddraw.png" alt="DroidDraw">

使いかたは簡単。左にある Screen というキャンバスに、右にあるパレットから選んだ部品をドラッグしてレイアウトをしていくだけです。

部品には、ボタンなどの各種コンポーネントにあたる Widgets、 LinearLayout などのレイアウト用の部品である Layouts、の二種類があります。
配置した部品は、選択後 Properties タブを選択することで サイズや表示テキストなどの設定をすることができます。

レイアウト作成後 Generate ボタンを押すと、Output 欄に xml が出力されますので、それをそのまま レイアウト用の xml ファイルに利用することができます。

ダウンロードして利用できる java アプリもありますので、僕はそちらを使っています。

]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2007/12/android_gui.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2007/12/android_gui.html</guid>
         <category>Android</category>
         <pubDate>Mon, 17 Dec 2007 00:01:01 +0900</pubDate>
      </item>
            <item>
         <title>[Android] オリジナルの ContentProvider を作成する</title>
         <description><![CDATA[<p><a href="/flashmind/blog/2007/11/android_contentprovider.html">前回</a>は ContentProvider クラスを使って コンタクトリスト から電話帳データ を取得してみました。<br />
今回はオリジナルの ContentProvider を作ってみようと思います。<br />

ContentProvider クラスを使い実装することで、他のアプリケーションからもデータを利用することが可能となります。</p>

<p>データへのアクセスは、ContentURI という URI を用い、<br />
<img src="/flashmind/blog/images/content_uri.png"><br />
という形式でアクセスすることになります。<br />
A ：固定の プリフィックス<br />
B ：Authority パート。一意であることを保証する為、ContentProvider のパッケージ名を含んだクラス名とします。<br />
AndroidManifest.xml の <provider> タグの android:authorities 属性と対応します。<br />
C ：ContentProvider が操作するデータの種類を示します。<br />

D ：データのIDを示します。省略すると全てのレコードを取得します。<br />
となっています。</p>

<p>今回は<a href="http://code.google.com/android/intro/tutorial.html" target="_blank">Google が提供しているチュートリアル</a>で作ったNotepadv3 アプリを改造します。<br />
Notepadv3ではデータの保存に SQLite を使っていますが、これを ContentProvider を使うように変更してみましょう。</p>

<h3>Notepadv3 プロジェクトをコピーして Notepadv4 プロジェクトを作成</h3>
既存のものをディレクトリ毎複製したあと "v3" となっている部分を "v4" に書き換えて "create project from existing source" を選択してプロジェクト作成しました。
この時点で、エミュレータ で動作させてちゃんと表示されればOK。

<h3>NotesProvider クラスで操作するデータを表現するクラスを作成</h3>

Provider を作る前に、データを表現する為の NotePad クラスを作成しておきましょう。
Notes は、DB 上では _id,title,body という3つのカラムを持ちます。

<p>com.google.android.demo.provider パッケージを新しく作り、そこに以下のクラスを作ります。<br />
クラス内には、データのパスを示す ContentURI やカラムを示す 定数を指定します。<br />
BaseColumns を継承することにより、_id にあたる部分は省略できます。<br /></p>
<pre class="prettyprint">package com.google.android.demo.provider;

import android.net.ContentURI;
import android.provider.BaseColumns;

public final class NotePad {
	public static final class Notes implements BaseColumns {
        /**
         * このデータへアクセスする為の URI
         */
        public static final ContentURI CONTENT_URI
                = ContentURI.create("content://com.google.android.demo.provider.NotePad/notes");

        /**
         * デフォルトのソート順
         */
        public static final String DEFAULT_SORT_ORDER = "_id DESC";

        /**
         * タイトル
         */
        public static final String TITLE = "title";

        /**
         * ノート
         */
        public static final String BODY = "body";
	}
}
</pre>

<h3>ContentProvider クラスを拡張して、NotesProvider クラスを作成</h3>
これまでデータの保存に利用していた DBHelper の代わりになる、NotesProvider クラスを作ります。

<p>com.google.android.demo.notepad4 パッケージ内に NotesProvider クラスを作りましょう。<br />
下の図のように、ContentProvider クラスを親クラスにします。</p>

<p><img src="extend_contentprovider.png" alt="ContentProvider を継承"></p>

<p>生成された NotesProvider クラスのソースを見てみると、オーバーライドしなくてはいけない関数がいくつかるようです。</p>

<pre class="prettyprint">public int delete(ContentURI uri, String selection, String[] selectionArgs)
public String getType(ContentURI uri) 
public ContentURI insert(ContentURI uri, ContentValues values) 
public boolean onCreate() 
public Cursor query(ContentURI uri, String[] projection, String selection,
			String[] selectionArgs, String groupBy, String having,
			String sortOrder) 
public int update(ContentURI uri, ContentValues values, String selection,
			String[] selectionArgs) 
</pre>

<p>onCreate ：この Provider が生成された時に実行されるメソッドです。<br />
query ：データを取得する為のメソッドです。<br />
getType ：メソッドは、この Provider で取得できる ContentURI の META タイプを返します。<br />
insert,update,delete ：それぞれ データの新規作成時、更新、削除時のメソッドです。<br />

これらの実装は後述します。</p>

<h3>DBへのアクセス方法を変更</h3>
これまで使っていた DBHelper クラスから、必要な処理を移植します。
まずは、インスタンスが生成された時の初期化処理です。
DBHelper クラスでは、
<pre class="prettyprint">public DBHelper(Context ctx) {
    db = ctx.openDatabase(DATABASE_NAME, null);
</pre>
とあるように、コンストラクタの中から DB へアクセスする為のインスタンスが取得できました。
ContentProvider では、ContentProviderDatabaseHelper を拡張したクラスを使ってDB へのアクセスを行います。

<pre class="prettyprint">/**
 * DB アクセス用のHelper クラス
 * @author hal
 */
private static class DatabaseHelper extends ContentProviderDatabaseHelper {
    /**
     * テーブルのCreate文。Helper が最初に生成された際に発行される
     */ 
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table notes (_id integer primary key autoincrement, "
                + "title text not null, body text not null);");
    }
	/**
	 * アップグレード時に実行される。
	 */
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS notes");
        onCreate(db);
    }
}
</pre>
Notepadv3 では、Database が存在していない場合に Create Database 文を発行していましたが、ContentProviderDatabaseHelper では自動でやってくれるので必要ありません。

<h3>残りのメソッドを実装</h3>
query,insert,updateなどを実装します。
流れとしては、ContentURIParser クラスの URL_MATCHER を使って渡されてきた URI をチェックし、onCreate で生成した mDB にクエリを発行しています。
query() で使っている QueryBuilder は、 DB へ発行するクエリを生成する為のクラスです。
getType() メソッドは、この Provider を利用して取得できるデータの METAタイプを返します。
NotesProvider クラス全体は以下のようになります。
<pre class="prettyprint">package com.google.android.demo.notepad4;

import java.util.HashMap;

import com.google.android.demo.provider.NotePad;
import android.content.ContentProvider;
import android.content.ContentProviderDatabaseHelper;
import android.content.ContentURIParser;
import android.content.ContentValues;
import android.content.QueryBuilder;
import android.content.Resources;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.net.ContentURI;
import android.provider.BaseColumns;
import android.text.TextUtils;

public class NotesProvider extends ContentProvider {
	private SQLiteDatabase mDB;
    // URI のパース用
	private static final ContentURIParser URL_MATCHER;
    /**
	 * DB アクセス用のHelper クラス
	 * @author hal
	 */
    private static class DatabaseHelper extends ContentProviderDatabaseHelper {
    	/**
    	 * テーブルのCreate文。Helper が最初に生成された際に発行される
    	 */ 
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("create table notes (_id integer primary key autoincrement, "
                    + "title text not null, body text not null);");
        }
		/**
		 * アップグレード時。
		 */
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("DROP TABLE IF EXISTS notes");
            onCreate(db);
        }
    }

	// DB名
    private static final String DATABASE_NAME = "notepad.db";
	// テーブル名
    private static final String DATABASE_TABLE = "notes";
	// DB バージョン
    private static final int DATABASE_VERSION = 1;

    private static final int NOTES = 1;
    private static final int NOTE_ID = 2;
    private static final HashMap&lt;String, String&gt; NOTES_LIST_PROJECTION_MAP;
    // URL_MATCHER と NOTES_LIST_PROJECTION_MAP を作成
    static {
    	// URI パース用
        URL_MATCHER = new ContentURIParser(ContentURIParser.NO_MATCH);
        URL_MATCHER.addURI("com.google.android.demo.provider.NotePad", "notes", NOTES);
        URL_MATCHER.addURI("com.google.android.demo.provider.NotePad", "notes/#", NOTE_ID);
		
        NOTES_LIST_PROJECTION_MAP = new HashMap&lt;String, String&gt;();
        NOTES_LIST_PROJECTION_MAP.put(BaseColumns._ID, "_id");
        NOTES_LIST_PROJECTION_MAP.put(NotePad.Notes.TITLE, "title");
        NOTES_LIST_PROJECTION_MAP.put(NotePad.Notes.BODY, "body");

    }
	/**
	 * 初期化処理
	 */
    @Override
	public boolean onCreate() {
        DatabaseHelper dbHelper = new DatabaseHelper();
       	mDB = dbHelper.openDatabase(getContext(), DATABASE_NAME, null, DATABASE_VERSION);
        return (mDB == null) ? false : true;    	
	}
	/**
	 * 削除処理
	 */
	@Override
	public int delete(ContentURI uri, String selection, String[] selectionArgs) {
		int count;
        switch (URL_MATCHER.match(uri)) {
        case NOTES:
            count = mDB.delete(DATABASE_TABLE, selection, selectionArgs);
            break;

        case NOTE_ID:
            String segment = uri.getPathSegment(1);
            count = mDB
                    .delete(DATABASE_TABLE, "_id="
                            + segment
                            + (!TextUtils.isEmpty(selection) ? " AND (" + selection
                                    + ')' : ""), selectionArgs);
            break;

        default:
            throw new IllegalArgumentException("Unknown URL " + uri);
        }

        getContext().getContentResolver().notifyChange(uri, null);
		return count;
	}

	/**
	 * この URIが返すデータ型
	 */
	@Override
	public String getType(ContentURI uri) {
        switch (URL_MATCHER.match(uri)) {
        case NOTES:
            return "vnd.android.cursor.dir/vnd.google.note";

        case NOTE_ID:
            return "vnd.android.cursor.item/vnd.google.note";

        default:
            throw new IllegalArgumentException("Unknown URL " + uri);
        }
	}
	@Override
	/**
	 * データの挿入
	 */
	public ContentURI insert(ContentURI uri, ContentValues initialValues) {
	    long rowID;
	    ContentValues values;
		// データがなければ新規に作る
	    if (initialValues != null) {
	        values = new ContentValues(initialValues);
	    } else {
	        values = new ContentValues();
	    }
		// 正しいURI かどうかチェック
	    if (URL_MATCHER.match(uri) != NOTES) {
	    	throw new IllegalArgumentException("Unknown URI " + uri);
	    }

	    Resources r = Resources.getSystem();
		// タイトル設定
	    if (values.containsKey(NotePad.Notes.TITLE) == false) {
	        values.put(NotePad.Notes.TITLE, r.getString(android.R.string.untitled));
	    }
		// 本文設定
	    if (values.containsKey(NotePad.Notes.BODY) == false) {
	        values.put(NotePad.Notes.BODY, "");
	    }
		// 挿入
	    rowID = mDB.insert(DATABASE_TABLE, "note", values);
	    if (rowID &gt; 0) {
            ContentURI newuri = NotePad.Notes.CONTENT_URI.addId(rowID);
            // 変更を通知
            getContext().getContentResolver().notifyChange(uri, null);
            return newuri;
        }

        throw new SQLException("Failed to insert row into " + uri);
    }
	/**
	 * データの取得
	 */
	@Override
	public Cursor query(ContentURI uri, String[] projection, String selection,
			String[] selectionArgs, String groupBy, String having,
			String sortOrder) {

		QueryBuilder qb = new QueryBuilder();

	    switch (URL_MATCHER.match(uri)) {
	    case NOTES:
	        qb.setTables(DATABASE_TABLE);
	        qb.setProjectionMap(NOTES_LIST_PROJECTION_MAP);
	        break;
        case NOTE_ID:
            qb.setTables(DATABASE_TABLE);
            qb.appendWhere("_id=" + uri.getPathSegment(1));
            break;
        default:
            throw new IllegalArgumentException("Unknown URL " + uri);
        }
        // ソート順がなければデフォルトのものを使う
        String orderBy;
        if (TextUtils.isEmpty(sortOrder)) {
            orderBy = NotePad.Notes.DEFAULT_SORT_ORDER;
        } else {
            orderBy = sortOrder;
        }
        // カーソル作成
        Cursor c = qb.query(mDB, projection, selection, selectionArgs, groupBy,
            having, orderBy);
        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c;
	}
	/**
	 * 更新処理
	 */
	@Override
	public int update(ContentURI uri, ContentValues values, String selection,
			String[] selectionArgs) {
		int count;
		switch (URL_MATCHER.match(uri)) {
		// 全て
		case NOTES:
			count = mDB.update(DATABASE_TABLE, values, selection, selectionArgs);
	        break;
		// ID 指定
	    case NOTE_ID:
	        String segment = uri.getPathSegment(1);
	        count = mDB.update(DATABASE_TABLE, values, "_id="
	                            + segment
	                            + (!TextUtils.isEmpty(selection) ? " AND (" + selection
	                                    + ')' : ""), selectionArgs);
	        break;
		default:
	        throw new IllegalArgumentException("Unknown URL " + uri);
	    }
		// 更新を通知
		getContext().getContentResolver().notifyChange(uri, null);
	    return count;
	}

}
</pre>

<h3>Naotepadv4 クラスの修正</h3>
DBHelper ではなく、ContentProvider クラスを利用するように変更します。

<p>データを取得するのに、前回の例と同じように managedQuery メソッド が使えるようになっていると思います。</p>

<p>getIntent().setData(NotePad.Notes.CONTENT_URI);<br />
cur = managedQuery(getIntent().getData(), PROJECTION, null, null);</p>

<p>の部分ですね。<br />

また、編集時に編集画面へデータを渡すには、</p>

<p>ContentURI uri = getIntent().getData().addId({編集したい行の_ID});<br />
startSubActivity(new Intent(Intent.EDIT_ACTION, uri), ACTIVITY_EDIT);</p>

<p>のように、編集したい行の URI を示す ContentURI クラスを渡します。</p>

<p>今回から、startSubActivity へ渡す Intent には、Intent.INSERT_ACTION や Intent.EDIT_ACTION という定数を渡しています。<br />
このように、Intent で ACTION を指定する場合には、AndroidManifest.xml への記述が必要となります。<br />
AndroidManifest.xml については、後述します。<br />
<pre class="prettyprint">package com.google.android.demo.notepad4;

import java.util.ArrayList;

import android.app.ListActivity;
import android.content.Intent;
import android.database.Cursor;
import android.net.ContentURI;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.view.Menu;
import android.view.View;
import android.view.Menu.Item;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.google.android.demo.provider.NotePad;

public class Notepadv4 extends ListActivity
{
    private static final int ACTIVITY_CREATE=0;
    private static final int ACTIVITY_EDIT=1;
    
    private static final int INSERT_ID = Menu.FIRST;
    private static final int DELETE_ID = Menu.FIRST + 1;

    private Cursor cur;
    private ArrayList&lt;Long&gt; mItems ;
    /**
     * DB から取得したい 項目
     */
    private static final String[] PROJECTION = new String[] {
            BaseColumns._ID, NotePad.Notes.TITLE};
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle icicle)
    {
	    super.onCreate(icicle);
        setContentView(R.layout.notes_list);
        fillData();
    }
	// データ表示    
    private void fillData() {
        // クエリ発行
        getIntent().setData(NotePad.Notes.CONTENT_URI);
        cur = managedQuery(getIntent().getData(), PROJECTION, null, null);
		// ID保持用
    	mItems = new ArrayList&lt;Long&gt;();
    	// タイトル保持用
    	ArrayList&lt;String&gt; items = new ArrayList&lt;String&gt;(); 
		int titleIndex = cur.getColumnIndex(NotePad.Notes.TITLE);
		// データ取得
		while (cur.next()){
        	mItems.add(cur.getLong(cur.getColumnIndex(NotePad.Notes._ID)));
        	items.add(cur.getString(titleIndex));
		}
                
        // リスト作成
        ArrayAdapter&lt;String&gt; notes = 
            new ArrayAdapter&lt;String&gt;(this, R.layout.notes_row, items);
        setListAdapter(notes);
    }
    /**
     * オプションメニュー生成
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        menu.add(0, INSERT_ID, R.string.menu_insert);
        menu.add(0, DELETE_ID, R.string.menu_delete);
        return true;
    }
	/**
	 * メニュー選択時
	 */
    @Override
    public boolean onMenuItemSelected(int featureId, Item item) {
        super.onMenuItemSelected(featureId, item);
        switch(item.getId()) {
        case INSERT_ID:
            Intent i = new Intent(Intent.INSERT_ACTION, getIntent().getData());
            startSubActivity(i, ACTIVITY_CREATE);
            break;
        case DELETE_ID:
        	// データ削除
    		cur.moveTo(getSelection());
    		cur.deleteRow();
            fillData();
            break;
        }
        return true;
    }

	/**
	 * リストアイテムクリック
	 */
    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);
        
		ContentURI uri = getIntent().getData().addId(mItems.get((int)getSelectionRowID()));
        
        startSubActivity(new Intent(Intent.EDIT_ACTION, uri), ACTIVITY_EDIT);
    }
	/**
	 * SubActivity 終了時
	 */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, String data, Bundle extras) {
        super.onActivityResult(requestCode, resultCode, data, extras);
        
		fillData();
    }
}
</pre>

<h3>NoteEdit.java の変更</h3>
NoteEdit.java 側も変更を加えます。
<pre class="prettyprint">package com.google.android.demo.notepad4;

import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.net.ContentURI;
import android.os.Bundle;
import android.provider.BaseColumns;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import com.google.android.demo.provider.NotePad;

public class NoteEdit extends Activity {
    private EditText titleText;
    private EditText bodyText;
    private Cursor cur ;
    
    private ContentURI mURI; 
    
    private static final String[] PROJECTION = new String[] {
        BaseColumns._ID, NotePad.Notes.TITLE, NotePad.Notes.BODY};

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.note_edit);
        
        titleText = (EditText) findViewById(R.id.title);
        bodyText = (EditText) findViewById(R.id.body);
        
        Button confirmButton = (Button) findViewById(R.id.confirm);

        final Intent intent = getIntent();

        final String action = intent.getAction();
        if (action.equals(Intent.INSERT_ACTION)) {
            // ContentResolver にURI を渡し、新規のカーソルを作成する
            mURI = getContentResolver().insert(intent.getData(), null);

            // エラー
            if (mURI == null) {
                Log.e("Notes", "Failed to insert new note into "
                        + getIntent().getData());
                finish();
                return;
            }

            // キャンセルボタンなどを実装したい場合、setResult には RESULT_CANCEL を渡すこと
            setResult(RESULT_OK, mURI.toString());
        }else if (action.equals(Intent.EDIT_ACTION)) {
            // 編集時
            mURI = intent.getData();
            setResult(RESULT_OK, mURI.toString());        	
        }
        // クエリを発行
        cur = managedQuery(mURI, PROJECTION, null, null);
        // データ表示
        populateField();
        // Submit ボタンクリック時のイベント
        confirmButton.setOnClickListener(new View.OnClickListener() {

            public void onClick(View arg0) {
                setResult(RESULT_OK);
                finish();
            }
            
        });
    }
    /**
     * アクティビティの終了、一時停止時
     */
    @Override
    protected void onPause() {
    	// 使っているリソースを開放する
    	super.onPause();
    	saveState();
    }
    /**
     * Pause 状態からの復旧時
     */
    @Override
    protected void onResume() {
    	// データを復旧
    	super.onResume();
    	populateField();
    }
    /**
     * データ保存用のメソッド
     */
    private void saveState(){
    	String title = titleText.getText().toString();
    	String body = bodyText.getText().toString();
        // cur.updateString で、データを更新		
        if (cur != null) {
            cur.updateString(cur.getColumnIndex(NotePad.Notes.TITLE), title);
            cur.updateString(cur.getColumnIndex(NotePad.Notes.BODY), body);
            managedCommitUpdates(cur);
        }
    }
    /**
     * 編集用のフィールド表示
     */
	private void populateField() {
        if (cur != null) {
            cur.first();
            String title = cur.getString(cur.getColumnIndex(NotePad.Notes.TITLE));
            titleText.setText(title);
            String body = cur.getString(cur.getColumnIndex(NotePad.Notes.BODY));
            bodyText.setText(body);
        }
    }
}
</pre>

<h3>AndroidManifest.xml の更新</h3>

最後に、IntentFilter や provider の設定を、AndroidManifest.xml に記述します。
<pre class="prettyprint">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.google.android.demo.notepad4"&gt;

    &lt;application android:icon="@drawable/icon"
    			 android:theme="@android:style/Theme.Dark"&gt;
	    &lt;provider class="NotesProvider" android:authorities="com.google.android.demo.provider.NotePad" /&gt;
        &lt;activity class=".Notepadv4" android:label="@string/app_name"&gt;
            &lt;intent-filter&gt;
                &lt;action android:value="android.intent.action.MAIN" /&gt;
                &lt;action android:value="android.intent.action.INSERT" /&gt;
                &lt;action android:value="android.intent.action.EDIT" /&gt;
                &lt;category android:value="android.intent.category.LAUNCHER" /&gt;
                &lt;type android:value="vnd.android.cursor.dir/vnd.google.note" /&gt;
            &lt;/intent-filter&gt;
        &lt;/activity&gt;
        &lt;activity class=".NoteEdit" android:label="@string/edit_note"&gt;
            &lt;intent-filter&gt;
                &lt;category android:value="android.intent.category.DEFAULT" /&gt;
                &lt;type android:value="vnd.android.cursor.dir/vnd.google.note" /&gt;
                &lt;type android:value="vnd.android.cursor.item/vnd.google.note" /&gt;
            &lt;/intent-filter&gt;
        
        &lt;/activity&gt;
    &lt;/application&gt;
&lt;/manifest&gt; 
</pre>

<p>&lt;provider class="NotesProvider" android:authorities="com.google.android.demo.provider.NotePad" /&gt;<br />
の部分が、今回作った ContentProvider を使う為の記述です。authorities 属性の value 値が、ContentURI に対応しているのがわかると思います。<br />
&lt;intent-filter&gt;内の<br />
&lt;action android:value="android.intent.action.INSERT" /&gt;<br />
&lt;action android:value="android.intent.action.EDIT" /&gt;<br />
は、Intent.EDIT_ACTION などを 別の Activity に渡す際に必要な記述になります。<br />
&lt;type android:value="vnd.android.cursor.dir/vnd.google.note" /&gt;<br />

部分は、Provider の getType() に対応しています。</p>

<p>以上の変更を加えることで、オリジナルの ContentProvider を利用できるようになっています。<br />
RUN してみると、これまでと同じように NotePad アプリケーションが動作すると思います。</p>

<p>今回はソースをまるまる貼り付けているので、かなりエントリが長くなってしまいました。<br />
</p>]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2007/11/android_contentprovider_1.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2007/11/android_contentprovider_1.html</guid>
         <category>Android</category>
         <pubDate>Tue, 27 Nov 2007 00:16:14 +0900</pubDate>
      </item>
            <item>
         <title>[Android] ContentProvider を使って コンタクトリストのデータを取得する</title>
         <description><![CDATA[<p>Android の ContentProvider クラス について。</p>

<p><a href="http://code.google.com/android/intro/tutorial.html" target="_blank">チュートリアル</a>にある NotePad アプリケーションではデータの保存に SQLite を使っていますが、</p>

<blockquote>For this exercise, we are just going to use a SQLite database directly to store our data, but in a real application it would be much better to write a proper ContentProvider to encapsulate this behavior instead.

<p>If you are interested, you can find out more about content providers or the whole subject of Storing, Retrieving, and Exposing Data.</blockquote><br />
とあるように、実際アプリケーションを作る場合は ContentProvider を使う方がいいようです。</p>

<p>というわけで、以下のURL を参考に ContentProvider を使ってみました。<br />
<a href="http://code.google.com/android/devel/data/contentproviders.html" target="_blank">http://code.google.com/android/devel/data/contentproviders.html</a></p>

<p>まずは、サンプルコードにあるように、アドレス帳のデータを取り出してリスト形式で表示してみることにします。</p>

<h3>1.  新しい Android プロジェクトを作る。</h3>
<p>プロジェクト名、アプリケーション名は ContactView にしました。</p>

<h3>2. layout 用の xml ファイルを作成。</h3>
<p>
main.xml に ListView を追加<br />
main.xml<br />
<pre class="prettyprint">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"

    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    &gt;
    &lt;ListView id="@+id/android:list"
          android:layout_width="wrap_content"
        	android:layout_height="wrap_content"/&gt;

  	&lt;TextView id="@+id/android:empty"
          android:layout_width="wrap_content"
        	android:layout_height="wrap_content"
        	android:text="No Contacts!"/&gt;
&lt;/LinearLayout&gt;
</pre></p>

リスト内の表示用に、contacts_row.xml を追加

<pre class="prettyprint">&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;TextView id="@+id/text1" xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"/&gt;
</pre>

<h3>3. コンタクトリストからデータを表示するプログラムを書きます。</h3>
<pre class="prettyprint">package jp.plantsweb.android.contact;

import java.util.ArrayList;

import java.util.List;

import android.app.ListActivity;
import android.database.Cursor;
import android.os.Bundle;
import android.widget.ArrayAdapter;

public class ContactList extends ListActivity {
    /** Called when the activity is first created. */

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.main);
        fillData();
    }

    /**
     * コンタクトデータを取得して表示
     */
    private void fillData(){
        List<String> items = new ArrayList<String>();

	     // コンタクトリストから取得するカラムを指定
	     String[] projection = new String[] {
   		    android.provider.BaseColumns._ID,
       		android.provider.Contacts.PeopleColumns.NAME
    	};
	     

	    // クエリを発行する
    	Cursor cur = managedQuery( android.provider.Contacts.Phones.CONTENT_URI,
                 projection, // 取得するカラムを指定
               	 null,             // WHERE句にあたる部分。全部必要なのでnull
                 android.provider.Contacts.PeopleColumns.NAME + " ASC"); // Order by

       	String name;

       	// データ取得用のインデックスを取得する
      	int nameColumn = cur.getColumnIndex(android.provider.Contacts.PeopleColumns.NAME); 
       	// データ取得開始
		while (cur.next()) {
  	        	// データ取得
      	    	name = cur.getString(nameColumn);

	      	    // 表示用の配列に追加
				items.add(name);
		}
     	// アダプタを介してデータ表示
       	ArrayAdapter<String> contacts = new ArrayAdapter<String>(

       		this, R.layout.contacts_row, items);
       	setListAdapter(contacts);
    }
}
</pre>

<p>Run してみたところ、SecurityException が発生しました。</p>
<img src="/flashmind/blog/images/error.png" alt="error">

<p>デフォルトではコンタクトリストには許可されたアプリケーションしかアクセスできない模様です。</p>

<h3>4. パーミッションの設定をする</h3>
<p>セキュリティの章に答えがありました。
<a href="http://code.google.com/android/devel/security.html" target="_blank">http://code.google.com/android/devel/security.html</a></p>

<p>保護されているデータにアクセスする場合、AndroidManifest.xml に、<br />
    &lt;uses-permission id="android.permission.RECEIVE_SMS" /&gt;<br />
というような宣言を書く必要があるようです。</p>

<p>パーミッションの一覧が以下の URL にあり、<br />
<a href="http://code.google.com/android/reference/android/Manifest.permission.html" target="_blank">http://code.google.com/android/reference/android/Manifest.permission.html</a><br />

READ_CONTACT というパーミッションがありました。AndoroidManifest.xml に<br />
<pre class="prettyprint">&lt;uses-permission id="android.permission.READ_CONTACTS" /&gt; </pre>
<br />
という行を追加して RUN してみたところ、無事コンタクトリストの内容が表示されました!</p>
<br />
<img src="/flashmind/blog/images/ad_list.png" alt="list of address">

<p>しかし、コンタクトリストには2名しか登録していないはずですが、4行になっていますね。調べてみたところ、ContactList 内のデータは、携帯と自宅と登録されている場合は2行分に分かれているようです。</p>

<p>次回はオリジナルの ContentProvider を作ってみようと思います。<br />
</p>


]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2007/11/android_contentprovider.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2007/11/android_contentprovider.html</guid>
         <category>Android</category>
         <pubDate>Fri, 23 Nov 2007 21:34:29 +0900</pubDate>
      </item>
            <item>
         <title>trac から pukiwiki 形式に変換する、trac2pukiwiki.sh </title>
         <description><![CDATA[trac を pukiwiki 形式に変換するスクリプトを書いてみた。<br>
<br>
/Users/hal/bin/trac2pukiwiki.rb
<pre class="prettyprint">#!/opt/local/bin/ruby
#####################################################
# trac で書かれたコードを pukiwiki 記法に変更します。
#####################################################

require 'nkf'

while gets
  NKF.nkf('-w -S',$_).split("¥r").each() { |l|
    print NKF.nkf('-W -s', l.gsub(/^(¥s*)(¥*+)([¥w¥W]+)/) { |m|
      m = ' ' * $1.length + '-' * ($2.length) + ' ' + $3  # * を - に変換
    }.gsub(/(=+)¥s+([¥w¥W]+)¥s+(=+)/){|m|
      m = '*' * ($1.length) + " " + $2                    # == 見出し == を ** 見出し に変換
    }.gsub(/¥{¥{¥{/,'#code(){{').                         # {{{ を {{ に変換
      gsub(/¥}¥}¥}/,'}}').                                # }}} を }} に変換
      gsub(/¥|¥|/,'|').                                   # || を | に変換
      gsub(/¥[¥[br¥]¥]/, '‾').                            # [[BR]] を ‾ に変換
      gsub(/¥[¥[PageOutline¥]¥]/, '#contents').           # [[PageOutline]] を #contents に変換
      gsub(/¥[wiki:([^¥s]*)¥s([¥w¥W]*)¥]/, '[[¥2>¥1]]')) + "¥n"  # [wiki:リンク] を [[リンク]] に変換
  }
end
</pre>
<br>
このスクリプトの標準入力に trac記法の ソース を食わせると、pukiwiki 形式の ソース が標準出力に出力されます。<br>
<br>
NKF.nkf('-w -S',$_) や NKF.nkf('-W -s',... としているのは、osx のクリップボードがなぜか SJIS だから。<br>
そしてなぜ osx のクリップボードが必要かというと、以下の シェル で、クリップボードから直接変換をするようにしているからです。<br>
<br>
<pre class="prettyprint">#!/bin/bash
pbpaste | /Users/hal/bin/trac2pukiwiki.rb |  pbcopy 
</pre>
<br>
pbpaste は osx 固有のコマンドで、クリップボードの中身を取得するコマンドです。そして、pbcopy は、標準入力から入力されたものを クリップボード へコピーします。<br>
<br>
というわけで、このシェルを QuickSilver に登録しておき、tracの編集画面でソースをコピー。<br>
その後QuickSilver から シェル を実行した後 pukiwiki の編集画面へペーストすると、無事移行完了です。<br>
<br>
実は、上記スクリプトは若干手抜きで、インデントを付けた記述が、ちゃんと変換されなかったりします。<br>
代表的なタグ(?)しか変換してないし、あくまで補助ツールということで。<br>
]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2007/11/trac_pukiwiki_trac2pukiwikish.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2007/11/trac_pukiwiki_trac2pukiwikish.html</guid>
         <category>開発</category>
         <pubDate>Thu, 22 Nov 2007 20:49:55 +0900</pubDate>
      </item>
            <item>
         <title>「転職gungi」で喋ってきました。</title>
         <description><![CDATA[さてさて、いつもやってる gungi の転職イベント版、「<a href="http://open.gungi.jp/modules/eguide/event.php?eid=7" target="_blank">転職軍議・Webベンチャーで働く6つの理由</a>」で喋ってきました。
誤解なきように最初に言っておきますが、主催者側ですからね。

主催の一人、上原さんも<a href="http://ceonews.jp/archives/2007/11/post_276.html" target="_blank">自身のブログ</a>で
<blockquote>やり終えて感じていることは、「これはやり続けよう」ということ。今日は私は司会兼タイムキーパーをしながらずっとセミナーを見続けたあとで、ブースで参加者の方々とウェブベンチャーってどうよなお話をしていたりしたのですが、なんと言うか、エンジニアでない自分でもなんだか「ウェブベンチャーでエンジニアするのっていいキャリアだ」というようなことを感じられる時間と空気がそこにありました。それが結構信じれる何かだった感覚があるので、「これはやり続けよう」と。
</blockquote>
と書いているように、良いイベントだったと思います。
6社それぞれの代表者がベンチャーで働く良さについて語ったわけですが、他の会社のプレゼンを見ながら「うんうん、そうだよなぁ」といったような、不思議な一体感を感じさせる場がありました。
なんというか、20後半から30前半の大事な時期にWEBベンチャーを選んだ自分の選択は間違っていなかったなとも思いましたし。
来てくれた人たちにも、WEBベンチャーで働く楽しさというものが少しは伝わってくれていたら嬉しいなと思います。

ちなみに、僕は「自由」というテーマでプレゼンを行ないました。
その資料を作る際に開発チームのみんなにアンケートを取ってみたところ、
これまでいろいろと試行錯誤してきた、自由研究制度やプロジェクト進行方法などについての評価が高かったことがうれしかった。

飲み会でも、各社の人事制度の話などで盛り上がり、非常に得るものがありました。
次回が楽しみです。
]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2007/11/gungi_1.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2007/11/gungi_1.html</guid>
         <category></category>
         <pubDate>Sat, 17 Nov 2007 19:47:17 +0900</pubDate>
      </item>
            <item>
         <title>gungi 第6回開催されます。</title>
         <description><![CDATA[マイネットジャパンさんと共同で運営しているエンジニア向けオープン勉強会、gungi の第6回の募集が始まっています。

http://open.gungi.jp/modules/eguide/event.php?eid=6

<blockquote>今回の勉強会テーマは「キーワード検索のむこう側」です。

近年Googleに代表されるキーワード検索は、今やエンジニアにとっても必要不可欠のツールとなりました。しかし、本当に欲しいデータを取得できているかと言えば、まだまだ満足できないという方も多いと思います。
今回はそんなキーワード検索の「むこう側」を模索しながら、思わず検索したくなるようなサービスを開発している現場のエンジニアをお呼びしました！
</blockquote>

毎回楽しい勉強会ですが、今回も楽しみです!
技術者の方は是非ご参加ください。]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2007/10/gungi_6.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2007/10/gungi_6.html</guid>
         <category></category>
         <pubDate>Mon, 29 Oct 2007 13:36:27 +0900</pubDate>
      </item>
            <item>
         <title>QuickSilver + twitter script で、twitter 経由で Remember The Milk へタスク登録</title>
         <description><![CDATA[先日のエントリ、<a href="http://www.plants-web.jp/flashmind/blog/2007/10/ipod_touch_remember_the_milk_t.html">iPod touch + Remember The Milk + Twitter + QuickSilver + cybozu2ical で 、ついに統合タスク管理環境が完成</a>
で、続きを書くと宣言しているので、続きを書きます。

前回のエントリでは、タスクの一元化に成功しました。
残る問題は登録の面倒さで、いちいちrmilk.comを開いて登録しなくてはいけないわけです。
タスクの管理だけにブラウザを上げておくのはいやですね。どうせなら、Twitter 経由で登録できるようにしちゃいましょう。

■ Twitter から RTM を使えるようにする
1. Twitter で RTM ユーザを追加
<a href="http://blog.rememberthemilk.jp/2007/07/15/30/" target="_blank">公式ブログ</a>でも解説されていますが、Twitter のアカウントを持っていれば、簡単です。
公式ブログの説明が簡潔なので、以下の通りに作業をします。
<blockquote>
まだ Twitter を使ったことがない場合は、Twitter の<a href="http://twitter.com/signup">アカウントを取得</a>する必要があります。その後、<a href="http://twitter.com/rtm">rtm</a> をあなたのTwitterの友人として加えてください。（<a href="http://twitter.com/rtm">rtm のプロフィールページ</a>の ‘add’ リンクをクリックするか、’follow rtm’ を送信することで友人として追加できます。） 次に、<a href="http://www.rememberthemilk.com/services/twitter/">Remember The Milk for Twitter</a> のページの右上にある ‘Twitter setup’ と書かれた設定BOXで、あなたの Twitter ユーザ名を入力して ’submit’ ボタンを押してください。（Remember The Milk にログインしていない場合は先にログインしてください。） そうすると、認証用のパスワードが表示されますので、これを Twitter の <a href="http://twitter.com/rtm">rtm</a> にダイレクトメッセージで送信してください。（’d rtm [認証用パスワード]’ ）これで準備は完了です。</blockquote>

2. d rtm [メッセージ]でタスクを登録
さっそく、Twritterから "d rtm Twriterからタスクを登録できるようにする" と送信してみましょう。
remeber the milk のページにタスクが登録されていればOKです。
頭に "d" がつく投稿は、特定ユーザへのダイレクトメッセージとなり、他のユーザには表示されませんので、安心してください。
タスクの完了や延期などもTwitter経由でできますので、公式サイトを確認してみてください。

■ QuickSilver からタスクを登録できるようにする
Twitterific などのTwitterクライアントを使えば比較的簡単にタスクを登録することができます。
しかし、ここはもう一歩進めて、QuickSilver 経由でタスクを登録できるようにしましょう。
僕の場合は、" Command + option + t " キーを割り当てているので、"Command + option + t" → "d rtm ブログを書く at 10/18 12:00 " → Enter 等のアクションでタスクを登録することができます。

<a href="http://purple-skys.blogspot.com/2007/10/twitter-via-quicksilver.html" target="_blank">Twitter via QuickSilver </a>
のサイトを参考に設定しましょう。
<blockquote>追記
QuickSilver は日本語が使えないので、日本語を post したい場合は、post 内容を QuickSilver にコピペしないと駄目なようです。
</blockquote>
とありますが、僕の場合複数の日本語入力を切り替えることで日本語入力ができるようになっています。
(書き込むときに、AquaSKK と ことえり を切り換える)

また、タスクの完了については、<a href="http://www.people.cornell.edu/pages/yi38/rtm/Remember%20the%20Milk%20Widget.html" target="_blank">Remember The Milk Widget</a>を使えば簡単に消すことができるのでおすすめです。

以上です。今のところはこの方法で落ち着いています。
ちょっと面倒な手続が多かったですが、一度構築してしまえば、タスク登録時の気持ち良さは相当です。
他にも、タスク/スケジュール管理で工夫されている人がいたらぜひご意見ください!!
]]></description>
         <link>http://www.plants-web.jp/flashmind/blog/2007/10/quicksilver_twitter_script_twi.html?from=rss</link>
         <guid>http://www.plants-web.jp/flashmind/blog/2007/10/quicksilver_twitter_script_twi.html</guid>
         <category></category>
         <pubDate>Thu, 18 Oct 2007 11:58:05 +0900</pubDate>
      </item>
      
   </channel>
</rss>
