2010年9月29日水曜日

Eclipse Short cut

自分のためのチートシート

■操作系

short cut を表示 : Ctrl + Shift + L

閉じる : Ctrl + W

全部閉じる : Ctrl + Shift + W

Open Resource : Ctrl + Shift + R

Editor を切り替える
Next Editor : Ctrl + F6
Previous Editor : Ctrl + Shift + F6
Quick Switch Editor : Ctrl + E

View を切り替える
Next View : Ctrl + F7
Previous View : Ctrl + Shift + F7

Perspective を切り替える
Next Perspective : Ctrl + F8
Previous Perspective : Ctrl + Shift + F8


■リファクタリング系

Refactor : Alt + Shift + T

Rename  : Alt + Shift + R

メソッドに分離
Extract method : Alt + Shift + M

ローカル変数に分離
Extract Local Variable : Alt + Shift + L


■コーディング系

必要な package を import に追加する
Organize Import : Ctrl + Shift + O


field の名前を変える
Rename in file : Ctrl + 2 , R


Class の field を割り当てる
Assign to field : Ctrl + 2, F


ローカル変数を割り当てる
Assign to local variable : Ctrl + 2, L


Quick Fix : Ctrl + 1


Content Assist : Ctrl + Space
(Space で切り替え)


Format : Ctrl + Shift + F


Delete Line : Ctrl + D

Delete Next Word : Ctrl + Delete

Delete Previous Word : Ctrl + Backspace

Delete to End of Line : Ctrl + Shift + Delete


Display : Ctrl + Shift + D


Incremental Find : Ctrl + J

Incremental Find Reverse : Ctrl + Shift + J

Find Next : Ctrl + K

Find Previous : Ctrl + Shift + K


Go to Line : Ctrl + L

Last Edit Location : Ctrl + Q


Word Completion : Alt + /


■コメント系

Toggle Comment : Ctrl + /

/ *
* Javadoc のコメントを付ける
*/
Generate Element Comment : Alt + Shift + J

/*
このコメントを付ける
*/
Add Block Comment : Ctrl + Shift + /

/*
このコメントをはずす
*/
Remove Block Comment : Ctrl + Shift + \


■ソースリーディング系

Open Call Hierarchy : Ctrl + Alt + H


Quick Outline : Ctrl + O


Quick Type Hierarchy : Ctrl + T


Declaration in Workspace : Ctrl + G


Find Text in Workspace : Ctrl + Alt + G


Open Attached Javadoc : Shift + F2


Toggle Break Point : Ctrl + Shift + B


Show View : Alt + Shift + Q,


Open Type : Ctrl + Shift + T


 

  

2010年9月27日月曜日

Android PowerManager を使う

PowerManager

デバイスの電源状態をコントロールするためのクラス

このAPIを使うと、デバイスのバッテリーライフに重大な影響を及ぼす。
なので、本当に必要な時以外は WakeLocks を取得しないこと!
そして可能な限り最低レベルの使用にし、必要なくなったらすぐに開放する!

1. Context.getSystemService() でこのクラスのインスタンスを取得

2. newWakeLock() で PowerManager.WakeLock オブジェクトを生成する

3. このオブジェクトのメソッドを使って、デバイスの電源状態をコントロールする

こんな感じ

PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.SCREEN_DIM_WAKE_LOCK, "My Tag");
wl.acquire();
// ..screen will stay on during this section..
wl.release();


次の flag によって、システムの電源への影響度を変えられる。
これらは組み合わせられない。

Flag ValueCPUScreenKeyboard
PARTIAL_WAKE_LOCKOn*OffOff
SCREEN_DIM_WAKE_LOCKOnDimOff
SCREEN_BRIGHT_WAKE_LOCKOnBrightOff
FULL_WAKE_LOCKOnBrightBright


*PARTIAL_WAKE_LOCK を設定した場合、CPU はタイマーに関係なく、ユーザーが電源ボタンを押した後でも走り続ける。それ以外の wakelocks を選択した場合は、ユーザーは電源ボタンを使ってデバイスをスリープ状態にすることができる。

上記の flag に加えて、以下の2つの flags を組み合わせることができる。
これらは、スクリーンの挙動にのみ影響を与える。また、これらの flags は
PARTIAL_WAKE_LOCK と組み合わせても意味はない。


Flag ValueDescription
ACQUIRE_CAUSES_WAKEUP通常の wake locks なので、実際に照明をつけることはない。その代わり、ユーザーの動作などによって、一度照明がつくとそのままの状態を保つ。この flag は WakeLock が取得されたとき、スクリーンとキーボードがすぐに立ち上がるように強制する。典型的には、ユーザーにとってすぐに見れることが重要な notifications などに使う。
ON_AFTER_RELEASEこの flag をセットすると、WakeLock が開放されたときにユーザーの activity timer がリセットされる。これによって照明がすこしだけ長くついたままになる。もし wake lock 状態を循環しているなら、ちらつきを減らすために使うことができる。



 

2010年9月23日木曜日

Android 次へ・戻るボタンを作る

Button には上下左右に画像を配置することができます。
これを使うと、こんな感じの「次へボタン」や「戻るボタン」
が作れます。



次の attribute を指定します。

 android:drawableLeft="@drawable/icn_left"
 android:drawableRight="@drawable/icn_right"
 android:drawableTop="@drawable/icn_top"
 android:drawableBottom="@drawable/icn_bottom" 


いろいろ並べてみました。



drawable の場所は縁から一定で、width が変わると文字との距離が変わる。
padding を設定すると、drawable と縁との距離が変わる。

文字と drawable の間を設定することはできないみたい。。。うーむ。
android:drawablePadding を使ってできました!
kuriyama さんありがとうございます~。

 android:drawablePadding="10dip"

のように使います。



 
xml はこちら

<?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"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:drawableLeft="@drawable/stat_sys_phone_call_forward"
android:drawableRight="@drawable/stat_sys_phone_call_bluetooth"
android:drawableTop="@drawable/stat_sys_phone_call"
android:drawableBottom="@drawable/stat_sys_phone_call_on_hold"
/>


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text= 'android:padding="20dip"'
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
android:drawableLeft="@drawable/stat_sys_phone_call_forward"
android:drawableRight="@drawable/stat_sys_phone_call_bluetooth"
android:drawableTop="@drawable/stat_sys_phone_call"
android:drawableBottom="@drawable/stat_sys_phone_call_on_hold"
android:padding="20dip"
/>


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text= 'android:layout_weight="1"'
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableLeft="@drawable/stat_sys_phone_call_forward"
android:text="Button"
/>

<Button
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:drawableRight="@drawable/stat_sys_phone_call_bluetooth"
android:text="Button"
/>
</LinearLayout>


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text= 'android:layout_width="wrap_content"'
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/stat_sys_phone_call_forward"
android:text="Button"
/>

<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableRight="@drawable/stat_sys_phone_call_bluetooth"
android:text="Button"
/>
</LinearLayout>


<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text= 'android:layout_width="150dip"'
/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button
android:layout_width="150dip"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/stat_sys_phone_call_forward"
android:text="Button"
/>

<Button
android:layout_width="150dip"
android:layout_height="wrap_content"
android:drawableRight="@drawable/stat_sys_phone_call_bluetooth"
android:text="Button"
/>
</LinearLayout>
</LinearLayout>

