2011年12月30日金曜日

今年はいろいろあった

日本に住んでいる人のすべてがそうであったと思うが、自分も今年はいろいろあった。
今後の人生に大きく影響するだろう出来事が7〜8はあったと思う。

地震はあるし、そんとき海外にいたし、会社はリストラ始まるし、転職先でも粉飾が起こるし、1年で2回転職するし。

思い返せば毎年、何かしらその後の人生に影響するだろうことが起こっている訳だが、今年ほど様々なことが起こるのは珍しいだろう。


地震があり原発に対する考え方が揺らいだ。ただ、政治やテレビ番組や新聞などが、それらに対して驚くほど鈍感であった。もっと大きな変化があってもよさそうなものであるが、ほとんど何も変わっていない。今までも別に変わらなかったのだとは思うが、自分がいる業界や生活の変化と比べて、あまりにも変わらなすぎて今まで以上に遠い存在になってしまったように思える。


Androidが興味の中心だった。twitterとかbookmarkとかを1年分さらっと見返してみたが、自分にとってはAndroidが関心の中心となった一年だったようだ。今も仕事で関わっているので、たぶん来年もだろう。端末のスペックの進化は、もう少し続くかもしれないけど来年くらいからモバイルのみのソフトウェアの競争が始まるのであろうか?
個人的にはWindowsPhone7が年末にヒットして来年に普及し始めるだろうくらいに思っていたのだが、日本のみならず海外でも鳴かず飛ばずのようで非常に残念だった。

情報の取得の方法も去年までとは様変わりしたように思う。
twitterはもう少し前からやっていたけど、facebookも始めて、知り合いが調べたことや興味のあることがTL上に並んで、RSSやGoogleAlertで情報を取得していた頃よりも、情報の幅とか深さが増して、尚かつ情報にたどり着くまでの時間が減ったように思える。IT業界以外の人たちも同じような体験をし始めているだろうか?だとしたらとても面白い。

来年は自分に何が起こって、世の中がどう変わるのか、元々予想など出来ないけれども、予想不可能であるということを実感したのは今年が始めてであった気がする。

2011年12月11日日曜日

WebViewからJavaScriptをコールしたり



AndroidとかiPhoneのアプリ開発はWebViewを使ったWebアプリが増えていく感じがあるが、
WebViewだけだと読み込みが遅いとかレスポンスが悪いとかまだまだNativeで作った方がユーザとしてはストレスが少ないものが作れるような気がする。
HTML5でアプリがWebアプリになったとしてもなかなかレスポンスの部分は解決が難しいのではないかな?というのが個人的な印象。
そんなときにはNativeとWebViewの組み合わせでアプリを作ろうとする場合があると思う。
両者を橋渡しすることになるのはJavaScriptになりそうだけど、それぞれのプラットフォームでコールの仕方とか違うみたいなので少し調べてみた。

AndroidとiPhoneを比べると
NativeからJSを呼び出すのはiPhoneのが整っていて
JSからNativeを呼び出すのはAndroidの方が整っている印象。


Androidの場合は、
・NativeからJSの呼び出し
mWeb.loadUrl("javascript:コールしたいJSの関数");
のようにWebViewのインスタンスからmethodをコールして
JSを呼び出すような感じになっている。
ただ、この関数戻り値がvoidのなので結果が分からない。。。
結果を知ろうと思ったら、関数コールの引数にcallbackを指定するような文字列を
入れるなどして結果を取得しなければならない。サンプルではconsole.logをフックして結果を表示しているけど、ほめられた方法ではない。


・JSからNativeの呼び出し
mWeb.addJavascriptInterface(new NativeClass(), "Android");
のようにするとNativeClassが"Android"がJS内における名前空間にように扱えるので
Android.callNative("arg");
のようにJS側からコールスことが出来る。callNativeはJavaのNativeClassのmethodになっている。
この関数は戻り値をとることが出来るので同期でコールすることが出来そうだ。


iPhoneの場合は、
・NativeからJSの呼び出し
[_webView stringByEvaluatingJavaScriptFromString:@"IPHONE.CallJSfromNative('hoge')"];
のように、WebViewのメソッドをコールスことでJSを呼び出すことが出来る。
ここでは、IPHONE.CallJSFromNativeがJS側で宣言されている関数になる。
stringByEvaluatingJavaScriptFromStringは戻り値があるので同期でコールすることが出来そうだ。


・JSからNativeの呼び出し
iPhoneの場合はJSからNativeをコールする方法はきちんと用意されていないようで、少し調べた限りでは、いろんな人がそれぞれ工夫しながら実現しているような印象だ。
具体的には独自のスキームを利用してコンテンツをJSからロードしたときにshouldStartLoadWithRequestがコールされるので、独自のスキームをフックして、処理を行うような感じにするのが一般的のようだ。
- (BOOL) webView:(UIWebView *)webView
                shouldStartLoadWithRequest:(NSURLRequest *)request
                navigationType:(UIWebViewNavigationType)navigationType {
    
    NSString *requestString = [[request URL] absoluteString];
    if (![requestString hasPrefix:@"callobjc:"]) return YES;
    
    NSArray *components = [requestString componentsSeparatedByString:@":"];
    NSString *function = [components objectAtIndex:1];
    [self performSelector:NSSelectorFromString(function)];   
    return NO;
}
上のような感じで、関数名を取り出した後にself performSelector:NSSelectorFromString()で内部に宣言した関数をコールしている。
サンプルをここにおいておいた。
WebがわはDjandoで作っているが、アプリではなく静的なファイルを戻すだけなので、
必要なのは、それぞれのディレクトリ内にあるindex.htmlとsite_templeteの中にあるJSのファイルだけだ。AndroidについてはeclipseにPydevさえ入れてあればwebサーバとかたてなくても試せるはず。


少し調べてみた感じだと、すべてのフレームワークに共通のIFを用意するのはなかなか難しそうで、用意するとしたら全部非同期になりそうだ。なぜなら戻り値がなかったりするから。
https://github.com/callback/callback-ios.git
にPhoneGapのソースがあって、これは結構面白かった。
JS側からNativeをコールするときにはPhoneGap.exec()を利用する。
内部では、gap:のスキームをloadするとWebViewのコールバックが呼ばれて、その中でJS側でキューイングされている文字列を取得して、Native側の関数をキューイングされている分だけすべて関数をコールする感じになっている。
独自にJSのNativeの連携部分を作るよりもこういったコードを流用する方がよいかもしれない。
./PhoneGapLib/javascripts/core/phonegap.js.base
は拡張子が微妙だけどJS部分だろうと思う。
./PhoneGapLib/Classes/PhoneGapDelegate.m
の中にJSからのコールバック部分があったりした。


実際にWebViewとNativeの組み合わせのアプリ作ってみて感じたけど、やっぱりWebだと連打はつらいなー。というのが現状のようだ。
それと思いのほかWebViewとNativeの組み合わせでやり取りしても、画面表示のレスポンスは結構よかった。


2011年10月4日火曜日

ichannel風のどの画面でもテロップ



いつでもどの画面でもテロップが表示され続けるアプリを作った。
Yahoo!の国内ニュースを取得してテロップで流し続けて
ドラッグで移動できるので邪魔にならない場所に移動できるが、消す方法が無いので我ながらかなりうざい。。。

コード整理したらマーケットに上げよーっと。

2011年9月30日金曜日

freeのPDF Viewer


Androidで使えるfreeでソースが手に入るPDF Viewerが欲しかったので探したがあまりいいのがなかった。
でもapvはい感じだった。
ただ、日本語の表示が出来なかったのでIPAフォントを入れて日本語表示できるようにした。
ソースは以下においてある。
https://github.com/matsuhiro/APV-Custom

コードを取得したら、
$ cd pdfview/scripts
$ ./build-native.sh
を実行するとnativeコードのライブラリができるので、
eclipseでプロジェクトをインポートしてあげれば動くはず。

拡大縮小の時に画面がチラつくが、表示の速さは実用レベルだw

これを自分用にカスタマイズしていこーっと。

写真は京都の鴨川です。3月のライオンにも出てきたところです。いいトコだった。マタ行きたい。

2011年9月12日月曜日

WindowsPhoneとAndroidのゲーム




WindowsPhoneゲームプログラミングの本を読んでみた。
発売前の本だけど、MSの人がブログ上でレビューアを募集していたので
メールしてみたらやらせてくれた。
9月末に発売する予定のようですが、内容はC#とか知らない自分でも十分に読める内容だったし、
量も程良いのでゲームプログラム以外でもWindowsPhoneアプリの入門には丁度よいかも。
WindowsPhoneの本自体は、ebookで公開されている。けどねぇ。。。
900ページ超の大作、しかも英語なので、読むとしてもこちらは検索してリファレンスとして使うくらいでしょうかね(^^ゞ


WindowsPhone自体はXNAとSilverlightの組み合わせで書くわけだけど、
Androidのアプリとか他のモバイルアプリを作ったことがある人はSilverlightで作るアプリは
とっつきやすいと思う。
XNAについては「ゲームってこう作るもんなのね。」という感想。
Silverlightは、フツーにXAML(ザムルとよむらしい。まあXMLですよね)でUIを書いて
UI部品のコールバックを書いてイベントドリブンで処理が進んでいく感じです。
Androidのアプリ開発と感覚が近いです。
でも、XNAの方は、初期化→コンテンツロード→状態更新→描画→状態更新→描画・・・
みたいに、状態更新と描画が繰り返し呼ばれる。しかも1/30秒ごとに。
じゃあイベントの処理(タッチとかジェスチャーとか)をどう処理するのか?というと
状態更新の中で、XNAのフレームワークの中にあるTouchPanelというクラスから
ReadGesture()のようなメソッドをコールしてイベントがあったら処理をする。
といった感じの処理でイベントキューから自分でイベントを取得して処理する感じだ。
フツーはアプリのフレームワーク側でやっているようなイベントキューからの
イベント取得処理をアプリ側でやる感じだと思う。


Androidでゲーム書くときは?
と思ったけどOpenGLでゲーム作ると少しだけど似た感じになるかな?
GLSurfaceView.RendererをimplするとonDrawFrameが繰り返し呼ばれて
その中で描画処理を行う。
イベント処理はというとGLSurfaceViewをオーバーライドして、onTouchEventの中でイベント処理をする。
なので、Androidは、描画は勝手に呼び出されるコールバックで行うけど
イベント処理はイベントドリブンで。
といった感じになっている。
onDrawFrame()の呼び出しはどこで行われているのかというと
frameworks/base/opengl/java/android/opengl/GLSurfaceView.java
の中。
GLをAndroidで使おうとするとGL専用のスレッドが勝手に作られるけど、
その中のguardedRun()でonDrawFrameがコールされている。
で、ソース見てみたらwhile(true)で延々コールしていた。
AndroidでJavaからGLを使うと勝手に繰り返しの描画が行われるみたいで
気おつけるべきだなーっと感じた。まあ、そうしないためのインテーフェースは用意されていたけど。


WindowsPhoneもAndroidもゲームを作るための仕組みはあるけど、
イベント処理はAndroidの方が楽そう。
でも描画や状態の更新が1/30secごとに呼び出されるというのは
ゲームを作る上では結構大事かも。(処理が重かったりすると描画がすっ飛ばされてコマ落ちみたいになることもあるみたいだけど。)
Androidはwhile(true)で処理しているから描画タイミングのすべてを把握できないのかも。
まあ、ソースをパッと見ただけなので、どこかのmethod内にタイミングを図るための仕組みが入っているかもです。

ただ、ゲームでないアプリを作るのであればWindowsPhoneのがよいかも。
ExpressionBlendがすごいので、いい感じのUIを作りやすいのはWindowsPhoneだと思う。

なんかモヤモヤした文章になってしまった。
やっぱりコード書いて確認しなきゃはっきりわからんね。
DevQuizも時間切れだし、こんど時間つくろ。

2011年9月4日日曜日

localにgitのserverを作ってみる。

githubでソース管理してもいいんだけど、タダアカウントだと全部公開されてしまうので
自分のPCにgitのserverたてようかと調べた。
思いの外はまったし、Web上の情報も足りないとこがあったりして
イイ感じにまとまってなかったので、自分でまとめる

git-coreとgit-daemon-runをapt-getして以下を実行する

$ sudo emacs /etc/sv/git-daemon/run

中身を以下のような感じで編集するとgitの公開用のフォルダが/home/user/gitになる
- exec chpst -ugitdaemon \
- "$(git --exec-path)"/git-daemon --verbose --base-path=/var/cache /var/cache/git
+ #exec chpst -ugitdaemon \
+ # "$(git --exec-path)"/git-daemon --verbose --base-path=/var/cache /var/cache/git
+ exec chpst -ugitdaemon "$(git --exec-path)"/git-daemon --verbose --base-path=/home/user/git --export-all --enable=receive-pack


デーモンの再起動をしする
$ sudo sv restart git-daemon
レポジトリを作る
$ sudo git clone --bare /home/user/git/test
ownerを変える。自分はこれではまってしまった。。。
$ sudo chown -R gitdaemon .

適当なディレクトリで
$ mkdir test
$ cd test
$ git init
$ git remote add origin git://localhost/test.git
$ echo "This is README" > README
$ git add README
$ git commit -m "initial commit"
$ git push origin master

$ git branch hoge
$ git checkout hoge
READMEを適当に変更して
$ git add README
$ git commit -m "hoge commit"
$ git push origin hoge
$ git branch -r
origin/hoge
origin/master
こんなかんじで、localにgitのサーバを作って、そんなかでソースの管理ができる。
まあ、ひとつのPCで完結するならサーバにする必要も無いんだけど
PC複数ある時とかはいいかも。
蛇足として、写真は上高地の川の中の画像です。
雨が降ってましたが川はとても透明でした。
youtubeに動画上げてみました。岩魚がおよいでます。

2011年6月29日水曜日

StatusBarのなかみ



以前、StatusBarに情報表示させるアプリを作った際に
ステータスバーでも自作のViewが表示出来ればいのにーと思ったのでStatusBarについて調べた。自分用のメモなので分かりにくくても仕方がない。

android.app.NotificationManager.notify(String tag, int id, Notification notification)
がアプリからコールされて、そのなかでenqueueNotificationWithTag()がコールされる。
サービスとプロセス間通信していいて、インターフェースのStubは
INotificationManager.Stubで、その実態の中で
com.android.server.StatusBarManagerService.addNotification()
がコールされている。
これも中でIPCのI/Fの
com.android.internal.statusbar.IStatusBar.addNotification()
をコールしている。
これの実態は
./frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
にIStatusBar.Stubの拡張として定義されているのだが、これがeclipseのパッケージに含まれてないんだよなー。なぜ??
まあいいや。
んで、それを
./frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarService.java
内で利用しているので、
StatusBarService.java内で作られたViewに対して、プロセス間通信を利用して
android.app.NotificationManagerから情報を渡していっている。
情報はStatusBarIcon implements Parcelableをプロセス間でわたして行っている。といったかんじ。

StatusBarIconView.set()からコールされているgetIcon()の中をみると
r = context.getPackageManager().getResourcesForApplication(icon.iconPackage);
みたいな感じにで、他のパッケージのリソースを取得している。
こうやれば他のパッケージが持っているリソースを利用出来るっぽい。
getIcon()の中でリソースのIDをとってDrawableを作成している。
なので、アイコンは自在に変更できそうもない。という結論。

プロセス間通信しまくりで、最終的にはSystemのUIに行ってしまったので、
やっぱり自作Viewの表示は無理そうだ。
でも、ソースを見ていて
./frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
は面白かった。
Handlerを拡張して自作のHandlerを作成して、
そのハンドラーに対して
mHandler.obtainMessage(MSG_UPDATE_NOTIFICATION, 0, 0, ne).sendToTarget();
みたいにMessageをsendしている。
handleMessageってどんな時に使うのが正解なんだろう?
と思っていたけど、このようにプロセス間通信で情報を取得して
それをUIの変更に反映させたい時には、プロセス間通信用のI/Fの中で
MessageをsendすることでUIスレッドに処理を変えてやることで、いい感じの実装になると感じた。

Handlerとかの仕組みって、どうやって思いついたんだろ?
本当はそれが一番知りたい。。。

2011年6月28日火曜日

androidのbuildが32bitでも出来るようになってた?


androidのソースの中身が更新されていて64bitじゃなきゃビルド無理ーな部分が消えていた。
実際、何も変更加えなくてもbuildできた。
でも、HPには8GBのRAMが必要って書いてあったけど、virtual machineじゃないからOK??
良く解らん。


マシンのスペックを晒すと以下のような感じ。

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=10.04
DISTRIB_CODENAME=lucid
DISTRIB_DESCRIPTION="Ubuntu 10.04.2 LTS"


$ cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz
stepping : 10
cpu MHz : 2003.000
cache size : 6144 KB
physical id : 0
siblings : 4
core id : 0
cpu cores : 4
apicid : 0
initial apicid : 0
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm tpr_shadow vnmi flexpriority
bogomips : 5652.55
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:

processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz
stepping : 10
cpu MHz : 2003.000
cache size : 6144 KB
physical id : 0
siblings : 4
core id : 1
cpu cores : 4
apicid : 1
initial apicid : 1
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm tpr_shadow vnmi flexpriority
bogomips : 5652.46
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:

processor : 2
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz
stepping : 10
cpu MHz : 2003.000
cache size : 6144 KB
physical id : 0
siblings : 4
core id : 2
cpu cores : 4
apicid : 2
initial apicid : 2
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm tpr_shadow vnmi flexpriority
bogomips : 5722.90
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:

processor : 3
vendor_id : GenuineIntel
cpu family : 6
model : 23
model name : Intel(R) Core(TM)2 Quad CPU Q9550 @ 2.83GHz
stepping : 10
cpu MHz : 2003.000
cache size : 6144 KB
physical id : 0
siblings : 4
core id : 3
cpu cores : 4
apicid : 3
initial apicid : 3
fdiv_bug : no
hlt_bug : no
f00f_bug : no
coma_bug : no
fpu : yes
fpu_exception : yes
cpuid level : 13
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe nx lm constant_tsc arch_perfmon pebs bts aperfmperf pni dtes64 monitor ds_cpl vmx smx est tm2 ssse3 cx16 xtpr pdcm sse4_1 xsave lahf_lm tpr_shadow vnmi flexpriority
bogomips : 5652.50
clflush size : 64
cache_alignment : 64
address sizes : 36 bits physical, 48 bits virtual
power management:


$ cat /proc/meminfo
MemTotal: 3355252 kB
MemFree: 842344 kB
Buffers: 196160 kB
Cached: 1619120 kB
SwapCached: 3308 kB
Active: 1101240 kB
Inactive: 1249040 kB
Active(anon): 221664 kB
Inactive(anon): 320800 kB
Active(file): 879576 kB
Inactive(file): 928240 kB
Unevictable: 0 kB
Mlocked: 0 kB
HighTotal: 2498184 kB
HighFree: 624792 kB
LowTotal: 857068 kB
LowFree: 217552 kB
SwapTotal: 9823708 kB
SwapFree: 9809132 kB
Dirty: 112 kB
Writeback: 0 kB
AnonPages: 532084 kB
Mapped: 44960 kB
Shmem: 7456 kB
Slab: 98232 kB
SReclaimable: 85304 kB
SUnreclaim: 12928 kB
KernelStack: 2616 kB
PageTables: 6432 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 11501332 kB
Committed_AS: 1298504 kB
VmallocTotal: 122880 kB
VmallocUsed: 70356 kB
VmallocChunk: 34300 kB
HardwareCorrupted: 0 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 4096 kB
DirectMap4k: 36856 kB
DirectMap4M: 872448 kB


全然関係ないけど、bloggerにも「+1」ボタンが増えていますね。

2011年6月15日水曜日

TYPE_SYSTEM_ALERTでいつでも一番上


なんか震災から落ち着かない日々が続いていたので、
全然かけてなかったけど書くかな。

Dock4DroidとかWave Launcherなど、Androidにおいて、
すべての画面で例外なく一番上にUI部品を配置する方法についてメモを取っておく。

基本的には
http://harehare1110.blogspot.com/2011/04/android-view.html
を参照すればそのようなことができる。
でもTYPE_SYSTEM_OVERLAYを使うとキーイベントとかが全部捨てられてしまうので、
TYPE_SYSTEM_ALERTを使う。
http://developer.android.com/reference/android/view/WindowManager.LayoutParams.html
にWindowManager.LayoutParamsの説明がある。

public static final int FLAG_LAYOUT_NO_LIMITS
Since: API Level 1
Window flag: allow window to extend outside of the screen.

とか試してないけど、面白そうなフラグもあった。

で、自分でも試したいので、サンプルを作った。以下に置いてある。
https://github.com/matsuhiro/TestForTYPE_SYSTEM_OVERLAY
気をつけるのは、
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
をManifestに書くくらいで、作ったソースのフラグとかは以下のよううな感じ。
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.WRAP_CONTENT,
    WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
    WindowManager.LayoutParams.FLAG_FULLSCREEN |
    WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
    PixelFormat.TRANSLUCENT);
  WindowManager wm = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