Android ProgressBar の indeterminate

ProgressBar の indeterminate って何に使うのか
いまいちよく分わかってなかったので、並べてみた。

直訳は indeterminate = 「不定期」

つまり、”進捗状況がわからないとき”ってことかな。

 android:text='style="?android:attr/progressBarStyleHorizontal"'

のときは違いが明確だけど、くるくるの方は同じだった。

Android 端末に設定されているアカウント情報を取得

端末に設定されているアカウントの一覧を取得するには

 AccountManager

を使います。(API Level 5)

AndroidManifest.xml に

 <uses-permission android:name="android.permission.GET_ACCOUNTS" />

を書くのを忘れずに。

 AccountManager.get(this) で現在の Context と関連付いている
 AccountManager のインスタンスが取得できます。
 このインスタンスに対して、getAccounts()
 getAccountsByType() で Account の配列が取得できます。
 Account クラスはアカウント名とタイプを保持しています。


アカウント名や名前を取得するには、こんなかんじ。

private String[] getAccounts() {
ArrayList accountsInfo = new ArrayList();
Account[] accounts = AccountManager.get(this).getAccounts();
for (Account account : accounts) {
String name = account.name;
String type = account.type;
int describeContents = account.describeContents();
int hashCode = account.hashCode();

accountsInfo.add("name = " + name +
"\ntype = " + type +
"\ndescribeContents = " + describeContents +
"\nhashCode = " + hashCode);
}

String[] result = new String[accountsInfo.size()];
accountsInfo.toArray(result);
return result;
}




 

2010年9月20日月曜日

Android Cloud to Device Message - C2DM - 3

Android アプリ側を作る

 ● AndroidManifext.xml の設定

  ・ 必要な permission

    ・メッセージを登録・受信 (<uses-permission>)
      com.google.android.c2dm.permission.RECEIVE

    ・インターネットの使用 (<uses-permission>)
      android.permission.INTERNET

    ・他のアプリのメッセージ登録・受信防止
     (<permission>, <uses-permission>)
      applicationPackage + ".permission.C2D_MESSAGE

    ・Broadcast receiver (<receiver>, <intent-filter>)
       (この Intent の <category> は applicationPackage)
      com.google.android.c2dm.intent.RECEIVE
      com.google.android.c2dm.intent.REGISTRATION

    ・C2DMフレームワークだけがメッセージを送れるようにする
      (<receiver>)
      com.google.android.c2dm.permission.SEND

    ・C2DMがうまく走らないような環境では、
     アプリがインストールされないようにする (<uses-sdk>)
      android:minSdkVersion="8"

    ・端末に設定されているアカウントを取得 (<uses-permission>)
      android.permission.GET_ACCOUNTS

    ・ PowerManager WakeLocksの使用を許可
      (<uses-permission>)
      android.permission.WAKE_LOCK

    ・CREDENTIALS を使う (<uses-permission>)
      android.permission.USE_CREDENTIALS
   
Sample

<manifest package="com.example.myapp" ...>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

<uses-permission android:name="android.permission.INTERNET" />

<permission android:name="com.example.myapp.permission.C2D_MESSAGE" android:protectionLevel="signature" />
<uses-permission android:name="com.example.myapp.permission.C2D_MESSAGE" />

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />


<receiver android:name=".C2DMReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<category android:name="com.example.myapp" />
</intent-filter>

<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="com.example.myapp" />
</intent-filter>
</receiver>


   ChromeToPhone のソースをみたら、MainActivity.java は

    android:launchMode="singleTop"

   になってた

   あとは、c2dm パッケージを使っていて、そのために若干マニフェスト
   の書き方が変わっていた。
   詳しくは ChromeToPhone のソースを見てください。
   chrometophone - Project Hosting on Google Code


 ● メッセージ登録用の Intent の発行

  ・Intent : com.google.android.c2dm.intent.REGISTER
  ・extras1 : Sender ID
  ・extras2 : Application ID


Intent registrationIntent = new Intent("com.google.android.c2dm.intent.REGISTER");
// Application ID (boilerplate)
registrationIntent.putExtra("app",PendingIntent.getBroadcast(this, 0, new Intent(), 0);
// Sender ID
registrationIntent.putExtra("sender", SenderID);
startService(registrationIntent);

  ChromeToPhone Sample では
    registrationIntent.setPackage("com.google.android.gsf");
  も入ってた。


 ● メッセージ登録解除の Intent の発行


Intent unregIntent = new Intent("com.google.android.c2dm.intent.UNREGISTER");
unregIntent.putExtra("app",PendingIntent.getBroadcast(this, 0, new Intent(), 0));
startService(unregIntent);

  ChromeToPhone Sample では
    registrationIntent.setPackage("com.google.android.gsf");
  も入ってた。


 ● メッセージ登録結果の処理

  ・REGISTRATION Intent は、登録が完了しなかった場合には、
   エラー・パラメータを生成する
    → アプリはメッセージ登録を再試行すべき
      (1秒後、2秒後、4秒後、8秒後、16秒後…といった、
       exponential back offで)

  ・REGISTRATION Intent エラー

    ・SERVICE_NOT_AVAILABLE
      デバイスが、レスポンスを読むことが出来ないか、
      サーバから500/503が返った時
        → アプリは、exponential back offのスタイルで
          再試行しなければならない

    ・ACCOUNT_MISSING
      デバイス上に、Google Account がない
 
    ・AUTHENTICATION_FAILED
      パスワードが違っている

    ・TOO_MANY_REGISTRATIONS
      ユーザが、あまりに多くのアプリを登録している
        → ユーザに他のいくつかのアプリの
          アンインストールを要求する

    ・INVALID_SENDER
      Senderのアカウントが認識できない

    ・PHONE_REGISTRATION_ERROR
      Googleへの電話の登録が正しくない
        =この電話は現在C2DMをサポートしていない


public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals("com.google.android.c2dm.intent.REGISTRATION")) {
handleRegistration(context, intent);
}
else if (intent.getAction().equals("com.google.android.c2dm.intent.RECEIVE")) {
handleMessage(context, intent);
}
}

private void handleRegistration(Context context, Intent intent) {
String registration = intent.getStringExtra("registration_id");

if (intent.getStringExtra("error") != null) {
// 登録に失敗したら、後で再試行する
}
else if (intent.getStringExtra("unregistered") != null) {
// 登録が解除されたら、新しいメッセージは拒否する
}
else if (registration != null) {
// アプリケーション・サーバにregistration IDを送る
// これは、別スレッドで行われるべき
// これが終わってはじめて登録作業は終了
}
}


ChromeToPhone だと、Retry 用の Intent の処理も入ってた

 } else if (intent.getAction().equals("com.google.android.c2dm.intent.RETRY")) {
  C2DMessaging.register(context, senderId);
 }


 

2010年9月19日日曜日

Android ScrollView + Dialog で横幅を window いっぱいにする

例えば、

 タイトル
 本文 (ScrollView 内に TextView)
 ボタン

というダイアログがあるとします。
Layout が

 LinearyLayout
  ScrollView
   TextView
  Button

になっているとします。
この場合、LinearyLayout, ScrollView, TextView で
layout_width="fill_parent" を指定すると
こんな残念な感じになります。



ちなみに xml は


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip"
android:gravity="center"
android:background="#666666"
>
<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
>
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:autoLink="web"
android:text="Libraroid v5.11.235\n\n対応図書館..."
/>
</ScrollView>
<Button
android:id="@+id/dialog_cancel_about"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_return"
android:layout_marginTop="10dip"
/>
</LinearLayout>



ここで、ScrollView を layout_width="wrap_content"
にします。

するとなぜかダイアログが画面の横幅いっぱいになります。謎。



xml はこうなる

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip"
android:gravity="center"
android:background="#666666"
>
<ScrollView
// fill_parent -> wrap_content
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_weight="1"
>
...
</ScrollView>
...
</LinearLayout>


こうすると、横幅いっぱいになってくれます。
layout_width="200dip" とか固定値にしても横幅は広くなりますが、
これのいいところは、画面の向きを横向きにした場合でもちゃんと
横長のダイアログになってくれるところです。
画面の解像度にもよらないので、マルチデバイスに対応するには
数字で指定するよりいいと思います。

画面いっぱいじゃなく、すこし隙間がほしいなと思って、
ルートの LinearLayout に layout_margin を設定したら
謎なことになりました。



グレーの部分が LinearLayout 部分。
なぜか右と下に padding っぽい隙間が!うーん謎。
ちなみに、xml はこんな感じ。


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="10dip"
android:gravity="center"
android:background="#666666"
// add layout_margin
android:layout_margin="20dip"
>
....
</LinearLayout>

Android ScrollBar (スクロールバー)の位置

ScrollView には android:scrollBarStyle という attribute があります。
これは、スクロールバーの出る場所を設定するもので、

ConstantValueDescription
insideOverlay0x0Inside the padding and overlaid
insideInset0x01000000Inside the padding and inset
outsideOverlay0x02000000Edge of the view and overlaid
outsideInset0x03000000Edge of the view and inset


が指定できます。

いまいち違いがピンとこなかったので、並べてみました。



黒が TextView の領域、水色が ScrollView の領域です。
outsideInset をよく使っていたけど、padding が十分なら
outsideOverlay でも大丈夫ってことがわかった。

# Froyo(2.2)からスクロールバー(の thumb)がタッチしてない時は消える
# ようになって、この画像をとるのに「4個いっぺんに触るとか無理だし!」
# ということでいろいろ調べてたら見つけました。
# 別エントリにしました。
# Android Froyo(2.2)でスクロールバーを常に表示する

ちなみに xml はこんな感じ


<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text='android:scrollbarStyle="insideOverlay"'
/>
<ScrollView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:scrollbarStyle="insideOverlay"
android:layout_weight="1"
android:scrollbars="vertical"
android:padding="10dip"
android:background="#ddddff"
android:fadeScrollbars="false"
>
<TextView
android:layout_width="300dip"
android:layout_height="fill_parent"
android:text="@string/waganeko"
android:background="#000000"
/>
</ScrollView>
...
</LinearLayout>


 
 

Android Froyo(2.2)でスクロールバーを常に表示する

Froyo(2.2)からタッチしてないとスクロールバーが消えるように
なってしまったんです。でも常に表示して置きたいときもあるわけで

 android:scrollbarAlwaysDrawVerticalTrack="true"
 android:scrollbarAlwaysDrawHorizontalTrack="true"

はダメです!

 android:scrollbarDefaultDelayBeforeFade
 (API 5)

を使います。も、対処療法で、正しくは

 android:fadeScrollbars="false"
 (API 5)

を使います。
こうすると消えません。adakoda さんありがとうございます!!!
# ちなみに、この attribute は http://developer.android.com/reference/android/view/View.html には載ってませんが、
# public void setScrollbarFadingEnabled (boolean fadeScrollbars)
# なら載っているというパターンのやつですw

android:scrollbarDefaultDelayBeforeFade は
その名のとおり、消え始める前の時間を milliseconds で指定します。
10秒表示するなら、
 
 android:scrollbarDefaultDelayBeforeFade="10000"

です。

ちなみに、

 android:scrollbarFadeDuration
 (API 5)

というのもあります。これは、消え始めてから消え終わるまでの時間です。
同じく milliseconds で指定します。

 android:scrollbarFadeDuration="10000"

なら10秒かけてゆっくりフェードアウトします。


# どうもタッチが外れてからの時間のようで、起動時はもっと長く
# 表示されました。
# scrollbarDefaultDelayBeforeFade の設定値をすごく長く指定すれば
# いいのでしょうが、絶対に消えない=常に表示する方法はないみたい

 

Android layout_weight チップス

とあるアプリのソースを見ていたら、こういう指定方法をしていたので

 android:layout_width="0dip"
 android:layout_height="wrap_content"
 android:layout_weight="1"

めもめも。width が 0 !

こんな感じで使う


<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<Button
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Left"
android:layout_marginTop="5dip"
/>

<Button
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Right"
android:layout_marginTop="5dip"
/>
</LinearLayout>


layout_weight を使うケースとしては

 android:layout_width="fill_parent"
 android:layout_height="wrap_content"
 android:layout_weight="1"

をよく使ってたので、試しに並べてみました。



、、、同じですね。

fill_parent にしたくない場合
(そういえば API 8 からは match_parent が推奨なんだった)
にはいいのかもね



 

Android 画面の向きを設定

画面の向き(縦/横)を設定するには、AndroidManifest.xml の
<activity> で screenOrientation を設定します。
landscape / portrait がよく知られてますが、それ以外もあります。


<activity
android:name=".MyActivity"
android:label="@string/app_name"
android:screenOrientation="portrait" >


"unspecified" : デフォルト値。システムが向きを決める。
        特定の状況によって向きが決定されるため、
        デバイスによって異なる可能性がある

"landscape" : 横向き(横長)

"portrait" : 縦向き(縦長)

"user" : ユーザーの現在の向き

"behind" : activity stack の直下の activity と同じ向き

"sensor" : 物理的なセンサー(加速度センサー)によって向きが決まる。
     向きはユーザーがどのようにデバイスを持つかに依存する。
     つまり、ユーザーがデバイスを回転すると向きが変わる。

"nosensor" : 物理的センサー以外の要素で向きが決まる。
      物理センサーは無視されるので、ユーザーがデバイスを動かしても
      画面は回転しない。この別を除いて、システムは "unspecified" を
      設定した時と同じポリシーで向きを決める。


# いままで知らなかったけど、behind ってのが結構便利そう

 

2010年9月16日木曜日

Android Cloud to Device Message - C2DM - 2

■ C2DMに必要なもの

 ・Mobile Device (デバイス)
   ・Marketアプリがインストールされている
   ・2.2以上のAndroidが走っている
   ・Googleアカウントと紐付いている
   ・C2DMを利用するAndroidアプリをインストールする

 ・Third-Party Application Server (アプリケーションサーバ)
   ・開発者が用意
   ・デバイス上のAndroidアプリに、C2DM Server経由でデータを送る

 ・C2DM Servers (C2DMサーバ)
   ・Third-Party Application Serverからメッセージを取り出し、
    それをデバイスに送る役割を果たすGoogleのサーバ