FLAG_NOT_FOCUSABLEを入れてあげないと裏側の画面がうまく動かない。。。
でもAndroidってフォーカスが当たっているところにイベント配信する仕組みだったような??
InputMethodもViewキッカケだと起動しないみたいだし。
なんでもかんでも好きなようには出来ないかもですね(^^ゞ


調べてみて思ったのは、Dock4DroidとかWave Launcherなどのランチャーなら
こういった仕組みは便利だけど、それ以外だとちょっと使い道がなさそうということ。
今ランチャー以外で考えつくのは、C2DMとかのキッカケで全画面に
このようなViewを貼りつけて強制的に端末を操作不能にするとか?
うーむ。なんか危ないなこの仕組。
面白いけど、端末ベンダーの証明書で署名されていないアプリは
使えないようにしたほうがいいじゃねぇかな。

2011年4月11日月曜日

続:Fragmentとandroid-support-v4.jar




気になったので、シーケンスとか描いてみた。

2.3.3 & android-support-v4.jarのシーケンス

3.0のシーケンス

android-support-v4のソースの中身を見てみたが、
どうやら
FragmentActivity内のonPostCreateで、
dispatchActivityCreatedしているのが問題の模様。
http://developer.android.com/guide/topics/fundamentals/fragments.html
には、ActivityのonStartと同じタイミングで
FragmentのonActivityCreatedのあとでのonStartがコールされるはずであり、
これに関しては問題ない模様であるが、
onStart後にコールされるonPostCreatedのタイミングで
またActivityCreatedしようとしてしまっている。
なので、FragmentActivity#onPostCreate内のdispatchActivityCreatedをコメントアウトすれば治りそうな予感。

まあ、それでもActivityの呼び出し元からFragmentのmethodを呼び出すのではなく、
Activityを拡張したFragmentActivityからFragmentのmethodを呼び出しているわけだから
シーケンスのタイミングのずれが発生するのは仕方の無いことか。。。

2011年4月6日水曜日

Fragmentとandroid-support-v4.jar


"SDK"/extra/android/compatibilityにある
FragmentManager.javaを読んだけど
新しいUI部品というよりはユーティリティーの追加のように感じる。
本来のFragmentの実装ではなく
Fragmentとほぼ同じ機能を有した外部ライブラリとして
FragmentActivityとかを実装しているわけだから、それは仕方のないことかも。
サンプルとしては、
FramgnetStackSupport.javaを参考にすればいい。
基本的には、FragmentXXXSupport.javaが外部ライブラリを利用したサンプルになる。

だが、そもそもFragmentを利用するメリットはどこにあるのだろうか?
画面遷移が画面全体の一部の差し替えになることで
かっこいいUIをつくることが出来る。
これは、Viewのadd removeでも出来たことだ。
でも、なんであえてこんなモノがあるのかの理由は
http://android-developers.blogspot.com/2011/02/android-30-fragments-api.html
に書いてある。
XMLを変えて、Activityを一部増やすことで
tablet向けにつくっていたActivityをソースコードをまるまる流用して
二つのActivityに分けることが出来ると言っているわけだ。

ただ、ここで気になってくるのがライフサイクルだ。
compatibilityの外部ライブラリを利用した場合の
FragmentとActivityのライフサイクルと
本来のFragmentとActivityのライフサイクルが一緒なのだろうか?
結論を書くと全然違った!!

ApiDemosにあるFragmentHideShow.javaをほとんどパクって
compatibilityの外部ライブラリを利用して作成したプロジェクトと
3.0の本物のFragmentを利用して作成したプロジェクトを作った。
長いからサンプルコードは貼り付けないが、
FragmentActivityはActivity
FirstFragmentはFragment
SecondFragmentはFragment
で、その各コールバックにEnter/Exitのログを仕込んだ。
Logcatで見えやすいのでログレベルをあえて分けた。

2.3.3 & android-support-v4.jarで作ったFragmentのサンプル


3.0で作ったFragmentのサンプル



結果としては、
2.3.3 & android-support-v4.jarのログ
----------------------------------------
Enter FragmentActivity#onCreate
Enter SecondFragment#onAttach
Exit SecondFragment#onAttach
Enter SecondFragment#onCreate
Exit SecondFragment#onCreate
Enter SecondFragment#onCreateView
Exit SecondFragment#onCreateView
Enter FirstFragment#newInstance
Exit FirstFragment#newInstance
Exit FragmentActivity#onCreate
Enter FragmentActivity#onStart
Enter FirstFragment#onAttach
Exit FirstFragment#onAttach
Enter FirstFragment#onCreate
Exit FirstFragment#onCreate
Enter SecondFragment#onActivityCreated
Exit SecondFragment#onActivityCreated
Enter SecondFragment#onStart
Exit SecondFragment#onStart
Enter FirstFragment#onCreateView
Exit FirstFragment#onCreateView
Enter FirstFragment#onActivityCreated
Exit FirstFragment#onActivityCreated
Enter FirstFragment#onStart
Exit FirstFragment#onStart
Exit FragmentActivity#onStart
Enter SecondFragment#onStop
Exit SecondFragment#onStop
Enter FirstFragment#onStop
Exit FirstFragment#onStop
Enter FragmentActivity#onResume
Exit FragmentActivity#onResume
Enter SecondFragment#onStart
Exit SecondFragment#onStart
Enter SecondFragment#onResume
Exit SecondFragment#onResume
Enter FirstFragment#onStart
Exit FirstFragment#onStart
Enter FirstFragment#onResume
Exit FirstFragment#onResume
----------------------------------------

3.0のログ
----------------------------------------
Enter FragmentActivity#onCreate
Enter SecondFragment#onAttach
Exit SecondFragment#onAttach
Enter SecondFragment#onCreate
Exit SecondFragment#onCreate
Enter SecondFragment#onCreateView
Exit SecondFragment#onCreateView
Enter FirstFragment#newInstance
Exit FirstFragment#newInstance
Exit FragmentActivity#onCreate
Enter FirstFragment#onAttach
Exit FirstFragment#onAttach
Enter FirstFragment#onCreate
Exit FirstFragment#onCreate
Enter FragmentActivity#onStart
Exit FragmentActivity#onStart
Enter SecondFragment#onActivityCreated
Exit SecondFragment#onActivityCreated
Enter SecondFragment#onStart
Exit SecondFragment#onStart
Enter FirstFragment#onCreateView
Exit FirstFragment#onCreateView
Enter FirstFragment#onActivityCreated
Exit FirstFragment#onActivityCreated
Enter FirstFragment#onStart
Exit FirstFragment#onStart
Enter FragmentActivity#onResume
Exit FragmentActivity#onResume
Enter SecondFragment#onResume
Exit SecondFragment#onResume
Enter FirstFragment#onResume
Exit FirstFragment#onResume
----------------------------------------

全然違う。。。
ぱっと見た感じだと
android-support-v4.jarを利用したほうがバグっているような。。。
3.0のソースを見ることが出来ないから、どんな理由でこのような差分が出ているのか
正確なところは解らない。
でも今のところFragmentを3.0以外で利用するのは控えたほうが無難かも。
少なくともそのまま流用は考えないほうがいいと思う。

2011年4月3日日曜日

Google Apps Scriptが使えそう



Google Apps Script(GAS)が気にはなっていたけど、
特に使いたいシーンもないし使っていなかった。
最近、東京電力が電力の利用状況をCSVで公開し始めた。
こいつを利用したアプリをマーケットに公開したが、
東京電力からのデータは一日分だけしか手に入らないし、
最新の情報であっても時間にすれば1時間半以上前の情報になってしまっている。
本当に知りたいのは、30分か1時間後の予測値だと思う。
それを実現するための第1歩として、データの収集がある。
収集しておいて、それを後で利用するためにサーバ立ててDBに入れて、
取り出すためのIF定義して。。。
みたいなことをしなければいけないほど大規模なデータでもない。
1日24要素なので、1年間続けたとしても9000弱の要素しかない。
なので、Google DocsのSpreadSheetに入れればいいし、GASで遊んでみたたいし。。。

GASを利用するためには
といった感じでスクリプトエディタを開いて、
のようなスクリプトを書く。Javascriptで。
初めてJavascript書いた。はずい。。。

そして、自動でデータを集めたいので定期実行するわけだが、
「トリガー」の中のメニューを選ぶと
のような画面になる。上の絵では10分おきにgetTodenDataを実行している。


自動で最新のデータがSpreadSheetに保存出来た。
データが欲しいときは、
ただ見たいなら
https://spreadsheets.google.com/ccc?key=0Arsm5M6qVMHDdEJDZUVTbTg5UWxWbGJ4Rm1ISVF1RHc&hl=en&output=html

CSVでデータが欲しいのであれば
https://spreadsheets.google.com/ccc?key=0Arsm5M6qVMHDdEJDZUVTbTg5UWxWbGJ4Rm1ISVF1RHc&hl=en&output=csv
とすることで誰でもダウンロードできる。


自分は普段スクリプト言語使わないし、
使ったことあるとしてもコピペーしかしたことが無かったけど、
一応、目的のものを作ることが出来た。
書いたのは画像で貼り付けた関数一つだけなので、
それで、定期実行やデータの公開が出来るのは非常に便利だと思う。

2011年3月29日火曜日

シリコンバレーカンファレンスに行って来た。


いまさらではあるが、
今月の初めにシリコンバレーカンファレンスに行って来た。
アメリカにいる最中に地震とかあって、なんだか落ち着かない旅であった。

シリコンバレーは、ソフトウェアとか色々生まれているところではあるが、
会社自体はドコも一緒だなぁ。というのが感想。
ダメなマネージャーはいるし、
開発プロセスがめちゃくちゃだったり、
給料は高そうであるが、出費も多くなるし、
田舎過ぎて経済的な効率はかなり悪そう。
住みやすい場所ではない。

そして、一番印象的だったのは、シリコンバレーにある会社でも
実装を東欧の賃金の安いとこで行っていたことだ。
全部の会社がそうであるわけではないし、
普通に考えれば当たり前だけど、シリコンバレーでも
賃金の安いところに実装(当然詳細設計もだろう)を出しているわけだ。
これを聞いて、ますますドコも一緒だと感じた。

ただ、シリコンバレーはたいしたことないとか思ったわけでもない。
GoogleもAppleもシリコンバレーにあるし、
ケータイのOS作りたかったら、シリコンバレーに行くしかない。

日本のソフトウェアの業界でもぜひシリコンバレーのようであって欲しい点としては、
人同士のつながりが、次の職場を決めていく感じとか
自分が何をしているのかカンファレンスとかに出て発表したりしていないと
職にありつけなさそうな所だ。
自分の能力を人に伝える努力をしないと職にあぶれるのはかなり厳しいけれど、
とても良いところだと思う。
こういった仕組みがあれば、
パワポ職人みたいなエンジニアもどきの数は、多少は減るだろうと思う。

ただ、東京でもモバイル端末のアプリケーションに関しては、
上のような仕組みがそのうち生まれ始めるのではないかと思っていたりする。

2011年2月27日日曜日

AndroidのLooper覚書


デ部に参加したときにLooperの話を聞いた。
なんとなくは解ったけど実際のところどうなのか微妙な理解なので
ソースをみてみた。
frameworks/base/core/java/android/os/Handler.java
の109行目あたりにコンストラクタがある(この辺のソースは、そうそう変わらないだろうから行数かいてみた)が、
prepareしていないとエラーが起こるような仕組みになっている。
なので、
new Thread(new Runnable() {
   @Override
   public void run() {
    Handler handler = new Handler();
    
   }
   
  }).start();
こんなことすると落ちる。
UI threadの場合はActivityが作成される際に、Looper.java内のprepareMainLooper()がコールされて、準備OKになるけど、UIでないthreadは自分でprepareしてあげないといけない。
そのことはLooperのjavadocコメントに書いてあったりする。
class LooperThread extends Thread {
 public Handler mHandler;
 public void run() {
  Looper.prepare();
  
  mHandler = new Handler() {
   public void handleMessage(Message msg) {
    // process incoming messages here
   }
  };
  
  Looper.loop();
 }
}
こんな感じで書いてみればーって感じでコメントがある。

でも、実際どーやって使うの?
って感じなので動くサンプルかいてみた。
package com.matsuhiro.android.loopersample01;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class LooperSample01 extends Activity {
 private static final String TAG = "LooperSample01";
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  Button btn_ui = (Button) findViewById(R.id.button1);
  Button btn_not_ui1 = (Button) findViewById(R.id.button2);
  Button btn_not_ui2 = (Button) findViewById(R.id.button3);

  Thread th = Thread.currentThread();
  long id = th.getId();
  Log.v(TAG, "onCreate ui id="+id);
  
  final LooperThread lt = new LooperThread();
  lt.start();
  
  btn_ui.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Thread th = Thread.currentThread();
    long id = th.getId();
    Log.v(TAG, "ui id="+id);
   }
  });
  btn_not_ui1.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    lt.mHandler.post(new Runnable() {
     @Override
     public void run() {
      Thread th = Thread.currentThread();
      long id = th.getId();
      Log.v(TAG, "not ui id="+id);
     }
     
    });
    
   }
  });
  btn_not_ui2.setOnClickListener(new View.OnClickListener() {
   @Override
   public void onClick(View v) {
    lt.mHandler.post(null);
    
   }
  });
 }
 
 class LooperThread extends Thread {
  public Handler mHandler;
  public void run() {
   Log.v(TAG, "Looper.prepare()");
   Looper.prepare();
   mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
     // process incoming messages here
     Thread th = Thread.currentThread();
     long id = th.getId();
     Log.v(TAG, "LooperThread id="+id);
    }
   };
   Looper.loop();
  }
 }
}
LooperThreadクラスの中でHandlerのhandleMessageをoverrideしているけど
こいつはMessageの中にcallback(Runnableのこと)が無かったら実行される。(インスタンス生成時にデフォルトのcallbackを設定しなければの話ではあるが。)
なので、
起動してbtn_ui、btn_not_ui1、btn_not_ui2の順番にクリックすると
onCreate ui id=1 ← onCreateが呼ばれた
Looper.prepare() ← 勝手Looperの準備しました
ui id=1 ← btn_uiクリックしました
not ui id=8 ← btn_not_ui1クリックしました
LooperThread id=8 ← btn_not_ui2クリックしました
のようなLog.vが出力される。