■ C2DMの認証に必要なもの

 ・Sender ID (Android app用)
   ・アプリの開発者にひもづいたemailアカウント
   ・デバイスにメッセージを送ることを許されたAndroidアプリの
    登録プロセスで利用される
   ・開発者ごとに異なる

 ・Application ID (Android app用)
   ・manifest中のパッケージ名
   ・Androidアプリごとに異なる

 ・Registration ID (Android app / Application Server用)
   ・メッセージを受け取ることを許しているAndroidアプリに対して、
    C2DMサーバから発行されるID
   ・特定のデバイス上の特定のアプリに結びついている
   ・デバイス上のAndroidアプリごとに異なる

 ・Google User Account
   ・ユーザーごとに異なる

 ・Sender Auth Token (Application Server用)
   ・Third-party application server 上に保存された
    ClientLogin Auth トークン
   ・メッセージを送るPOSTリクエストのヘッダに含まれる


■ C2DMへの登録

 1. REGISTER Intent で受信登録
  ・Register Intent
   (com.google.android.c2dm.intent.REGISTER)
   をC2DMサーバに送る
    ・register Intent は、Sender ID と Application ID を含む

 2. Registaration ID の取得
  ・登録成功時にC2DMサーバから発行される REGISTRATION Intent の
   ブロードキャストを受け取って、Registration ID を取得する
  ・Registration IDを更新ため、REGISTRATION Intent が複数回
   呼ばれることに対応する必要がある

 3. Registaration ID の送付
  ・Registration IDをアプリケーション・サーバに送る
  ・Registration IDは、アプリケーションが明示的に自分を登録から
   外した時か、GoogleがアプリのRegistration IDを更新する時まで
   存続する


■ メッセージの送信

 ・アプリケーション・サーバ
  ・一つの特定アプリ(=特定のデバイス上のアプリ)について
   一つのClientLogin tokenを持ち、複数のregistration IDを持つ
  ・それぞれのregistration IDは、特定のアプリについて、
   メッセージング・サービスを利用する特定のデバイスを表現している

 1. アプリケーション・サーバは、GoogleのC2DMサーバにメッセージを送る。

 2. Google側は、デバイスが立ち上がっていない場合には、メッセージを
  キューに入れて蓄えておく。

 3. デバイスがオンラインになったら、Googleは、デバイスにメッセージを送る。

 4. デバイス側では、システムが特定のアプリに対して適当なパーミッション
  設定をし、そのターゲットのアプリだけがメッセージを取得できるように、
  Intent Broadcastを使って、メッセージをブロードキャストする。
  メッセージを受け取るために事前に動いている必要はない。

 5. アプリは、メッセージを処理する。処理が複雑なものであれば、
  wake lockを取得して、バックグラウンドのServiceで処理する。


■ メッセージの受信

 1. システムは、送られたメッセージを受け取り、メッセージのpayloadから、
  生のkey/valueペアを直接に受け取る。

 2. システムは、ターゲットのAndroidアプリに、key/valueペアを、
  com.google.android. c2dm.intent.RECEIVE Intentの
  Extrasに詰めて渡す。

 3. Androidアプリは、RECEIVE Intentから、keyを用いて生のデータを
  取り出し、データを処理する。




■さっそく自分のアプリに組み込むべし

 1. ChromeToPhone を参考にして、My Google Chrome Extension を
  作成

   省略

 2. Chrome Extension から送られたメッセージを処理して、
  C2DMサーバへ送るためのアプリーション・サーバを
  Google App Engine 上につくる(別にGoogle App Engine
  じゃなくて自サバでも Amazon でもよし)

  ●必要な機能
   ・クライアントとコミュニケーションできること。

   ・C2DMサーバにHTTPリクエストを送れること

   ・リクエストを処理し、必要に応じてキューのデータを
    処理できること。例えば、exponential back offが可能である事。

   ・ClientLogin Auth tokenとクライアントのregistration IDを
    保存できること。

  ●メッセージを送る
   https://android.apis.google.com/c2dm/send に、
   POSTリクエスト
を送る


   POSTリクエストのヘッダー設定

    Content-Type : application/x-www-form-urlencoded

    Content-Length : Contentのバイト長

    Authorization : GoogleLogin auth=[AUTH_TOKEN]
      ac2dm サービスに関連するClientLogin Auth トークン


   POSTリクエストの Content に含まれるフィールド

    ・registration_id
      端末上のAndroidアプリから取得したregistration ID。必須。

    ・collapse_key
      デバイスがオフラインの時など、最後のメッセージだけが
      クライアントに送られるように、類似したメッセージの
      グループを折りたたむために使う、任意の文字列。
      端末がオンラインに復帰した時、沢山のメッセージが
      送られないためのもの。必須。
      メッセージの順序の保証はないので、本当に「最後」の
      メッセージではないかもしれないことに注意

    ・data. (optional)
      key/valueペアで表現されたPayloadデータもし、あれば、
      を持ったアプリケーション・データとして、Intentの中に
      含まれることになる。 オプショナル。
      key/valueペアの数に制限はないが、メッセージの全体の
      サイズには、制限がある。

    ・delay_while_idle (optioanl)
      デバイスが動いていないとき、メッセージをすぐには送らない
      ことを示す。サーバは、デバイスがアクティブになるのを待って、
      それぞれのcollapse_key毎に、その値の、最後のメッセージを
      送信する



   レスポンス(200)
    Header
    ・Update-Client-Auth=[Updated Auth Token]
      ・新しい Auth Token が発行された場合、この Field がレスポンスに入る

    Body
    ・id=[ID of sent message]

    ・Error=[error code]
      ・QuotaExceeded
         あまりに多くのメッセージを送っている。少し後に再試行せよ。
 
      ・DeviceQuotaExceeded
         特定のデバイスに、あまりに多くのメッセージを送っている。
         少し後に再試行せよ。

      ・InvalidRegistration
         registration_idが存在しないか、間違っている。
         このデバイスへのメッセージ送信を中止すべき。

      ・NotRegistered
         registration_idは、もはや有効ではない。
         例えば、ユーザがアプリをアンストールしたか、
         notificationを切った場合とか。
         このデバイスへのメッセージ送信を中止せよ。

      ・MessageTooBig
         メッセージのペイロードが大きすぎる。
         制限を確認して、メッセージのサイズを小さくせよ。

      ・MissingCollapseKey
         Collapse keyは、必須である。リクエストに
         Collapse keyを含めよ

   レスポンス(503)
     サーバは、一時的に利用できない。後で、レスポンス中の
     Retry-Afterヘッダを使って、再試行する。
     アプリケーション・サーバは、exponential back off を実装すべきしなければならない。
     問題をおこしたサーバは、ブラックリストに載せられる危険もある。

   レスポンス(401)
     ClientLogin AUTH_TOKEN が、正しくない。


 3. Android アプリを作る

  長くなったので次のエントリで。



 

2010年9月15日水曜日

Google Chrome Extension タブの情報を取得

Google Chrome Extension で、extension をクリックされたときなどに
今開いているタブの情報(ページのURL, タイトルなど)を取得するには

chrome.tags.* API

を使います

http://code.google.com/chrome/extensions/tabs.html

1. manifest.json で permission に tabs を追加します

{
"name": "My extension",
...
"permissions": [
"tabs"
],
...
}

 Samples
 Filter by API に chrome.tabs を選択する


2. JavaScript で API を使う


2.2 getSelected

 全面に出ている(=選択されている)タブの Tab Object を取得するには
 
  chrome.tabs.getSelected(integer windowId, function callback)

 を使います。

 function callback は function(Tab tab){ ... } のように引数で
 Tab Object を受け取ることができます。 

 例えば、選択されているタブが Amazon のページのときだけ
 タイトルを表示する場合は、こんな感じ



chrome.tabs.getSelected(null, function(tab) {
if (tab.url.indexOf('http://www.amazon.co.jp') == 0 ||
tab.url.indexOf('https://www.amazon.co.jp') == 0) {
// Amazon のページのときの処理
document.getElementById('msg').innerHTML = tab.title;
}
else {
// Amazon 以外のページ
document.getElementById('msg').innerHTML = "";
}
}


 引数で受け取った Tab Object は次の Types を保持しています

  ・id (integer)
    タブのID, ブラウザセション内で一意

  ・index (integer)
    window内のゼロから始まるタブindex

  ・windowId (integer)
    タブが含まれているwindowのID

  ・selected (boolean)
    タブが選択されているかどうか

  ・url (string)
    タブのURL

  ・title (optional string)
    タブのタイトル、タブがロード中の場合使えないことがある

  ・favIconUrl (optional string)
    タブのfavIconのURL、タブがロード中の場合使えないことがある

  ・status (optional string)
    ステータス、loading or complete

  ・incognito (boolean)
    タブが incognito window かどうか


2.3 create

 タブを生成するには

  chrome.tabs.create(object createProperties, function callback)

 を使います

 createProperties には windowId (optional integer) /
 index (optional integer) / url (optional string) /
 selected (optional boolean) が指定できます。

 function callback ( function(Tab tab){ ... } )で
 生成された Tab Object を受け取ることができます。 
 

2.4 remove

 タブを削除するには

  chrome.tabs.remove(integer tabId, function callback)
 
 を使います。

 削除するタブの Tab ID を引数で指定します。

 function callback は function(){ ... } のように引数なしです


2.5 get

 特定のタブの Tab Object を取得するには

  chrome.tabs.get(integer tabId, function callback)

 を使います

 function callback は function(Tab tab){ ... } のように引数で
 Tab Object を受け取ることができます。 


2.6 executeScript

 script を実行するには

  chrome.tabs.executeScript(integer tabId, object details, function callback)

 を使います

 JavaScript コードをページに inject します。
 詳しくは programmatic injection

 tabId で script を実行するタブを指定します。
 デフォルトは現在の window の selected tab。
 特定のタブで実行しない場合は null を指定。

 details には code (optional string) / file (optional string)
 / allFrames (optional boolean) が指定できます。

 こんな感じ

chrome.tabs.executeScript(null, {file: "content_script.js"});

2010年9月14日火曜日

Google Chrome Extension メモ

Google Chrome Extension は、実はただの zip file

必要なのは

 ・manifest file (manifest.json)

 ・1つ以上の HTML file

 ・必要なら CSS files, JavaScript files, 画像など

を一つのフォルダに入れて、 .zip にして .crx に変更するだけ。
ローカルでテストするなら、zip する必要もない。

基本的には chrome.* APIs を使う

 chrome.* APIs

それ以外にもさまざまなAPIが使える

 Other APIs

 例えば
  ・XMLHttpRequest
  ・HTML5 and other emerging APIs
  ・WebKit APIs 
 などなど

ファイルを参照する場合は相対パスで

<img src="images/myimage.png">


Google Chrome debugger などを使っていて absolute URL にアクセスしたい場合は

 chrome-extension://<extensionID>/<pathToFile>


自分のロードした extensions の ID は

 chrome://extensions

から確認できる


● manifest file の例


{
"name": "My Extension",
"version": "2.1",
"description": "Gets information from Google.",
"icons": { "128": "icon_128.png" },
"background_page": "bg.html",
"permissions": ["http://*.google.com/", "https://*.google.com/"],
"browser_action": {
"default_title": "",
"default_icon": "icon_19.png",
"default_popup": "popup.html"
}
}


詳しいフォーマットは Formats: Manifest Files



参考ページ

 ・Tutorial: Getting Started (Hello, World!)

 ・http://code.google.com/chrome/extensions/overview.html

 ・Samples



メモ
 ・http://gihyo.jp/dev/serial/01/chrome-extensions
 
 ・http://www.html5.jp/library/html5detector.html

 ・http://www.serendip.ws/archives/3823

 ・http://blog.smartnetwork.co.jp/staff/node/44

 ・http://dev.screw-axis.com/doc/chrome_extensions/

 ・http://gihyo.jp/dev/feature/01/chromeExt

 

2010年9月12日日曜日

HTML5 canvas上のイベントの位置を取得する

イベントの位置を

 var x = event.clientX ;
 var y = event.clientY ;

でとると、ウインドウのドキュメントを表示している部分の
左上が原点になるので、スクロールするとずれてしまいます。
それに、canvas の左上からの相対座標になりません。
なので、

 canvasOffsetX = canvas.offsetLeft;
 canvasOffsetY = canvas.offsetTop;
 var x = event.clientX - canvasOffsetX;
 var y = event.clientY - canvasOffsetY;

としたのですが、これだと一番上までスクロールしている
状態ならOKですが、下にスクロールするとずれてしまいます。
それで、次に

 canvasOffsetX = canvas.offsetLeft;
 canvasOffsetY = canvas.offsetTop;
 var x = event.pageX - canvasOffsetX;
 var y = event.pageY - canvasOffsetY;

とすると、スクロールしてもOKになりました。
ただし、pageX/pageYはW3C規格では定義されていないし、IEではサポートされていないそうです。
(IE以外の主要なブラウザすべてでサポートされているらしい)

で、"本の虫: DOM level 3のマウスイベントにおけるカーソル位置の詳細"
を参考にさせていただいて、こうなりました。ありがとうございます。

 var rect = event.target.getBoundingClientRect() ;
 var x = event.clientX - rect.left;
 var y = event.clientY - rect.top;


 

2010年9月11日土曜日

HTML5 canvas でピクセル処理

canvas の各ピクセルに対して処理を行って、さまざまなエフェクトを実現するには、

 getImageData(x0,y0,x1,y1)

を使います。
例えば、
 var image = ctx.getImageData(0,0,w,h);

とした場合、image にはピクセル単位の集合体がオブジェクトとなったもの(イメージデータ)が返ってきます。
このオブジェクトは、「data」というプロパティを持っていて、ここでピクセルデータを保持しています。

「data」プロパティの配列には、各ピクセルのRGBAデータが

 {R, G, B, A, R, G, B, A, ...}

の順番で格納されています。(R = Red, G = Greed, B = Blue, A = Alpha)
これがピクセル分だけ続きます。つまり配列の要素数は ピクセル数 x 4 になります。

例えば、Alpha を徐々に変化させる場合はこんな感じ


var canvas = document.getElementById('canvas');
if ( ! canvas || ! canvas.getContext ) { return false; }
ctx = canvas.getContext("2d");

var w = canvas.width;
var h = canvas.height;

ctx.fillStyle = 'rgb(224, 224, 240)';
ctx.fillRect(0,0,w,h);

var step = 265 / (h * 1.5);

var image = ctx.getImageData(0,0,w,h);
for(var i=3; i<image.data.length ; i+=4) {
var alpha = 255 - ((i/4/h) * step);
if(alpha < 0) alpha = 0;
image.data[i] = alpha;
}
ctx.clearRect(0,0,w,h);
ctx.putImageData(image,0,0);



モノクロ(monochrome)はこんな感じ

var canvas = document.getElementById('canvas');
if ( ! canvas || ! canvas.getContext ) { return false; }
ctx = canvas.getContext("2d");

var w = canvas.width;
var h = canvas.height;

ctx.drawImage(canvas, 0, 0);
var image = ctx.getImageData(0,0,w,h);
var i = image.data.length;

for(var y = h; y>0; y--){
for(var x = w; x>0; x--){
i -= 4;
var c = 0.298912 * image.data[i] + 0.586611 * image.data[i+1] + 0.114478 * image.data[i+2];
image.data[i] = image.data[i+1] = image.data[i+2] = parseInt(c);
}
}
ctx.clearRect(0,0,w,h);
ctx.putImageData(image,0,0);




 
 

HTML5 canvas の注意点??

めもめも

1. Canvas要素に対して、JavaScript のコーディングが誤っている場合、ブラウザによって行う対処が異なるため、それぞれ違った結果になることがある。

2. ビットマップ操作は重い処理なので、高い解像度におけるアニメーション処理などは注意が必要。できるだけピクセルを使い回して、必要な部分だけを書き換える。つまり、必要なピクセルだけをclearRect(x,y,w,h)でクリアして再描画する。

3. saveとrestoreという2つの方法で一旦状態を保存し、保存した状態からトランスフォーム処理(回転やスケーリング)を実施することで複数のトランスフォーム処理が実施できる。

4. コンポージットモードを使って、マスクやレイヤの機能を実現できる。

5. Canvasの幅と高さを設定すると、たとえ現在の長さと同一である場合でもCanvasはリセットされる。つまりCanvasのサイズを変更すると中身が消えてしまう。


参照先 : http://ajaxian.com/archives/real-world-canvas-tips via http://journal.mycom.co.jp/articles/2010/08/26/html5-canvas-thoughts-from-flash/index.html

 

2010年9月9日木曜日

Google App Engine はじめました

# やらないとなぁ、と思いつつ、、、ようやく始めました。

Google App Engine

App Engine を使うと、
  ・独自の Web アプリケーションが作れる!
  ・自分でサーバーを維持管理する必要がない!
  ・トラフィックやデータ ストレージの増大に合わせて
   容易にスケーリングが可能
  ・無料で始められる(無料の割り当て(ストレージ 500 MB、
   月間 500 万ページ ビュー)を超えると課金されるけど。。。)
  (無料割り当てを超えた場合の料金体系はこちらこちら

 公式ブログ Google App Engine Blog

言語は
 ・Java
 ・Python


スタート ガイド
 1. App Engine アカウントを登録

  ここから登録


 2. App Engine SDK をダウンロード

  ここからダウンロード

  2.1 SDK をダウンロード

  Python用 と Java用 があります。

   - Google App Engine SDK for Python
   - Google App Engine SDK for Java

  今回は Python にしちゃいます!

  Windows や Mac はインストーラが走るのでその流れで
  インストールすればOK

  のぱそは Ubuntu なので、Linux版 をダウンロードして
  適当な場所に解凍します。
  

 3. スタート ガイド: Python

  Windows だと Google App Engine Launcher からすべての
  コマンドが実行できるので楽です。

  linux版はコンソールから .py をぽちぽちです。
  これがいいんだよね。
  # 解凍したディレクトリにパスを通しておくと楽

  開発用の Web サーバーを稼働するスクリプトが dev_appserver.py
  
  > dev_appserver.py appli_name
  ...
  INFO 2010-09-09 14:03:16,394 dev_appserver_main.py:431] Running application appli_name on port 8080: http://localhost:8080
  ...
  
  とでれば成功。
  http://localhost:8080 でアプリをテストできます。


 4. アプリケーションのアップロード

  注: App Engine にアップロードしたアプリケーションを削除する
    手段は提供されていません。
    #知らなかった。。。

  App Engine Web アプリケーションの作成と管理は、App Engine
  管理コンソールから行います。

   http://appengine.google.com/

  Google アカウントで App Engine にログインします。

  新しいアプリケーションを作成するには、[Create an Application]
  ボタンをクリックします。

  アプリケーションの一意名であるアプリケーション ID 登録します。
  無償で利用できる appspot.com のドメイン名を選択すると、
  アプリケーションの URL は http://application-id.appspot.com/
  になります。

  app.yaml ファイルを編集し、application: 設定の値を、helloworld
  から登録したアプリケーション ID に変更します。

  アプリケーションをアップロードします。

   appcfg.py update helloworld/

  プロンプト画面で、Google のユーザー名とパスワードを入力します。

  これで

   http://application-id.appspot.com

  にアプリケーションがアップロードされました!




 

HTML5 はじめました。

はじめまして HTML5

いままでほっといてごめんね

まずはここからだよね
HTML5.jp

タグリファレンス | HTML5.jp

■ Canvas

 HTML5 と言えばなんといっても Canvas!

 Canvasとは | HTML5.jp

 "Canvasは、もともとはAppleが発祥で、当初、Mac OSでおなじみのDashboardで使われた技術仕様です。"

 ほぉー。知らなかった。

 "Canvasに関しては、Safari1.3以降、Opera9以降、Firefox1.5以降ですでに実装されています。"

 ふむ。

 "もっともブラウザシェアが高いInternet ExplorerはCanvasに対応していません。"
 "GoogleがExplorerCanvasというJavaScriptライブラリを公開"
 "ExplorerCanvasは、Internet ExplorerでCanvasをエミュレートするJavaScriptライブラリ"
 "ExplorerCanvasは、Internet Explorer 6以上で動作"

 これを使うと、あたかも Internet Explorer に Canvasが実装されて
 いるかのようにすることができるようになるのね。

 "Canvasの仕様には、アニメーションのメソッドがありません。もしアニメーションを実現したい場合は、一コマずつ、図を描きなおすという処理を繰り返さなければいけません。"

 はい。


 ● ツール

  リアルタイムにコード結果をみれる

   http://jsdo.it/

  を使いました。

   http://www.rendur.com/
  
  でもできるのかな??

 ● 書いてみる

  Canvasの使い方 | HTML5.jp

  Canvasリファレンス | HTML5.jp

  ★ 図形の描画

/* 2Dコンテキスト */
var ctx = canvas.getContext('2d');

/* パスの開始 */
ctx.beginPath();

/* 移動 */
ctx.moveTo(x, y);

/* 線を描画 */
ctx.lineTo(x, y);

/* パスを閉じる */
ctx.closePath();

/* 枠線を描画 */
ctx.stroke();

/* 塗りつぶし */
ctx.fill();

/* 矩形を描画 */
ctx.rect(x1, y1, x2, y2);

 /* 円弧を描画: 0[rad] は x座標 */
ctx.arc(x, y, radius, startAngle[rad], endAngle[rad], anticlockwise[true/false]);

/* 二次曲線 */
ctx.quadraticCurveTo(x1, y1, x2, y2);

/* ベジェ曲線 */
bezierCurveTo(x1, y1, x2, y2);

/* 塗りつぶした矩形 */
ctx.fillRect(x, y, width, height)

/* 枠線のみの矩形 */
ctx.strokeRect(x, y, width, height)

 /* 矩形を削除 : 削除された部分は透明になる */
ctx.clearRect(x, y, width, height)



  ★ 色の指定

/* 枠線の色を指定 : CSSで指定するフォーマットに対応 */
ctx.strokeStyle = color

/* 塗りつぶす色を指定 : CSSで指定するフォーマットに対応 */
ctx.fillStyle = color

/* 例 rgb(int red, int green, int blue) */
ctx.fillStyle = 'rgb(192, 80, 77)';

/* アルファ(透明度)を指定する */
/* rgba(int red, int green, int blue, int alpha) */
/* alpha : [0-1] 0:透明 1:不透明 */
ctx.fillStyle = 'rgba(192, 80, 77, 0.7)';

/* Canvas上で合成される前に図形や画像に適用するためのアルファ値をセット */
ctx.globalAlpha = 0.7;


globalAlpha の使い方

var ctx = canvas.getContext('2d');

/* 半透明度を指定 */
ctx.globalAlpha = 0.7;

/* 円 */
ctx.beginPath();
ctx.fillStyle = 'rgb(192, 80, 77)'; // 赤
ctx.arc(70, 45, 35, 0, Math.PI*2, false);
ctx.fill();
/* 矩形 */
ctx.beginPath();
ctx.fillStyle = 'rgb(155, 187, 89)'; // 緑
ctx.rect(20, 20, 50, 50);
ctx.fill();



  ★ 線の幅を変える

/* 線の幅を変える */
ctx.lineWidth(width);



  ★ 線形グラデーションの指定

  CanvasGradient = ctx.createLinearGradient(x0, y0, x1, y1);
  CanvasGradient.addColorStop(offset, color);

  createLinearGradient(x0, y0, x1, y1)
    x0, y0 : グラデーションの開始地点
    x1, y1 : グラデーションの終了地点

  addColorStop(offset, color)
    グラデーション全体から見たoffsetの地点で、新たな終点セットする

    例外 :
    offsetが0未満または1より大きい値の場合: INDEX_SIZE_ERR例外

    colorがCSS色として構文解析できない場合: SYNTAX_ERR例外

  注意: Safari 2.0の場合、fillRectメソッドで描画される図形には
     グラデーションが効かないという問題があるそうです。
     なので、x fillRect() o drawRect() + fill()


  ★ 円形グラデーションの指定
  
  anvasGradient = ctx.createRadialGradient(x0, y0, r0, x1, y1, r1);
  anvasGradient.addColorStop(offset, color);
  
  createRadialGradient(x0, y0, r0, x1, y1, r1)
    x0, y0 : 1つ目の円の中心座標
    r0 : 1つ目の円の半径
    x1, y1 : 2つ目の円の中心座標
    r1 : 2つ目の円の半径
  

  ★ 画像を組み込む

  ctx.drawImage(image, dx, dy)
   image に格納されたImageオブジェクトを描画

   dx, dy : 左上端

  ctx.drawImage(image, dx, dy, dw, dh)
   image に格納されたImageオブジェクトを dw x dh に伸縮して描画

   dx, dy : 左上端
   dw, dh : 横幅、縦幅

  ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh)
   image に格納されたImageオブジェクトを、トリミング&伸縮して描画

   sx, sy : トリミング開始位置
   sw, sh : トリミング横幅、縦幅
   dx, dy : 左上端
   dw, dh : 横幅、縦幅


   !drawImageメソッドを呼び出す前に、画像のロードが
    完了するようにしなければいけない