正直なところ、こんなの知っていたからって何時使うんだろって感じではある。
ただ、AsyncTaskとかを利用するような処理をいっぱいbackgroundで走らせると
基本設計時に考えたThread構成が良くわかんない感じになりそうではある。
そんなときには、自分でworker threadを作ってそのworkerに決まった処理をpostする。みないな仕組みを自分で作るにはいいかもね。

RSSのパースとか


AndroidにおけるRSSのパースに関しては、実はIBMのサイトで詳しく解説されている。
リンクで上げたページでは、パースの方法をいろいろ説明するためにFactoryを作っているけど
自分はandroid.sax.*を利用して実装した。
パースする前にHTTP GETするわけだけど、そのときに
httpget.setHeader("User-Agent", "Mozilla/5.0");
をセットし忘れないようにしないと、レスポンスがUTF-8で来てくれない。もっとも今はこれで平気だけど、何時ダメになるかわからないんですがね。。。

以前のエントリのようにAsyncTaskを利用して、HTTP GETして、
レスポンスを
 protected List<message> doInBackground(String... params) {
  String query;
  query = "https://www.google.com/history/find?output=rss"+"&q="+params[0];
  HttpGet httpget = new HttpGet(query);
  httpget.setHeader("User-Agent", "Mozilla/5.0");
  HttpResponse response;
  List<message> messages = null;
  
  try {
   response = _HttpClient.execute(httpget);
   
   int status_code = response.getStatusLine().getStatusCode();
   if (status_code < 400){
    InputStream istream = response.getEntity().getContent();
    FeedParser parser = new AndroidSaxFeedParser(istream);
    messages = parser.parse();
    istream.close();
   }
  } catch (ClientProtocolException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } finally {
   // TODO finalize
  }
  return messages;
 }