var ctx = canvas.getContext('2d');
/* Imageオブジェクトを生成 */
var img = new Image();
img.src = "image2.gif?" + new Date().getTime(); /* for IE */
/* 画像が読み込まれるのを待ってから処理を続行 */
img.onload = function() {
ctx.drawImage(img, 0, 0);
}


  ★ 変形

  ・scale()

  ・rotate()

  ・translate()

  ・transform()

  ・setTransform()




■ 関連サイト&資料

http://www.slideshare.net/myakura/html5-web-platform

HTML5 Japanese Interest Group

HTML5の機能を知るための15のデモンストレーション

これは見ておくべきHTML5のCanvasを使ったデモ集:phpspot開発日誌

http://9elements.com/io/projects/html5/canvas/

Mozilla Developer Network

「A Basic RayCaster」

Puzzles using Canvas

Canvascape - "3D Walker"

Apple - HTML5 and web standars

[html5] - 超自己満足プログラミング -

Compatibility tables for features in HTML5, CSS3, SVG and other upcoming web technologies | caniuse.com


■ GTUG での HTML5 セッションのときのメモ(いつのか忘れちゃった ><)

 ● HTML5 の特徴

  ・セマンティックな構造化言語へと進化
  ・フォーム機能の大幅な強化
  ・Web アプリケーション開発向け機能の強化


 ● セマンティックってなに?

  HTML5では情報を構造化するための新しい要素や属性が追加されている。

  例えば、以下の要素などが挙げられる。
   ・文章などのセクションを定義する「section 要素」
   ・ヘッダエリアを定義する「header 要素」
   ・フッタエリアを定義する「footer 要素」
   ・ナビゲーションエリアを定義する「nav 要素」

 ● フォーム機能の大幅な強化

  HTML5では、問い合わせや商品購入などでユーザーが情報を
  入力する際に用いるフォーム機能が、大幅に強化されている。

  例えば、以下のような強化点が挙げられる。
   ・必須入力フィールドかどうかを指定することができる「required 属性」
   ・未入力のテキストフィールドに、文字入力の手助けとなる文字列を
    あらかじめ表示できる「placeholder 属性」
   ・入力を開始する箇所を自動的にフォーカスすることができる
    「autofocus 属性」


■メモ

 ・noVNC | github
 
 ・http://www.hunlock.com/

 ・http://www.akibahideki.com/blog/html5-canvas/

 ・HTML5 Canvas Cheat Sheet

 ・HTML5 Gallery | A showcase of sites using HTML5 markup

 ・Dive Into HTML5

 ・HTML5 Canvas and Audio Experiment

 ・HTML 5 Cheat Sheet

 ・Modernizr

 ・jQuery Visualize Plugin: Accessible Charts & Graphs from Table Elements using HTML 5 Canvas
 
 ・70 Must-Have CSS3 and HTML5 Tutorials and Resources | Web Resources | WebAppers


 

2010年9月8日水曜日

Android Cloud to Device Message - C2DM -

ようやく C2DM やってみようかなーと思って、、、

http://android-developers.blogspot.com/2010/08/powering-chrome-to-phone-with-android.html

■C2DMのデモサンプルとして Chrome to Phone というのがあります。

 Google Chrome の Extension として、開いているページの URL を
 端末に送るというデモです。

 上記blogによると、このアプリの流れはこうなっている。

  1. Android アプリが C2DM サービスにレジスト
    → registration ID を取得

  2. Andorid アプリが registration ID とユーザーの
   アカウント名を AppEngine サーバーに送る

  3. AppEngine サーバーはユーザーアカウントを認証し、
   アカウント名と device registration ID のマッピングを保存する

  4. The Chrome Extension は現在のタブのURLとページタイトルを
   AppEngine server にPOSTする

  5. AppEngine サーバーはユーザーを認証し、ユーザー名に対応する
   device registration ID を探す

  6. URL とタイトルを Google's C2DM サーバーに HTTP Post する

  7. Google's C2DM サーバーはメッセージを端末にルーティングし、
   端末で Intent がブロードキャストされる

  8. Android アプリは Intent receiver によって起動する

  9. Android アプリは 新しいIntent(ブラウザやダイアラーや
   Google Maps) を発行して、URL を適切なアプリに渡す


 このアプリでは、特筆すべきデザイン選択として URL や title などのペイロードを push message の一部として送っている。URL のハッシュは、複数のボタンが押されたことによる intent の重複を防ぐための collapse_key として使われている。原則として、URL全体が使われているが、ハッシュはより短くされ、不必要なペイロードデータを公開するのを避けている。

 別のアプローチとして(確かに大きいペイロードには、こっちのほうが望ましい)