のようにして取得する。(エラー処理とかははずしてしまったが。。。) messagesには各RSSのアイテムが入っているので、 それぞれ値を取得してリストに表示させてやればいい。

今回は欲しい情報を得るのに、認証が必要でその処理がCookieを利用していた。 あんまりいい方法じゃないかもしれないが、一度認証済みのHttpClientを他のActivityでも使いまわしたいな。。。と思ったので、"android データ Activity"でググッたら出てくる出てくる。 簡単に言うとApplicationのインスタンスにデータを保持させて全体からそれを覗く。ということだ。 準備は
public class SingleConnectApp extends Application {
 private DefaultHttpClient _HttpClient = null;
 
 public void setHttpClient(DefaultHttpClient client) {
  _HttpClient = client;
 }
 
 public DefaultHttpClient getHttpClient() {
  return _HttpClient;
 }
}
こんなクラスを作ってmanifestのapplicationタグを <application android:icon="@drawable/gfoot_icon" android:label="@string/app_name" android:name="SingleConnectApp"> のようにして、onCreate内とかで
SingleConnectApp app = (SingleConnectApp)this.getApplication();
  DefaultHttpClient client = app.getHttpClient();
のようにすればActivity間でインスタンスを共有できる。
よくよく考えるとDefaultHttpClientを拡張してsingletonのクラス作れば同じこと出来んじゃね?てかソッチのが良いな。とか思ったけど、Applicationクラスにデータを持たしてみたかったのでそのまま進めた。