アプリケーションを立ち上げるトリガーとして、push message サービスを使う方法がある。アプリケーションが立ち上がった後に、HTTP通信などで out-of-band のペイロードを続けて受信するようにすればよい。


■早速使ってみました

まず、Google Chrome に Chrome to Phone Extension を入れます。

入れると、こんな感じ。「端末+黄色い矢印」のアイコンです。



アイコンをクリックすると、Google Account にログインしていない場合は、
ログインを求められます。

ログインすると、次の Help ページになります。



次に Android 端末の方にアプリを入れます。

上記の Help ページに、「Search for 'Chrome to Phone' in Market on your Android device.」って書いてあったので Android Market で探したのですが見つからない。。。

で、よくよく見たら

Google Chrome to Phone Extension のサイトには

"The Android app is currently only available in English in the following countries: US, Canada, Australia, Great Britain, Ireland, New Zealand, and South Africa. We will be extending to additional countries in the next few weeks."

とあるので、日本はまだみたいです。

そこで、google code から chrometophone-android-v1.7.apk をダウンロードして

> adb install chrometophone-android-v1.7.apk

で端末にインストール

左上のアプリです。



アプリを起動して、初期設定


紐付ける Google Account を選択


アプリが自動的に起動するかどうか選択


おしまい



あとは、端末に送りたいページを表示した状態で Chrome to Phone Extension
のアイコンをクリックすれば、端末のブラウザがかってに立ち上がって
そのページを表示しちゃうんです。おおー。



関連サイト

 ・Google Projects for Android: C2DM (Labs)

 ・上記サイトを丸山先生が翻訳した資料
  #ありがとうございます!

 ・Google Chrome Blog: Instantly zap links, maps and phone numbers to your Android phone with Chrome to Phone!

 ・chrometophone - Project Hosting on Google Code

 ・Google Chrome to Phone Extension - Google Chrome 拡張機能ギャラリー

2010年9月2日木曜日

Docomo SmartPhone Lounge のイベントにいってきました。

8/29(日)に有楽町の Docomo SmartPhone Lounge
イベントに登壇させていただきました。

blackberry がたくさん置いてあって、
思わずあれこれいじっていたら、お店の方が
丁寧に説明してくれました!

他にも Xperia のデザインカバーがずらっと100個あったり
自由に触れる Lynx が何台もありました。

奥の方に20席程度のイベントスペースがあって、
そこで ユーザー x 開発者 x 女性 目線で発表させて
いただきました。

ありがとうございました。





外観。ガラス張りでおしゃれ。




スマホをウィンドウショッピング






blackberry 

ブラインドタッチは無理でした。私にはちょっとボタン小さいなー。

隣のボタンも押しちゃう >< 爪が伸びてる人ならいいのかも。





はじまる前~

2010年9月1日水曜日

Android 「The world of ListView」 - Gotchas and don’ts -

Google I/O の 「The world of ListView」というセッションを見たので
そのまとめ。

長いので、項目ごとに分けました。
今回は 「Gotchas and don'ts」

--------------------------------------

Google I/O の「The world of ListView」のページは こちら

セッションのスライドはこちら

 agenda

  • Virtualization and adapters

  • Item properties

  • Headers and footers

  • List selectors

  • Other features

  • Gotchas and don'ts


--------------------------------------

■ Gotchas and don'ts (落とし穴とやっちゃいけないこと)

# gotchas って単語しらなかった。「落とし穴」なんだね。

 * Gotcha(落とし穴)

  ・リストが真っ黒になった?!
    • 最適化に便利なので...
      – スクロール中の view が bitmap でキャッシュされてるとき
      – ブレンディングを避けるために不透明の bitmap を使っている
    • 解決方法
      – android:cacheColorHint="#00000000"
      – android:cacheColorHint
         ="@color/myBackgroundColor"

  ・スクロールバーのサイズが変わった?!
    • view がすごく異なる高さを持つときにおこる
    • Smooth scrollbar は各要素(item)の高さが必要
      – Too expensive(コストかかりすぎ!)
    • 解決方法
      – android:smoothScrollbar=”false”



 * Don't (やっちゃいけないこと)

  ・android:layout_height=”wrap_content”
    • ListView は仮想化されている
    • wrap_content = 子要素の最大
      – ListViewは均等サイズの子要素に対応
      – 数千個の子要素を測る?
    • Android framework cheats
      – 3個の子要素だけ測る
      – それでもコストかかりすぎ
    •間違った結果になる

  ・ScrollView の中に ListView を入れる
    • ScrollView がスクロールする
    • ListView がスクロールする
    • どれがスクロールする?

  ・adapter に view をキャッシュする
    • ListView の裏をかこうとしない
      – ListView は view の所有権とコントロールを前提としている
    • 複雑な再利用メカニズム
      – 多数の最適化
      – 我々も時にはそこにバグを残してしまう
    • 最悪の場合はアンデッド
      – 再利用者と画面の両方に view

  ・必要でないときに ListView を使う
    • ListView は...
      – 再帰的な、境界のないデータ用
      – (たくさんの)複雑さが加わる
    • LinearLayout や ScrollView では置き換えられない?


   

Android 「The world of ListView」 - Other features -

Google I/O の 「The world of ListView」というセッションを見たので
そのまとめ。

長いので、項目ごとに分けました。
今回は 「Other features」

--------------------------------------

Google I/O の「The world of ListView」のページは こちら

セッションのスライドはこちら

 agenda

  • Virtualization and adapters

  • Item properties

  • Headers and footers

  • List selectors

  • Other features

  • Gotchas and don'ts


--------------------------------------

■ Other features

 * Transcript and stack from bottom

   • android:transcriptMode
     – コンテンツが変わったときのリストの振る舞い
     – "disabled", スクロールしない
     – "normal", 最後の要素が見えているなら、底部までスクロールする
     – "alwaysScroll", いつも底部までスクロールする

   • android:stackFromBottom
     – 要素を逆順にスタックする
     – adapter の最後の要素からスタートする

   • チャットなどにいい
     – Messaging, Talk, IRC, etc.




 * Text filter

   • android:textFilterEnabled="true"
   • adapter は Filterable を実装する必要がある
     – CursorAdapter 、 ArrayAdapterなど
     – getFilter() を実装する
   • Filter を実装する


// Filters the content of the adapter on a worker thread
protected FilterResults performFiltering(CharSequence prefix)

// Displays the results on the UI thread
protected void publishResults(CharSequence constraint, FilterResults results)


   CursorAdapter や ArrayAdapter のコードをみるのがいいらしい