2011年2月19日土曜日

leanbackとYouTube Remote


Androidで面白いのを見つけたので紹介。
youtubeのleanbackをリモートコントロールするソフトだ。
そもそもleanback自体があまり知られていない。
自分はIT企業に勤めているけど、そこでも知っている人のほうが少ないようだ。
youtubeのURLの後ろにleanbackと打つだけで、TVっぽいUIのページ(http://www.youtube.com/leanback)に飛ぶ(たぶんFlash)。
基本的に十字キーだけでコントロールできるように設計されている。

youtubeにログインして、leanbackのページをPCで表示させてから
これを起動すると、
Androidからleanbackの表示コンテンツがコントロールできる。
スゲー。
レスも速いし。
firefoxでleanbackのページを全画面表示にするとほんとにテレビっぽい。

youtubeに上げてみた。

2011年2月9日水曜日

AndroidでGoogle Web履歴をHTTP GET


Gfootというアプリをマーケットにリリースした。
アプリをリリースしたけど、何をやったのか?調べたのか?
書き残しておかないと忘れてしまいそうなので、
そろそろまとめてみる。
・ブラウザを利用しないでCookieを解決する
・RSSのパース
がメインかな?

GoogleのWeb履歴を見る際のCookieの扱いについては、ここを参照しました。
このサイトによると、履歴を見るためにログインするわけだが、そのためのCookieをログイン画面の
HTMLをGETで取得することで、解決していることがわかる。
また、ログインする際にformの情報をPOSTするわけだが、その情報も同時に取得している。
ただ、これだけだとAndroid上でどうやってログイン処理すればよいのかが
具体的にはわからないので、ここを参考にしてみた。
上記二つのサイトを参考に組み合わせてAndroidからGoogle Web履歴にアクセスしている。
以下は、説明に要らない部分など削っているので、変なところもあるかと思うが、
基本的には下記で得られた_HttpClient(DefaultHttpClientクラス)を利用すれば、履歴の取得が可能だ。
ちなみにHTMLをパースしてinput部分を取得するために
jericho-html-3.1.jarを利用しています。(LGPLなので使うだけならソースの公開義務はないはず。)
それと当然ですが、ネットワーク系の処理をするときにUIスレッドで処理すると
操作性が悪くなるので、AsyncTaskを利用してます。

@Override
  protected DefaultHttpClient doInBackground(String... params) {
   String email = params[0];
   String passwd = params[1];
   
   _HttpClient.getParams().setParameter(ClientPNames.COOKIE_POLICY, CookiePolicy.BROWSER_COMPATIBILITY);
   _HttpClient.getParams().setParameter("http.connection.timeout", 5000);
   _HttpClient.getParams().setParameter("http.socket.timeout", 3000);
   
   HttpGet httpget = new HttpGet(https://www.google.com/accounts/ServiceLogin?hl=ja&nui=1&service=hist);
   HttpResponse response;
   String result = null;
   try {
    response = _HttpClient.execute(httpget);
    int status_code = response.getStatusLine().getStatusCode();
    Log.v("Gfoot","status_code="+status_code);
    if (status_code < 400){
     InputStream istream = response.getEntity().getContent();
     InputStreamReader reader = new InputStreamReader(istream);
     BufferedReader objBuf = new BufferedReader(reader);
     StringBuilder builder = new StringBuilder();
     String sLine;
     while((sLine = objBuf.readLine()) != null){
      builder.append(sLine);
     }
     result = builder.toString();
     istream.close();
    } else {
     _HttpClient = null;
    }
   } catch (ClientProtocolException e) {
    _HttpClient = null;
    e.printStackTrace();
   } catch (IOException e) {
    _HttpClient = null;
    e.printStackTrace();
   }
   if (result == null) {
    return null;
   }
   
   
   String name;
   String value;
   Source source = new Source(result);
   List linkElements = source.getAllElements(HTMLElementName.INPUT);
   int count = linkElements.size();
   List nameValuePair = new ArrayList(count);
   for (Element linkElement : linkElements) { 
    name = linkElement.getAttributeValue("name");
    value = linkElement.getAttributeValue("value");
    if ("Email".equals(name)) {
     value = email;
    }
    if ("Passwd".equals(name)) {
     value = passwd;
    }
    nameValuePair.add(new BasicNameValuePair(name, value));
   }
   // この段階でPOSTするために必要な情報は、nameValuePairに含まれている。
   
   HttpPost httppost = new HttpPost(https://www.google.com/accounts/ServiceLoginAuth?service=hist);
   try {
    httppost.setEntity(new UrlEncodedFormEntity(nameValuePair));
    response = _HttpClient.execute(httppost);
   } catch (UnsupportedEncodingException e) {
    _HttpClient = null;
    e.printStackTrace();
   } catch (ClientProtocolException e) {
    _HttpClient = null;
    e.printStackTrace();
   } catch (IOException e) {
    _HttpClient = null;
    e.printStackTrace();
   }
   
   return _HttpClient;
  }

ここでログインの際に
https://www.google.com/accounts/ServiceLogin?service=hist
というのを利用して、ログインのためのCookieを取得しているが、
これはどうやって知るのか?履歴以外はどうなのか?
気になるところだと思う。
このサイトがかなりいろいろな情報をもたらしてくれる。
service=histは履歴をさしているわけだ。

次回は、履歴やBookmarkをRSS形式で取得する部分と
パースについて。
また、おまけでアプリケーション全体において永続的なデータを作る方法。

2011年2月6日日曜日

Androidマーケットに初登録


アンドロイドマーケットに初めて登録しました。
マーケットへのリンク
いろいろ調べたことは、また後日に書こうかと思います。

2011年1月27日木曜日

Google Bookmarks API 。。。は無いらしい


Google Bookmarksが便利だし、履歴も含めたアプリ作れないものかと思って少し調べてみた。
結論としては、公式にはAPIはない模様。。。

ただし。XML形式でデータは取れる。

https://www.google.com/bookmarks/lookup?start=25&output=xml
lookup → リストを取得の意

https://www.google.com/bookmarks/find?q=maps.google.com&start=25&output=xml
find → 検索の意
q=maps.google.com → キーワード"maps.google,com"で検索
start=25 → 25個目から取得
utput=xml → XML形式で取得


本当に欲しかったのは履歴なので、それは後日調べることにする。

以下追記
履歴は
https://www.google.com/history/find?output=rss&st=maps&q=
で、RSS形式でデータが取れる模様。
BookmarkもRSS形式で取得可能だった。
st=maps → google mapsのデータを取得している。
st部分を変更すれば、他の種類のデータが取れそう。
ただ、wgetは出来なそう。
ここを見たけど、ブラウザのCookieをりようすれば可能との事。

2011年1月18日火曜日

Google Bookmarksを共有


最近知ったんだけど、
Google Bookmarksを旅行での写真や動画を共有するのにちょうどいい。

写真→picasaへアップロード
動画→youtubeとかほかの動画サイトへアップ

で、ばらばらなものを一つにまとめる。
かつ、プライバシーの観点から閲覧を許可制にする。
これのためのGoogle Bookmarks
https://www.google.com/bookmarks/l
にアクセスすと英語表記のページになる。(最後の「l」が大事)
label等で分けたりしたBookmarkにチェックをつけて、
Copy to listを選択して、リスト作成したり、すでにあるリストに追加したり。

作ったリストは、左上の「My lists」から見られる。
リストを共有したい場合は、リストを表示した状態で、
Shareを選択すると表示されるポップアップで、
「People with access」タブの「Invite people」に
見せても良い人のGmailのアドレスを打ち込んでinviteでOK。

visibilityはprivateにするのをお忘れなく。

結構便利。

2011年1月5日水曜日

android build on 32bit (froyo/gingerbread)


froyo(2.2)から、Androidのソースをbuildしようとしても
64bitじゃないからムリー。みたいな事をいわれてビルドできなかった。
しばらく放置してたけど、もう一度調べてみたら、
出来たのでメモをとっておく。

1)
$ repo init -u git://android.git.kernel.org/platform/manifest.git -b froyo
もしくは、
$ repo init -u git://android.git.kernel.org/platform/manifest.git -b gingerbread
でソースコード取得。

2)
$ repo sync

3)
64bitの記述があるところを修正
32-bit support in froyo/2.2を参考にすればいける。

./build/core/main.mk
の以下のように編集
ifeq ($(BUILD_OS),linux)
build_arch := $(shell uname -m)
-ifneq (64,$(findstring 64,$(build_arch))) ←コメントアウトしたり削除したり
+ifneq (i686,$(findstring i686,$(build_arch))) ←追加するコード
$(warning ************************************************************)
$(warning You are attempting to build on a 32-bit system.)

4)
以下の4つの中に
./external/clearsilver/cgi/Android.mk
./external/clearsilver/java-jni/Android.mk
./external/clearsilver/util/Android.mk
./external/clearsilver/cs/Android.mk

LOCAL_CFLAGS += -m64
LOCAL_LDFLAGS += -m64
という記述があるので、コメントアウトするか
LOCAL_CFLAGS += -m32
LOCAL_LDFLAGS += -m32
のように32bitに書き換える。

5)
$ make -j2
で、コンパイル開始。
コアの数や作業状況によっては、-j4としたほうがコンパイルは早くなる。


手順4)を抜かしてみたけど自分の環境では、
64bit用のヘッダーがないよー。みたいなエラーが出たので、
ダメみたいです。

最後は、以下を実行すればemulatorが立ち上がる。
./out/host/linux-x86/bin/emulator -kernel ./prebuilt/android-arm/kernel/kernel-qemu -system ./out/target/product/generic -ramdisk ./out/target/product/generic/ramdisk.img -data ./out/target/product/generic/userdata.img -skindir ./sdk/emulator/skins/ -skin HVGA


froyoでもgingerbreadでも同じ手順でbuild出来ました。

2011年1月3日月曜日

WebViewで画像を表示



リモートの画像を読み込む方法として、
ImageViewを使うよりもWebViewを使うほうが良いのではないかと思ったので、
サンプルソースを書いてみた。
WebViewImageLoad01.tar.gz


AsyncTaskを利用して、バックグラウンドで
mClient = new DefaultHttpClient();
mGetMethod = new HttpGet();
mGetMethod.setURI(new URI(uri));
HttpResponse resp = mClient.execute(mGetMethod);
InputStream is = resp.getEntity().getContent();
Bitmap bit = createBitmap(is);
のようにする手もあるが、
uriに指定された画像が、大きすぎてメモリ不足になったりしたときに落ちる。。。
WebViewを利用しておけば、この点はWebkitのほうで何とかしてくれる?とか
思ったので、とりあえずやってみた。
でも、画像が読み込めなかったときに、標準ブラウザの「?」が表示されちゃうから
やっぱり無しかな。。。

loadImageは、画像をhttpで取得して表示
loadImage2は、トリミングして表示
loadImageFromSDは、sdcardから取得した画像を表示
loadImageFromSD2は、トリミングして表示
トリミング自体は、CSSで記述しています。

表示してみるとわかるが、
setBackgroundColor(Color.parseColor("#00000000"))を利用することで
WebViewの背景を透明にできる。さらにlayoutのファイルで
backgroundを指定するれば、色つきの透過とか色々できる模様

上がloadImageで、下がloadImage2。
ちなみに上記スクリーンショットは
eclair-x86で、ubuntuでつくったイメージをWindowsのVMplayerで実行しているところ。

もとの画像は以下のもの。(ちなみに戸田恵梨香さん。勝手に使っているけど平気かな。。。)



CustomWebViewClientは、シーケンスを見るためにWebViewClientを拡張しただけです。
package com.matsuhiro.android.webimageload01;

import java.io.File;

import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Environment;
import android.webkit.WebView;

public class WebImageload01 extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        WebView web1 = (WebView) findViewById(R.id.web1);
        loadImage(web1, "http://img.article.pchome.net/00/26/58/73/pic_lib/wm/Toda_Erika1063.jpg");
        
        WebView web2 = (WebView) findViewById(R.id.web2);
        loadImage2(web2, "http://img.article.pchome.net/00/26/58/73/pic_lib/wm/Toda_Erika1063.jpg");
        
        WebView web3 = (WebView) findViewById(R.id.web3);
        loadImageFromSD(web3, "DCIM/100MEDIA/IMAG0077.jpg");
        
        WebView web4 = (WebView) findViewById(R.id.web4);
        loadImageFromSD2(web4, "DCIM/100MEDIA/IMAG0077.jpg");
        
    }
    
    private void loadImage(final WebView webview, final String image_uri) {
     final String html_src = "<html><head><style type=\"text/css\">body { margin: 0px; background: transparent; background-color: transparent;}</style></head><body><img src=\""+image_uri+"\" width=\"210\" height=\"210\"/></body></html>";
     webview.setBackgroundColor(Color.parseColor("#00000000"));
     new Thread() {
      @Override
      public void run() {
       CustomWebViewClient clieant = new CustomWebViewClient();
       webview.setWebViewClient(clieant);
          webview.loadData(html_src, "text/html", "UTF-8");
      }
     }.start();
    }
    
    private void loadImage2(final WebView webview, final String image_uri) {
     final String html_src = "<html><head><style type=\"text/css\">body { margin: 0px; background: transparent; background-color: transparent;}p.thumb a{ display:block; float:left; width:210px; height:210px; line-height:210px; overflow:hidden; position:relative; z-index:1}p.thumb a img{ float:left; position:absolute; top:0px; left:-32px;} </style></head><body><p class=\"thumb\"><a href=\"#\"><img src=\""+image_uri+"\" width=\"280\" height=\"210\"/></a></p></body></html>";
     webview.setBackgroundColor(Color.parseColor("#00000000"));
     new Thread() {
      @Override
      public void run() {
       CustomWebViewClient clieant = new CustomWebViewClient();
       webview.setWebViewClient(clieant);
          webview.loadData(html_src, "text/html", "UTF-8");
      }
     }.start();
    }
    
    private void loadImageFromSD(final WebView webview, String file_name) {
     final String html_src = "<html><head><style type=\"text/css\">body { margin: 0px; background: transparent; background-color: transparent;} </style></head><body><img src=\""+file_name+"\" width=\"210\" height=\"210\"/></body></html>";
     webview.setBackgroundColor(Color.parseColor("#00000000"));
     final File directory = Environment.getExternalStorageDirectory();
     new Thread() {
      @Override
      public void run() {
       CustomWebViewClient clieant = new CustomWebViewClient();
       webview.setWebViewClient(clieant);
       webview.loadDataWithBaseURL("file://"+directory.getAbsolutePath()+"/", html_src, "text/html", "UTF-8", "");
      }
     }.start();
    }
    
    private void loadImageFromSD2(final WebView webview, String file_name) {
     final String html_src = "<html><head><style type=\"text/css\">body { margin: 0px; background: transparent; background-color: transparent;}p.thumb a{ display:block; float:left; width:210px; height:210px; line-height:210px; overflow:hidden; position:relative; z-index:1}p.thumb a img{ float:left; position:absolute; top:-60px; left:0px;} </style></head><body><p class=\"thumb\"><a href=\"#\"><img src=\""+file_name+"\" width=\"210\" height=\"351\"/></a></p></body></html>";
     webview.setBackgroundColor(Color.parseColor("#00000000"));
     final File directory = Environment.getExternalStorageDirectory();
     new Thread() {
      @Override
      public void run() {
       CustomWebViewClient clieant = new CustomWebViewClient();
       webview.setWebViewClient(clieant);
          webview.loadDataWithBaseURL("file://"+directory.getAbsolutePath()+"/", html_src, "text/html", "UTF-8", "");
      }
     }.start();
    }
    
}



レイアウトは以下のような感じ。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">
 <ScrollView  
    android:id="@+id/ScrollView"  
    android:layout_height="fill_parent"  
    android:layout_width="fill_parent">
    <LinearLayout
   android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent" 
  android:background="@drawable/icon">
   <TextView  
       android:layout_width="fill_parent" 
       android:layout_height="wrap_content" 
       android:text="@string/hello"/>
      <WebView android:id="@+id/web1"
       android:layout_width="300dp" 
       android:layout_height="300dp"
       android:background="#2A0000FF" />
      <WebView android:id="@+id/web2"
       android:layout_width="300dp" 
       android:layout_height="300dp"
       android:focusable="false" 
       android:background="#2A00FFFF"/>
      <WebView android:id="@+id/web3"
       android:layout_width="300dp" 
       android:layout_height="300dp"
       android:background="#2AFF00FF" />
      <WebView android:id="@+id/web4"
       android:layout_width="300dp" 
       android:layout_height="300dp"
       android:focusable="false"
       android:background="#2A00FF00" />
  </LinearLayout>
    </ScrollView>
</LinearLayout>