2010年10月28日木曜日

Android dip, dp, から pt, px に変換する

■ Converting from dips to pixel

Android refenrece の Converting from dips to pixels
に書いてある

  getContext().getResources().getDisplayMetrics().density

で画面解像度の比率を取得して、dip, dp 単位の値に掛ければ
pt, px 単位での値になる

dip -> px
// The gesture threshold expressed in dip private static final float GESTURE_THRESHOLD_DIP = 16.0f; // Convert the dips to pixels final float scale = getContext().getResources().getDisplayMetrics().density; mGestureThreshold = (int) (GESTURE_THRESHOLD_DIP * scale + 0.5f); // Use mGestureThreshold as a distance in pixels

px -> dip は反対に px 単位の値を
getContext().getResources().getDisplayMetrics().density
で割れば OK

android.util.DisplayMetrics.density が(その端末での) dip - pixel 間の変換用 scale factor

medium (160) density screen のとき、DisplayMetrics.density は "1.0" になり、high (240) density screen だと "1.5" になる


■ Use pre-scaled configuration values

ViewConfiguration は、Android framework で使われる一般的な距離、速度、時間のアクセスするためのクラス

例えば、framework で scroll threshold として使われる距離(ピクセル単位)は次のようにして取得できる
ViewConfiguration.get(aContext).getScaledTouchSlop()

getScaled で始まるメソッドは、現在の画面解像度に関係なく、適切に表示するためのピクセル単位の値を返す


Android Expandable List で group を開いたときに自動でスクロールする



この状態で一番したの「夏目漱石」をタップすると、
child は下に隠れて見えなくなってしまう



勝手にスクロールして、child が見えるようになってくれる
ようにするには、

 public void smoothScrollToPosition (int position)

を使う

注: API Level 8






ExpandableListView el = getExpandableListView();
el.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {

@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
((ExpandableListView)getExpandableListView()).smoothScrollToPosition(groupPosition);
return false;
}
});
setContentView(el);

2010年10月26日火曜日

Android ListView で区切り行をつくる

Q. ListView の 区切り行 * はどうやってつくるんですか?

A. isEnabled を Override して、 getView で 区切り行のときと、そうでないときで返す View を変えます

* 区切り行とは、Android Market アプリの 緑の区切りのようなやつのことです。ListView の1行ですが、タップしても反応しません。







public class ExtendsArrayAdapterSample2 extends ListActivity {

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

List<bindData> data = new ArrayList<bindData>();
data.add(new bindData("Clock", R.drawable.alermclock));
data.add(new bindData("-", 0));
data.add(new bindData("Camera", R.drawable.camera));
data.add(new bindData("-", 0));
data.add(new bindData("Book", R.drawable.bookopen));
data.add(new bindData("-", 0));
data.add(new bindData("Calculator", R.drawable.calculator));
data.add(new bindData("-", 0));

MyAdapter adapter = new MyAdapter(this, R.layout.list_item5, data);
setListAdapter(adapter);
}

class bindData {
String text;
int imageResourceId;

public bindData(String text, int imageResourceId) {
this.text = text;
this.imageResourceId = imageResourceId;
}
}

static class ViewHolder {
TextView textView;
ImageView imageView;
}

public class MyAdapter extends ArrayAdapter<bindData> {
private LayoutInflater inflater;
private int layoutId;

public MyAdapter(Context context, int layoutId, List<bindData> objects) {
super(context, 0, objects);
this.inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.layoutId = layoutId;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;

if (convertView == null) {
convertView = inflater.inflate(layoutId, parent, false);
holder = new ViewHolder();
holder.textView = (TextView) convertView
.findViewById(R.id.textview);
holder.imageView = (ImageView) convertView
.findViewById(R.id.imageview);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}

if(!isEnabled(position))
convertView.setBackgroundColor(Color.BLACK);
else {
bindData data = getItem(position);
holder.textView.setText(data.text);
holder.imageView.setImageResource(data.imageResourceId);
}

return convertView;
}

@Override
public boolean isEnabled(int position) {
return !(getItem(position).text.startsWith("-"));
}
}
}





 

2010年10月25日月曜日

Android Improving App Quality

ざっとまとめてみました。

http://android-developers.blogspot.com/2010/10/improving-app-quality.html

1. Listen to your users

 ・ Android Market にはユーザーとデベロッパーの双方向のコミュニケーション方法が提供されていないので、自分でサポートを用意すべき。例えば、 Google Group, Zoho Discussions, getsatisfaction.com, uservoice.com などのすぐれたツールを使うことができる。もし、このようなツールを使ってサポートを始めた場合、ここへのリンクを Android Market やアプリ内に入れること。

 ・ public beta や trusted tester program をする方法もある。website に apk を置いて、そのページに認証をいれるなどすればいい。


2. Improve stability and eliminate bugs

 ・ Monkey テストを行う UI/Application Exerciser Monkey (aka Monkey)

 ・ error reporting feature が Android 2.2 から提供されている。デベロッパーは Android Market developer console から確認できる。

 ・ 外部のバグや、機能追加のリクエストなどをトラックできるようにする。もし、プロジェクトがオープンソースなら、 Google CodeGitHub などの repository hosting sites を使えば、これらの機能を利用することができる。


3. Improve UI Responsiveness

 ・ 遅い UI だと、ユーザーは離れて行ってしまう。(speed matters として、研究で示されている)

 ・ 時間がかかる処理をアプリケーションの main thread からはずず in Google I/O 2010 talk, Writing Zippy Android Apps

 ・ UI のパフォーマンスを改善するには、レイアウトの複雑さを最小化する。 hierarchyviewer でレイアウトの階層をチェックし、5階層以上あるなら、もっと単純化する必要がある。

 ・ View objects は約 1 ~ 2 KB のメモリを消費する in Google I/O talk, World of ListView by Romain Guy。よって深い階層は頻繁な VM garbage collection の元凶になり、main (UI) thread をブロックしてしまう。

 ・ traceview や ddms を使って VM memory allocations をモニターする

 ・ Designing for Performance

 ・ Designing for Responsiveness
 

4. Improve usability

 ・ 実際のユーザーに聞くのが一番!

 ・ 家族や友人に試してもらう。操作に戸惑ったら改善の余地あり

 ・ user interface patterns the Android UI team discussed at Google I/O

 ・現在の Android アプリで流行ってる悪いインターフェースは、「小さいタップターゲット」と「小さすぎるフォントサイズ」

 ・ 視認性、可読性を最適化し、情報の密度のバランスに気をつける

 ・ Analytics を実装して、使用方法のログをみる

 ・あまり使われない機能は options menu に移したり、完全になくす

 ・ よく使う機能やセクションや UI は、すぐに見えて簡単にアクセスできるようにする

 ・ usability は interface design, cognitive science, and other disciplines と密に関連した題材である。もし crash-course を探しているなら、Donald Norman’s The Design of Everyday Things がオススメ


5. Improve appearance and aesthetics

 ・ モバイルと Android に精通していて、 interaction と visual design の能力をもった人を見つけることはなかなかできない。デザイナのための jobs.smashingmagazine.com や social connection や Twitter や LinkdIn を活用する。

 ・ 専任の UIデザイナーが必要ない場合は、自分で改善する。Adobe Photoshop や Adobe Fireworks や他の raster image editing tool を使えるようになる。これらのアプリ内の技術をマスターするのは時間がかかるが、インターフェイスのデザインを磨き上げることができるようになる。

 ・ framework UI assets and layouts を勉強したり、 resources documentation を読んで resources framework をマスターする

 ・ 9-patches や resource directory qualifiers などの Android 特有のテクニックは柔軟で美しい UI を構築する上で重要

 ・ Android UI Design Tips slide


6. Deliver the right set of features

 ・ アプリにとって正しい機能を揃えることが重要

 ・ 多くの情報を提供し過ぎることは、十分提供しないときよりもフラストレーションがたまる

 ・ やっぱりユーザーに聞くのが一番!

 ・ 機能のリクエストに耳を傾けて、よく考えて選択すること。リクエスト全部を実装する必要はない


7. Integrate with the system and third-party apps

 ・ いいユーザーエクスペリエンスを提供するのに最適な方法は、オペレーティングシステムと緊密に統合すること。 app widgets, live folders, global search integration, Quick Contacts badges とか

 ・ いくつかのアプリの種類については、app widgets のような基本的な機能は当然のこと。ないとユーザーが離れてしまう

 ・ いくつかのアプリは Android 2.0 以降で、新しい contacts、アカウント、同期API を使って、OSと緊密に統合可能。 SampleSyncAdapter (bundled with the SDK samples) and JumpNotr がこれらの API のサンプル

 ・ Third-party と integrate する。例えば、カメラアプリなら、ユーザーが Photoshop Express をインストールしていれば、保存する前にこのアプリで編集できるようにすることが可能。詳しい方法は Can I Use this Intent?

 ・ Designing for seamlessness


8. Pay attention to details...

 ・ icon quality and consistency

 ・ crisp and pixel-perfect at all resolutions

 ・ icon guidelines

 ・ Android Asset Studio tool


 

2010年10月22日金曜日

Android Zippy apps tips

元ネタ
http://code.google.com/intl/ja/events/io/2010/sessions/writing-zippy-android-apps.html

https://docs.google.com/viewer?url=http://dl.google.com/googleio/2010/android-writing-zippy-android-apps.pdf

http://code.google.com/p/zippy-android/



時間がかかる処理のときの、UI処理のガイドライン的なもの


■ AsyncTask を走らせる前に、すぐに UI 要素(ボタンとか)を disable にする

■ なにかアニメーションさせる

 - 例えば、タイトルバーで spinner とか

■ ProgressDialog

 – より時間がかかる操作用

 – 控えめに使う、多いとうざい


■ これらの組み合わせ

 – どのくらい時間がかかるかわからないときどうすればいい?

   1. UI 要素の disable, なにかのアニメーション

   2. timer (タイマー) を起動

   3. 200 ms 以上かかるようなら、ProgressDialog を表示

   4. AsyncTask の onPostExecute で timer をキャンセル



 

Android ANR

ANR (“Application Not Responding”)



■ ANR はいつ起こる?

 – main thread (“event thread” / “UI thread”) が 5 秒以上反応しない

 – BroadcastReceiver が 10 秒以内で終わらない

 – 典型的には...

  • ネットワーク操作 (ダウンロードなど) を main thread で行っている

  • 時間がかかる 'disk' 操作 (最適化されていない SQL とか) を
   main thread で行っている

■ 5 ~ 10 秒 より早く! そうしないと...

 – ユーザーに 「このアプリ、ジャンクだな」「おっそいなぁ」…と思われてしまいますよ!


参考元:
Google I/O - Writing zippy Android apps -


 

2010年10月19日火曜日

Google Books Search API : Data API - Reference -

■ Book Search feed types

 ● Search feed


http://books.google.com/books/feeds/volumes?
q=[KEYWORD]
&start-index=[NUM]
&max-results=[NUM]
&min-viewability=[noview|partial|full]


 ● Partner Co-Branded Search feed


http://books.google.com/books/feeds/p/PARTNER_COBRAND_ID/volumes?q=[KEYWORD]


 ● Single item feed


http://www.google.com/books/feeds/volumes/VOLUME_ID


 ● User library feed

認証あり (authentication token in the Authorization HTTP request header)

http://books.google.com/books/feeds/users/me/collections/library/volumes


認証なし

http://books.google.com/books/feeds/users/USER_ID/collections/library/volumes


 ● User annotation feed

認証あり (authentication token in the Authorization HTTP request header)

http://books.google.com/books/feeds/users/me/volumes


認証なし

http://books.google.com/books/feeds/users/USER_ID/volumes



■ HTTP response codes for Book Search Data API requests

CodeExplanation
200 OKNo error. The request to retrieve or update the resource, feed, or entity was successful.
201 CREATEDCreation of a resource was successful.
400 BAD REQUESTInvalid request URI or header, or unsupported nonstandard parameter.
401 UNAUTHORIZEDAuthorization required. This may occur when a request did not contain an Authorization header, when the format of the Authorization header was invalid, or that the authentication token supplied in the header was invalid. See the Authentication section of the developer's guide.
403 FORBIDDENUnsupported standard parameter, quota exceeded, or authentication or authorization failed.
404 NOT FOUNDResource (such as a feed or entry) not found.
500 INTERNAL SERVER ERRORInternal error. This is the default code that is used for all unrecognized errors.


■ Book Search elements reference

 - Atom elements reference

 ● atom:category

  <category> element : エントリの分類

  例

<category scheme="http://schemas.google.com/g/2005#kind"
term="http://schemas.google.com/books/2008#volume"/>
<category scheme="http://schemas.google.com/books/2008/labels" term="ForLydia"/>



 ● atom:link

  レビュー、サムネイル画像、プレビュー URL、検索結果ページなどへのリンク

  例

<link rel="http://schemas.google.com/books/2008/thumbnail" type="image/jpeg"
href="http://bks3.books.google.com/books?id=o9dyGQAACAAJ&printsec=frontcover&img=1&zoom=5&sig=ACfU3U2sYY4y7XFmHKMYvISqfVGKwdN93w&source=gbs_gdata"/>
<link rel="http://schemas.google.com/books/2008/info" type="text/html"
href="http://books.google.com/books?id=o9dyGQAACAAJ&dq=pride+and+prejudice&ie=ISO-8859-1&source=gbs_gdata"/>
<link rel="http://schemas.google.com/books/2008/preview" type="text/html"
href="http://books.google.com/books?id=o9dyGQAACAAJ&dq=pride+and+prejudice&ie=ISO-8859-1&source=gbs_gdata"/>
<link rel="alternate" type="text/html"
href="http://books.google.com/books?id=o9dyGQAACAAJ&dq=pride+and+prejudice&ie=ISO-8859-1"/>
<link rel="self" type="application/atom+xml"
href="http://www.google.com/books/feeds/volumes/o9dyGQAACAAJ"/>



 - Dublin Core elements reference

 ● dc:creator

  著者、訳者など

  例

<dc:creator>Jane Austen</dc:creator>


 ● dc:date

  出版日

  例

<dc:date>1813-01-28</dc:date>


 ● dc:description

  説明

  例

<dc:description>Austen's comedy of manners--one of the most popular novels
of all time--features splendidly civilized sparring between the proud Mr...</dc:description>


 ● dc:format

  ページ数

  例

<dc:format>95 pages</dc:format>


 ● dc:identifier

  特定の本への明白なリファレンス
  ・各 <entry> に少なくとも1つ含まれる
  ・最初の identifier は Books Search でユニーク
   (例 s1gVAAAAYAAJ)
  ・2番目以降の identifier は外部の識別子 (ISBN, ISSN,
   Library of Congress Control Numbers (LCCNs),
   OCLC numbers) これらには prefix ("ISBN:" など) がつく

  例

<dc:identifier>qCkfAAAACAAJ</dc:identifier>
<dc:identifier>ISBN:0582780934</dc:identifier>


 ● dc:publisher

  出版社

  例

<dc:publisher>T. Egerton of the Whitehall Military Library</dc:publisher>


 ● dc:subject

  主題。topic of the book

  例

<dc:subject>Fiction</dc:subject>


 ● dc:title

  タイトル

  例

<dc:title>Pride and Prejudice</dc:title>
<dc:title>A Novel</dc:title>



 - Book Search custom elements reference

 ● gbs:viewability

  中身がどの程度みれるか

  例

<gbs:viewability value="http://schemas.google.com/books/2008#view_all_pages"/>


 ● gbs:embeddability

  どの程度 embed できるか

  例

<gbs:embeddability value="http://schemas.google.com/books/2008#embeddable"/>


 ● gbs:review

  レビュー

  例

<gbs:review type="text" xml:lang="en">A great beach read!</gbs:review>

Android 背景画像を repeat させる

背景画像を repeat させたい場合、ImageView とかの属性では実現できません。 BitmapDrawable で repeat した画像を作って、背景に指定するば OK です。

BitmapDrawable はコードからも生成できますが、xml で作ったほうが楽です。 Bitmap Resource の XML Bitmap のところを参照してね。

XML ファイルは
 res/drawable/filename.xml

XML ファイルの中身の SYNTAX


<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@[package:]drawable/drawable_resource"
android:antialias=["true" | "false"]
android:dither=["true" | "false"]
android:filter=["true" | "false"]
android:gravity=["top" | "bottom" | "left" |
"right" | "center_vertical" |
"fill_vertical" | "center_horizontal" |
"fill_horizontal" | "center" | "fill" |
"clip_vertical" | "clip_horizontal"]
android:tileMode=["disabled" | "clamp" |
"repeat" | "mirror"] />



ここの android:tileMode に repeat を指定すると、android:src で指定した
画像がタイル状になった Bitmap ができます。


この Bitmap をコードから指定するときは
  R.drawable.filename

レイアウトXMLの ImageView の android:src や android:backgroundに
指定するときは
  @[package:]drawable/filename

とすればOK

こんな感じ



res/drawable/tilebg.xml

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/icon"
android:antialias="true"
android:dither="false"
android:filter="false"
android:gravity="fill"
android:tileMode="repeat" />


res/layout/main.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"
android:background="@drawable/tilebg"
>
</LinearLayout>


 

2010年10月18日月曜日

Google Books Search API : Data API - Searching for books -

Boo Search Data API は書籍リストの feed を提供する

最も一般的なのは検索クエリにマッチする本のリストを引き出すアクション
このアクションは、次の URL に適切なクエリパラメータを付加して HTTP GET リクエストを投げればいい


http://books.google.com/books/feeds/volumes


注: Book Search では、本を参照するのによく "volume" term を使う
  volumes feed は本の feed である。

次の例は、メタデータやテキストが "football" にはマッチするが、"soccer" にはマッチしない検索結果の、10冊づつセットでの2番目を取得する


http://books.google.com/books/feeds/volumes?
q=football+-soccer
&start-index=11
&max-results=10


GET リクエストを送った後、Book Search はファクターに応じたリダイレクトを返す。その場合、リダイレクト URL に別の GET リクエストを送る。GET リクエストを送ると、Book Search は HTTP 200 OK status code を返し、その feed にクエリにマッチした本のリストが含まれる。

注: Book Search の結果は public なので、認証なしで Book Search query を発行できる


Book Search Data API のクエリパラメータ

  q
    検索クエリタームを指定する
    Book Search は全ての本のメタデータと term にマッチした本の
    全テキストを検索する
    本のメタデータには、タイトル、キーワード、説明、著者名、題目
    が含まれる

    注: URL エンコードすること

    特定のフレーズを検索する場合は、クオーテーションマーク(")でくくる
    クオーテーションマークも URL エンコードすること

    advanced search operators

  start-index
    検索結果リストの最初のインデックスを指定する
    
  max-results
    検索結果リストの最大数を指定する
    
  min-viewability
    本の viewability status に応じて結果をフィルターする
    指定できる値は noview (デフォルト、viewability に関係なく
    全ての本を返す)、partial (preview できる、もしくは全体を
    見れる本を返す)、full (全体が見れる本のみ返す)

    注: viewability はユーザーの location によるので、正確な
      結果を得るために、end-user の IP を HTTP リクエストの
      ヘッダに負荷するほうがよい


 

2010年10月17日日曜日

Google Books Search API : Data API - Authenticating to the Book Search service -

■ Getting started

 ● Creating a Book Search account

  Book Search は Google Account を使っているので、
  Google Account を既に持って入れば、そのアカウントで
  My Library の機能をテストできる。



■ Authenticating to the Book Search service

 Public feed には、authentication は必要ないが、read-only

 user libraries を編集したり、レビューや評価を投票したり、
 ラベルを追加したりする場合は、private feeds にリクエスト
 を投げる前に authentication が必要

 authentication には2つの方法がある
 AuthSub proxy authentication
 ClientLogin username/password authentication

 authentication documentation

 ● AuthSub proxy authentication

  AuthSub proxy authentication はユーザーの Google Accounts で
  認証する必要がある web アプリで使う
  web site operator や client code は Book Search ユーザーの
  username と password にアクセスする必要がない代わりに、
  client は特別な AuthSub token を取得する
  詳しくは AuthSub documentation を参照

  ユーザーが最初に自分のアプリケーションに来たときにはまだ
  認証がされていない。この場合、情報を表示して、ユーザーの
  アカウントにアクセスするリクエストを認証するための Google page に
  リダイレクトする必要がある。

  AuthSubRequest URL は以下のクエリパラメータを含む

  next
    認証後に Google がリダイレクトするページのURL
  scope
    アプリケーションが Book Search feeds のトークンを
    リクエストしていることを表す。この scope の文字列には、
    http://www.google.com/books/feeds/
    (もちろん URL-encoded する) を使う
  secure
    client が secure token をリクエストしているかどうかを表す
  session
    multi-use (settion) token に持ち替えられるかどうかを表す

  AuthSubRequest URL はこんな感じになる。

https://www.google.com/accounts/AuthSubRequest?scope=http%3A%2F%2Fwww.google.com%2Fbooks%2Ffeeds%2F&session=1&secure=0&next=http%3A%2F%2Fwww.example.com%2Fwelcome.html

  許可すると、next パラメータで指定したらURLにリダイレクトされ、
  AuthSub System が最後に token パラメータを追加する

  リダイレクト先の URL はこんな感じになる。

http://www.example.com/welcome.html?token=yourAuthToken
  

  例えば、

https://www.google.com/accounts/AuthSubRequest?scope=http%3A%2F%2Fwww.google.com%2Fbooks%2Ffeeds%2F&session=1&secure=0&next=http%3A%2F%2Fy-anz-m.blogspot.com

  にアクセスすると、次のようなページが表示される

  許可すると、http://y-anz-m.blogspot.com/ にリダイレクトされる


  このトークン値は single-use AuthSub token を提供する。
  この例では、session=1 を指定しているので、Authorization header に
  single-use token を付加して AuthSubSessionToken サービス
  を呼び出すことで、AuthSub session token を
  取り替えられる

  header はこんなかんじ

GET /accounts/AuthSubSessionToken HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Authorization: AuthSub token="yourAuthToken"
User-Agent: Java/1.5.0_06
Host: www.google.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive


  AuthSubSessionToken サービスのレスポンスには、 Token header に
  session token が含まれ、Expiration header にどのくらいの期間
  このトークンが有効かが含まれる
  
  アプリケーションでは、続く Book Search とのやりとりの
  Authorization header に、この session token をつかうことができる

  non-secure token を含む HTTP リクエストの例

GET http://www.google.com/books/feeds/users/me/volumes HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Authorization: AuthSub token="yourSessionToken"
User-Agent: Java/1.5.0_06
Host: www.google.com
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
Connection: keep-alive


 ● ClientLogin username/password authentication

  client が standalone (single-user が "installed" した
  クライアント、デスクトップアプリケーションとか)の場合、
  ClientLogin authentication を使う。ClientLogin を使って
  authentication token をリクエストするには、次の URL に
  POST リクエストを送る
  

https://www.google.com/accounts/ClientLogin


  POST body には、クエリパラメータのセットを含み、content type
  には、application/x-www-form-urlencoded を使う

  パラメータ

  accountType
    認証されるアカウントのタイプ
    デフォルトは GOOGLE
    もし、Google Apps ユーザーをサポートしたいなら
    HOSTED_OR_GOOGLE
  Email
    ユーザーの email アドレス
  Passwd
    ユーザーの パスワード
  service
    Book Search サービス名 "print"
    (他のサービス名は service name list 参照)
  source
    client アプリケーションの識別値
    companyName-applicationName-versionID から取得する


  パラメータの詳細は Authentication for Installed Applications 参照

  認証リクエストに失敗場合、サーバーは HTTP 403 Forbidden
  status code を返す

  成功した場合、サーバーは HTTP 200 OK status code を返す
  さらに 3つの long alphanumeric codes SID, LSID, Auth を
  レスポンスの bodyに含む
  Auth 値が authorization token であり、volumes-feed リクエスト
  と一緒に Book Search に送る。SID と LSID は無視していい。

  private feeds への全てのリクエストは認証が必要なので、Book Search
  との全てのやりとりには、次のフォーマットを使って Authorization
  header をセットしなければならない


Authorization: GoogleLogin auth=yourAuthToken


  yourAuthToken は ClientLogin リクエストで返された Auth 文字列

  リクエストとレンスポンスを含む ClientLogin authentication の詳細は、
  Authentication for Installed Applications を参照

  注: 与えられたセッションでの全てのリクエストに同じトークンを使うこと
    Book Search リクエストごとに新しい token を取得しないこと


 

2010年10月16日土曜日

Google Book Search API

Google Book Search APIs

Getting Started Guide


 Basic
 ● Wizards and tools : programmer じゃない人向け

   - BookBar : 本のサムネイルの帯をサイトに入れる
     ・クリックすると、Book Ssearch のページにとぶ
     ・Wizard から簡単に作れる
     ・advanced customize も可能

    

   - Preview Wizard
     ・本のプレビューを表示できる
     ・Wizard から簡単に作れる
       ・サイトにプレビューを埋め込む
       ・floating book viewer を開くボタンを作る
       ・プレビューページへのリンクボタンを作る
       ・カスタムプレビューページへのリンクボタンを作る
     ・カスタマイズも可能


 Medium
 ● Linking to Book Search : Book Search 内の本にリンクをはる

   - Linking to Search Result

    ISBN
      isbn=[ISBN]

http://books.google.co.jp/books?isbn=0060930314
    
    キーワード
      q=[KEYWORD]

http://books.google.co.jp/books?q=[query]
    

    書籍名
      q=intitle:[TITLE]

http://books.google.co.jp/books?q=intitle:scarlet+intitle:letter
    

    著者名
      q=inauthor:[AUTHOR]

http://books.google.co.jp/books?q=inauthor:hawthorne
    

    期間
      q=date:[DATE]

http://books.google.co.jp/books?q=intitle:nathaniel+date:1850-1870
    


   - Static Links

     ・International Standard Book Number (ISBN)
     ・Library of Congress Control Number (LCCN)
     ・Online Computer Library Center (OCLC) record number

   で指定可能

    Book
      vid=ISBN{ISBN Number}

http://books.google.com/books?vid=ISBN0451522907
    

    Front cover
      printsec=frontcover

http://books.google.com/books?vid=ISBN0451522907&printsec=frontcover


    Title page
      printsec=titlepage

http://books.google.com/books?vid=ISBN0451522907&printsec=titlepage


    Copyright page
      printsec=copyright

http://books.google.com/books?vid=ISBN0451522907&printsec=copyright


    Table of contents
      printsec=toc

http://books.google.com/books?vid=ISBN0451522907&printsec=toc


    Index
      printsec=index

http://books.google.com/books?vid=ISBN0451522907&printsec=index


    Back cover
      printsec=backcover

http://books.google.com/books?vid=ISBN0451522907&printsec=backcover



   - Dyanmic Links


 Medium
 ● Embedded Viewer API : 自分のサイトに直接コンテンツを埋め込む
   ・Google Maps API や YouTube Embedded Player みたいなもの
   ・embedded viewer は JavaScript の関数からコントロール可能
    ページ移動、ズーム、ハイライト、現在のページ番号の取得など
   ・Developer's Guide
   ・Reference Guide
   ・Examples and Demos


 Advanced
 ● Data API : Book Search サイトで一般ユーザーが行う操作の
        ほぼすべてを実行できる
   ・Book Search website 上で可能な多くの操作を実行できる
   ・本の検索、評価の集計など
   ・ユーザーの My Library を管理するために認証を使える
   ・基本的にはサーバサイド言語でプログラミングしている開発者向け
   ・例えばこんなことが可能
      ・ユーザー情報に応じてサイトやアプリをパーソナライズ
      ・Book Search から full-text 検索結果を表示する
       web アプリを作る
      ・新しいプラットフォームに Book Search experience を
       もたらすデスクトップアプリを作る
   ・Google Data Protocol Guide : requests and responses
   ・Google Data Reference Guide : API's feed types,
    HTTP requestparameters, HTTP response codes, and
    XML elements
   ・Google Data Protocol
   ・Atom Publishing Protocol
   ・client libraries : abstruct the API into
    a language-specific object model (Java, PHP, JavaScript)

   ・Google Data APIs protocol
   ・My Library features



 

2010年10月15日金曜日

Android 「TraceView War Story」

http://android-developers.blogspot.com/2010/10/traceview-war-story.html

面白かったので要約してみた。

途中でダレてきたので、訳間違ってるかもしれない。そのときはごめんなさいw



Making Apps Fast

私は、最も経験豊富な開発者と情報共有しているという信念を持っている:
どんなアプリであっても、たとえそれほど複雑でないアプリでも、遅くしている部分がどこなのか予測できるほど我々は賢くない。なぜなら、誰もソフトウェアのボトルネックが変わることを十分予測できないからである。

そのため、速いアプリを作る賢い方法は、そのアプリが動作できる最も単純な方法でビルドすることである。N乗のアルゴリズムなんていう言語道断な愚かな方法や、I/O を Android UI スレッドで行うなどは避けるべきだ。これで十分速いかもしれない。それならそれでおしまい!

もし、それでも速くないなら、なぜなのかを推測してはいけない。プロファイラを使って測って見つける。こうすべきだと言うことを知っていたので、窮地に追いつめられると System.err.println("Entered at" + System.currentTimeMillis()); とかを使っていた。幸いなことに、Android には合理的でまともなプロファイラが付属しているので、こんな醜い方法で取得する必要はない。


Case Study: LifeSaver 2

私は LifeSaver 2 という utility アプリを Android Market に出している。アプリの詳細については私の個人ブログをみてほしい。このアプリはシステムの SMS と phone-call ログを読んで、SD card 内に JSON text ファイルで保存するというものだ。これには少し時間がかかるので、動的なプログレスバーを表示している。しかし、ギガヘルツプロセッサを載せたデバイス上で、数百のレコードをテキストファイルに書き込むだけなのに、どうして時間がかかるのか不思議に思った。

愚かにも上記の私の忠告を聞かなかった人は、システムログの読み込みをする ContentProvider のカーソル処理の失敗や、SD card への書き込みオーバーヘッドが遅くなる原因だと仮定するかもしれない。賢い人ならコードを測って原因を見つけるだろう。ではやってみよう。


Turning On Tracing

Saver.java に以下のコードを入れた


public void run() {

android.os.Debug.startMethodTracing("lsd");

// ... method body elided

android.os.Debug.stopMethodTracing();
}


最初の呼び出しでトレースをオンにする。引数の "lsd" (stands for Life Saver Debug, of course) はトレースログの出力先ファイル名である(/sdcard/lsd.trace に保存される)。WRITE_EXTERNAL_STORAGE permission を AndroidManifest.xml に書くのを忘れずに!そして、必要なくなったら公開する前に削除するのを忘れずに。


[Update:] Android engineer Xavier Ducrohet が教えてくれた: “DDMS には 'device view' 内にプロファイリングの start/stop ボタンがある。stop が押されるまで、TraceView が実行される。詳細な start/stopMethodTracing には向いてないが極めて使いやすい。froyo より前の VM では、パーミッションが必要になる(DDMS は基本的に自動で SD card から trace を取得して traceview が呼ばれる前にローカルに保存する)。 Froyo 以降の VM では、trace file を JDWP connection を通して送ることができるので、もはやパーミッションは必要ない(これは本当に使いやすい)" Thanks, Xav!


アプリの実行が終わったら、デバイスからトレースログを取り出して、traceview コマンドで表示する。


540> adb pull /sdcard/lsd.trace
541> traceview lsd


ここでのポイントは、1. アプリで実際に遅くなっている部分のみトレースをオンにすること。2. トレースファイルは大きい(この場合では 4秒とるのに 8.6M)。 3. traceview の見た目はとっても Cool!



上部のバーはアプリのスレッドを示しており、タイムアウト処理にどの程度のかかるのかがわかる。Nexus One はシングルスレッド CPU なので順番に実行されなければならない。もう一段小さい縮尺にしてみよう。



トップラインは私のアプリでコードが走っている部分(赤い領域は GC が起こっている)で、ミドルラインは UI スレッドを表している。UI thread で Activity が バーストしているところは、プログレスバーの更新が起こっているところだ。3番目の HeapWoker という名前のスレッドはよくわからないが、アプリのランタイムへの寄与は大きくなさそうなので無視しよう。

スクリーンの下部には、本当に重要なデータがある:どのメソッドが時間を消費しているかを示しており、さまざまな異なる方法でソートすることができる。最初の2行をみてみよう。



この結果を翻訳すると、(あれとか、これとか)呼ばれるものすべてが含まれているなら、 top-level routine が 100% の時間を消費しているということを教えてくれている。次の行から本物っぽく面白くなってきた: java.io.PrintStream.println(Object) が何回も呼ばれていて、アプリ全体の 65.25% も使っている。これは、SD card に JSON を書きだしているコードだ。明らかに、電話の ContentProviders からデータを取り出して書きだす処理が、重すぎる処理ではないことが分かる。; it’s the output that’s hurting.

このアプリは SD card への書き込みパフォーマンスが低迷していることで制限されていると結論づけられるだろうか? 想像できる最も明白な point-and-click 手法でもう少し掘り下げてみよう。



うーん。厄介な驚きはない。もちろん、 println は(効果として) 全ての引数に toString() を呼び出している。引数を strings に変換している処理は、時間の半分以上を占めているように見える。それが
、println(Object) から println(String) へディスパッチする前であっても。

println(String) をさらに掘り下げるステップはスキップしよう。しかし、SD card への遅い I/O が起こっているということが示された。今度は String.valueOf() 呼び出しの中をみてみよう。



証拠があった。が、org.json.JSONObject.toString() は我々プロのプログラマーがよく呼び、なれ親しんでいる操作なので、私はここをいじりたくない。いじることは出来るが厄介なことになるだけだ。

我々にできることは、全てのルーチンを "Exclusive" time (ルーチン内で実行されたCPUサイクル数)でソートすることだ。これは、全実行時間の 1% 以上を使っているの一覧だ。



ここには、少しの GC と Android frameword View-wrangling stuff がある。しかし、org.jason と java.lang.StringBuilder コードによってリストが占められている。


The Conclusion

このアプリの場合、実際の結論は、、、確かに私はあまりパフォーマンスを気にしていない。ユーザーは古い電話と新しい電話で1回ずつ合わせて2回実行するが、目を休めるもの(アニメーションとか)がたくさんあるので、問題があるとは思わない。

もし、これをスピードアップしたかったら、やるべきことは明らかだ。最初に、JSON を使うのを止めるかもっとチープなシリアライズ方法を見つける。 2つ目に、println() の呼び出しを減らす; データを一つの大きなバッファにまとめて、単一の I/O 呼び出しでどばっと書き出す。しかし、ここでのキーポイントは、もし私がボトルネックを予測していたら、間違っていただろうということだ。

TraceView は素晴らしいツールで、もしまだ知らないなら自分自身のために学ぶべきだ。

2010年10月14日木曜日

Eclipse のスプラッシュを変える

なんか、やってみてと言われたのでやってみた。

注: これは Ubuntu でやった結果です。
  Windows や Mac でも同じ方法でできるかはわかりません。

追記: Windows では、eclipse\dropins\MergeDoc\eclipseにあった splash.bmp を入れ替えるとできるそうです。 @x333es さん情報ありがとうございます!


/eclipseがインストールされてるディレクトリ/plugins/org.eclipse.platform_**/

の中の splash.bmp を入れ替えるだけ。

デフォルトのサイズは 455 x 295

入れ替える画像の縦が 295 より小さいと、下の方にでる
プログレスバーが表示されなくなるので気をつけて。

こんな感じ。




# 今の壁紙まじでこれですよ。だって Droid かっこいいんだもん。


 

2010年10月12日火曜日

Java 正規表現

忘備録

  * : 0回以上の繰り返し

  + : 1回以上の繰り返し

  ? : 0回 or 1回

  . : 任意の1文字

  ^ : 先頭

  $ : 終端

  [^] : 文字クラスの否定


・エスケープ文字

  \ * + . ? { } ( ) [ ] ^ $ - |


・エスケープ文字をパターン入れるときは、エスケープ処理が必要

  \\( \\{ \\$ \\|


・パターン文字列には、エスケープシーケンスをいれることが可能

\0n8 進値 0n を持つ文字 (0 <= n <= 7)
\0nn8 進値 0nn を持つ文字 (0 <= n <= 7)
\0mnn8 進値 0mnn を持つ文字 (0 <= m <= 3、0 <= n <= 7)
\xhh16 進値 0xhh を持つ文字
\uhhhh   16 進値 0xhhhh を持つ文字
\tタブ文字 ('\u0009')
\n改行文字 ('\u000A')
\rキャリッジリターン文字 ('\u000D')
\f用紙送り文字 ('\u000C')
\a警告 (ベル) 文字 ('\u0007')
\eエスケープ文字 ('\u001B')
\cxx に対応する制御文字





こんな感じで使う


String in = "<input type=\"hidden"\ name=\"hoge1\" value=\"hogehoge11\">"
+ "<input type=\"hidden"\ name=hoge2 value=hogehoge22>"
+ "<input type=\"hidden"\ NAME=\"hoge3\" VALUE=\"hogehoge33\">";


int flags = Pattern.CASE_INSENSITIVE | Pattern.MULTILINE;
Pattern ptn = Pattern.compile("<input.* name=\"?([^\"]*)\"? value=\"?([^\"]*)\"?>", flags);

Matcher mt = ptn.matcher(in);
while (mt.find()) {
System.out.println(mt.groupCount());
String name = mt.group(1);
System.out.println(name);
String value = mt.group(2);
System.out.println(value);
}


() で括った部分が mt.group(n) で取り出せる。
mt.group(0) には全体が入っていて、最初の () 部分は mt.group(1) に入る。


 

2010年10月8日金曜日

Android LVL を使う - Securing Android LVL Applications -

# 自分で予習しないと、くごー先生におこられそうなので、、、(嘘)

参考サイト

 ・Android Market licensing service

 ・Android Developers Blog: Securing Android LVL Applications

 ・Android Developers Blog: Proguard, Android, and the Licensing Server

 ・mokkouyouの開発日記 GDD2010Tokyoで聞いたのLVL関連のちょっと応用編な話

 ・LVL を使った Android アプリをセキュアにする

 ・Proguard, Android と ライセンシングサーバについて


Securing Android LVL Applications

LVL (License Verification Library : ライセンス検証ライブラリ) とは、アプリを認証外の使用から防ぐ強力な手段である Android Market licensing service の中核をなすものである。

ライセンスライブラリの導入だけでは端末にインストールされたアプリを完全にセキュアにはできない。そのため、アプリ自体に LVL の深い知識が必要とするような複雑さや異質さを LVL への追加する必要がある。


海賊行為(root を取った端末で apk を抜き出すとか)への対策として

 1. 難読化 - リバースエンジニアリングの難易度を上げる

 2. LVLの改造 - よくあるクラック手法を防ぐ

 3. 不正な改造の検知 - アプリに不正な改造が行われたことをすぐに検知

 4. ライセンスの検証 - ライセンスの検証を信頼済みのサーバに任せる



1. 難読化す
  ・ProGuardを使う
    ・android-proguard-commandline を使えば、
     コマンドラインからも可能


2. ライセンスライブラリ(LL)の改造
  逆アセンブリしたコードの改竄によるライセンス検証の通過を難しくする

  そのために、
   ・アプリのバイトコードをより複雑にする
   ・アプリ内でのLVLの実装を独自のものにする

  これにより、2種の攻撃を防ぐ
   ・ 自分のアプリケーションを改竄から防ぐ
   ・ 自分のアプリケーションが他のアプリケーション(もしくは
     オリジナルのLVL)で得た攻撃手法で突破されることを防ぐ


  LLを改造する対象の3分野
   1. LLロジックの中核部分
   2. LLの entry/exit ポイント
   3. 自分のアプリケーションでのLLの呼び出し方法、応答方法


  1. LLロジックの中核部分を改造する

   LVLのコアを構成する LicenseChecker class と LicenseValidator
   class をアプリケーションの元の機能が保たれる限り、様々な方法を
   使って可能な限りいじくりまわす

    例
     ・ switch文をif文に置き換える

     ・分岐には定数の代わりにXORやハッシュ関数を元に生成した値を使う

     ・ソースコードから使われてない部分を削る
      例えば、差し替え可能な Policy が必要でない場合は、Policy の
      インタフェース部分を削除して、LicenseValidator の残りの部分に
      Policy の検証部分をインライン実装する

     ・LVLをまるごと自分のアプリケーションパッケージ内に移動する

     ・ライセンス検証の別々の部分をそれぞれ別プロセスで検証する

     ・可能な限りインライン化する

    など

    *目標は自分のアプリケーションが独自のLVL実装を行うこと!
     サンプルコピペは厳禁です!


  2. LLの entry/exit ポイント

   攻撃者が同じ public interface を実装する偽のLVLを作成し、
   自分のアプリケーション内の関連するクラスを置き換えようとするやり方を防ぐ

   ・LicenseChecker のコンストラクタへ引数を追加することを検討する
   ・LicenseCheckerCallback のメソッド allow(), disallow() へ
    引数を追加することを検討する
   ・その都度生成した値を引数で渡すなど

     * 難読化ツールを使う場合、自分で allow(), disallow()
       をリネームしても難読化ツールが自動的にリネーム
       するので、特に効果はない


  3. 自分のアプリケーションでのLLの呼び出し方法、応答方法

   攻撃者が自分のアプリ内での LVL 呼び出し部分を調べ、
   攻撃する手法気をつける。

   例
     ライセンス検証失敗時に「終了」ボタンのついたダイアログを
     表示する場合、攻撃者がそのダイアログを表示する部分のコードを
     コメントアウトしたらどうなるか?
     それでもアプリケーションがきちんと終了できるように実装してる?

   これを避けるためには
     ・別のアクティビティを呼び出してライセンスが無効であることを表示し、
      直ちに元のアクティビティを終了する
     ・finish() メソッドが無効化されても機能するよう、
      別のところにもfinish()を仕込む
     ・タイムアウト後にアプリを終了するようなタイマーを仕掛ける

   他にも
     ・攻撃者はアプリ起動時のライセンスチェックを予想するだろうから、
      アプリの起動時間が数分経過してからライセンスチェックを行う

   など


   *Proguardなどのツールを使った場合でも、特定のメソッドは難読化
    されないことに注意する。重大な例として、onCreate() は
    リネームされない。
なぜなら、Androidシステムから
    呼び出し可能のままでなければならないから。ライセンスチェック部分を
    この部分に記述するのはいけない。攻撃者にバレバレです。


3. 不正な改造の検知

  アプリに耐タンパ性を持たせる

  攻撃者が LVL 部分を削除するためにコードを改ざんしようとした場合、
  このような改ざんを自分のコード内で検知することが可能

  実際に使える方法として、

   ・(わかりやすい方法)CRC32のような軽いハッシュ関数で自分の
    アプリケーションのコードのハッシュ値を作成し、前もって作成した
    正しい値とを比較する
   
    * 自分のアプリケーションのファイルのpathは
      context.GetApplicationInfo()で取得可能
    * 正しいチェックサムの値を記述しておくファイルをチェックサムの
      計算の対象に入れちゃだめ。
      (こういう情報を独自のサーバに置いておくのもあり)


   ・自分のアプリケーションがデバッグ可能かどうかを確認する

 
boolean isDebuggable = (0 != (
getApplcationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE
));


  

4. ライセンスの検証

  もし自分のアプリケーションに通信を行う部分が存在するのなら、
  自分のオンライン上のサーバに、ライセンスサーバの応答のコピー
  (ResponseData クラスにアプリ自身の署名とセットで格納されている)を
  送信するという強力な方法が使える

  サーバはユーザが認証済みか検証でき、検証に失敗した場合はオンラインの
  コンテンツの提供をすべて拒否できる

  ライセンスサーバからの応答は暗号化かつ署名されているので、
  自分のサーバでは Android マーケットパブリッシャーコンソールに格納された
  RSA 鍵を使ってライセンスの応答が改竄されていないかどうか確認できる


  サーバサイドでの検証を行う際は以下の項目を確認する

   ・応答の署名が正当なものか

   ・ライセンスサーバが応答で LICENSED を返したか

   ・パッケージ名とバージョンが正しいアプリケーションと同一か

   ・ライセンス応答が有効期限以内かどうか (VTライセンスextraを確認する)

   ・ライセンスサーバ内でライセンス確認におけるuserIdフィールドのログをとり、
    別のユーザに認証されたライセンス応答が再利用されていないか確かめる
    (特定のユーザのライセンス確認ばかりがありえないほど何回も要求される
     場合に疑っておくべきパターン)


  ライセンス応答をどうやって正当に検証するかについては
  LicenseValidator.verify().を参照すること

  ライセンス確認がサーバ内で行われている限りは(そしてそのサーバ自身が
  セキュアなら)、熟練したクラッカーでさえ回避できないという無上の価値を有する
  サーバが信頼されたコンピューティング環境にあることがその理由です。

  ユーザのコントロール下にあるコンピュータ(Android端末も含む)で
  実行されるコードは原則として信用してはいけない(ということを覚えておく)
  もしユーザにサーバサイドでのライセンス認証の失敗を知らせる場合、このような
  コードは勧告を告げる範囲内でのみ実行されなければいけない
  そして、未認証のユーザへのコンテンツの送信はなんであれ拒否すると言う動作を
  確実に行わなければならない

2010年10月6日水曜日

Android Filterを使ってみた

android.widget.Filter を使ってみた。

例えば、EditText で英数字しか入力できないようにするには
こんな感じにする。


public class FilterTest extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

EditText ed = (EditText)findViewById(R.id.edittext);
ed.setFilters(filters);
}

private InputFilter[] filters = { new MyFilter() };

class MyFilter implements InputFilter {
public CharSequence filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend) {

if( source.toString().matches("^[a-zA-Z0-9]+$") ){
return source;
}else{
return "";
}
}
}
}


filter(CharSequence source, int start, int end,
Spanned dest, int dstart, int dend)
の各引数について

source: 今入力されていて、確定していない文字列
     つまり、一般的に下線がでている変換前の文字列

start : 今入力されていて、確定していない文字列の先頭位置
     常に 0 になる(みたいだ)

end  : 今入力されていて、確定していない文字列の終端位置
     常に確定していない文字列の長さになる(みたいだ)

dest : 今EditTextに入っている文字列、確定していない文字列も含む

dstart : 今入力されていて確定していない文字列の、 EditText
     に入っている文字列上での先頭位置
     
dend : 今入力されていて確定していない文字列の、 EditText に
    入っている文字列上での終端位置

試しにログを出してみた。


--- a ---

D/filterTest(14321): source : a
D/filterTest(14321): start : 0
D/filterTest(14321): end : 1
D/filterTest(14321): dest :
D/filterTest(14321): dstart : 0
D/filterTest(14321): dend : 0
D/filterTest(14321): source : a
D/filterTest(14321): start : 0
D/filterTest(14321): end : 1
D/filterTest(14321): dest : a
D/filterTest(14321): dstart : 0
D/filterTest(14321): dend : 1

--- b ---

D/filterTest(14321): source : ab
D/filterTest(14321): start : 0
D/filterTest(14321): end : 2
D/filterTest(14321): dest : a
D/filterTest(14321): dstart : 0
D/filterTest(14321): dend : 1
D/filterTest(14321): source : ab
D/filterTest(14321): start : 0
D/filterTest(14321): end : 2
D/filterTest(14321): dest : ab
D/filterTest(14321): dstart : 0
D/filterTest(14321): dend : 2

--- c ---

D/filterTest(14321): source : abc
D/filterTest(14321): start : 0
D/filterTest(14321): end : 3
D/filterTest(14321): dest : ab
D/filterTest(14321): dstart : 0
D/filterTest(14321): dend : 2
D/filterTest(14321): source : abc
D/filterTest(14321): start : 0
D/filterTest(14321): end : 3
D/filterTest(14321): dest : abc
D/filterTest(14321): dstart : 0
D/filterTest(14321): dend : 3

--- Enter ---

D/filterTest(14321): source : abc
D/filterTest(14321): start : 0
D/filterTest(14321): end : 3
D/filterTest(14321): dest : abc
D/filterTest(14321): dstart : 0
D/filterTest(14321): dend : 3
D/filterTest(14321): source :
D/filterTest(14321): start : 0
D/filterTest(14321): end : 1
D/filterTest(14321): dest : abc
D/filterTest(14321): dstart : 3
D/filterTest(14321): dend : 3

--- あ ---

D/filterTest(14321): source : あ
D/filterTest(14321): start : 0
D/filterTest(14321): end : 1
D/filterTest(14321): dest : abc
D/filterTest(14321): dstart : 3
D/filterTest(14321): dend : 3

--- い ---

D/filterTest(14321): source : あい
D/filterTest(14321): start : 0
D/filterTest(14321): end : 2
D/filterTest(14321): dest : abc
D/filterTest(14321): dstart : 3
D/filterTest(14321): dend : 3

--- う ---

D/filterTest(14321): source : あいう
D/filterTest(14321): start : 0
D/filterTest(14321): end : 3
D/filterTest(14321): dest : abc
D/filterTest(14321): dstart : 3
D/filterTest(14321): dend : 3

--- Enter ---

D/filterTest(14321): source : あいう
D/filterTest(14321): start : 0
D/filterTest(14321): end : 3
D/filterTest(14321): dest : abc
D/filterTest(14321): dstart : 3
D/filterTest(14321): dend : 3
D/filterTest(14321): source :
D/filterTest(14321): start : 0
D/filterTest(14321): end : 0
D/filterTest(14321): dest : abc
D/filterTest(14321): dstart : 3
D/filterTest(14321): dend : 3

--- 1 ---

D/filterTest(14321): source : 1
D/filterTest(14321): start : 0
D/filterTest(14321): end : 1
D/filterTest(14321): dest : abc
D/filterTest(14321): dstart : 3
D/filterTest(14321): dend : 3

--- 2 ---

D/filterTest(14321): source : 2
D/filterTest(14321): start : 0
D/filterTest(14321): end : 1
D/filterTest(14321): dest : abc1
D/filterTest(14321): dstart : 4
D/filterTest(14321): dend : 4

--- 3 ---

D/filterTest(14321): source : 3
D/filterTest(14321): start : 0
D/filterTest(14321): end : 1
D/filterTest(14321): dest : abc12
D/filterTest(14321): dstart : 5
D/filterTest(14321): dend : 5



 

Android ListAdapter

前回に続いて List 用の Adapter を整理してみた。
独自 Adapter を作る場合は BaseAdapter を継承するが定石なのかな、、、?
ArrayAdapterも使いやすい。

■ public interface
 ・ListAdapter
 ・SpinnerAdapter
 ・WrapperListAdapter
    別の list adapter のラッパー

■ public abstract class
 ・BaseAdapter
    ListView と Spinner の両方で使用可能な adapter を実装するための
    共通基底クラス
    ListView は ListAdapter 専用の interface を実装し、
    Spinner は SpinnerAdapter 専用の interface を実装する

 ・CursorAdapter
    Cursor から取り出したデータを ListView widget に紐づける

 ・ResourceCursorAdapter
    XMLファイルで定義された view を生成する簡単な adapter

■ public class
 ・ArrayAdapter
    任意のオブジェクト配列に連動する ListView を管理するための
    ListAdapter

 ・HeaderViewListAdapter
    ListView が header view を持つときに使用される ListAdapter

 ・SimpleAdapter
    static data をXMLファイルで定義された view に紐づけるための
    簡単な adapter

 ・SimpleCursorAdapter
    Cursor から取り出したデータをXMLファイルで定義された
    TextView や ImageView に紐づけるための簡単な adapter






 

Android ExpandableListAdapter

ExpandableList 用の Adapter を整理してみた。
独自 Adapter を作る場合は BaseExpandableListAdapter を継承するが定石かな。


■ public interface
 ・ExpandableListAdapter
 ・HeterogeneousExpandableList


■ public abstract class
 ・BaseExpandableListAdapter
   ExpandableListAdapter の基底クラス。expandable list view に
   データを紐づける。

 ・CursorTreeAdapter
   Cursor から取り出したデータを ExpandableListview widget に
   紐づける。

 ・ResourceCursorTreeAdapter
   ExpandableListAdapter を単純化したもの。
   XMLファイルで定義された View を作成する。

 ・SimpleCursorTreeAdapter
   Cursor から取り出したデータをXMLファイル内で定義されている
   TextView や ImageView に紐づける簡単な Adapter

■ public class
 ・SimpleExpandableListAdapter
   static data を XMLファイル内で定義された group view と
   child view に紐づける簡単な Adapter





 

2010年10月5日火曜日

Android SparseBooleanArray

SparseBooleanArray

booleans と integers の map。通常の boolean の配列と異なりインデックス内にギャップが存在できる。つまり、 HashMap をつかって Integers を Booleans に割り当てるよりも効果的。

void append(int key, boolean value)
Puts a key/value pair into the array, optimizing for the case where the key is greater than all existing keys in the array.

void clear()
Removes all key-value mappings from this SparseBooleanArray.

void delete(int key)
Removes the mapping from the specified key, if there was any.

boolean get(int key, boolean valueIfKeyNotFound)
Gets the boolean mapped from the specified key, or the specified value if no such mapping has been made.

boolean get(int key)
Gets the boolean mapped from the specified key, or false if no such mapping has been made.

int indexOfKey(int key)
Returns the index for which keyAt(int) would return the specified key, or a negative number if the specified key is not mapped.

int indexOfValue(boolean value)
Returns an index for which valueAt(int) would return the specified key, or a negative number if no keys map to the specified value.

int keyAt(int index)
Given an index in the range 0...size()-1, returns the key from the indexth key-value mapping that this SparseBooleanArray stores.

void put(int key, boolean value)
Adds a mapping from the specified key to the specified value, replacing the previous mapping from the specified key if there was one.

int size()
Returns the number of key-value mappings that this SparseBooleanArray currently stores.

boolean valueAt(int index)
Given an index in the range 0...size()-1, returns the value from the indexth key-value mapping that this SparseBooleanArray stores.

2010年10月4日月曜日

BootCamp を開催したよ。

9月28日は Google Developer Day でしたが、
その前日の 9月27日に GTUG 主催で BootCamp を
開催しました。

その1週間ほど前に、急遽あるセッションの代役
をすることになりまして、、、、なりました。

スライドです。



BootCamp というのは、GDDの前に普段関わっているテクノロジーの
とは異なるテクノロジーの知識を補うための初心者向けのイベントです。

例えば、普段は主に Android をやっているんだけど
HTML5にも興味がある。でも GDD のセッションは初心者むけ
ではないので、GDDでは Android を聞いて、BootCamp では
HTML5 の初心者向けセッションを聞く、というためのものです。

いつもどの程度のレベルの人が聞きにくるのか悩むところで、
BootCamp =「初心者 + あんまりそのテクノロジーに詳しくない人」
向けのイベント(私の中の認識では)なので、なるべくコードの話
などは少なくして、概念的な内容にしました。

アンケートをみると、わかりやすくてよかったという人と
もっと突っ込んだ内容を期待していたという人が8:2くらいでした。

もうちょっと余裕もって、ライブコーディングとかできるように
なりたいなぁ。。。


懇親会のご飯すごくおいしかったです。
Googler やGDDのスピーカーも懇親会にたくさんきてくれました。
パメラとかトニー・チャンとか。フレッドさんもきてたな。



参加者の方に Huawai の IDEOS を見せてもらいましたー!
QVGA でなんと Android 2.2 !






 

2010年10月3日日曜日

Android moveTaskToBack

Activity のリファレンスを眺めていたら、こんなの見つけました第2弾。

public boolean moveTaskToBack (boolean nonRoot)

この Activity が含まれる Task を Activity stack の後方に移動する。
Task 内の Activity の順番は変わらない。

引数 nonRoot
 true : Task 内のどの Activity の場合でも移動する
 false : Activity が Task の root の場合のみ移動する

戻り値
 true : Task は移動した(もしくは、すでに後方にいる)
 false : Task の移動に失敗した


これだけだとつまらないので、これが呼ばれたときのライフサイクルを
調べてみた。

moveTaskToBack() : true

onSaveInstanceState()

onResume()

onWindowFocusChanged (boolean hasFocus) : false

onCreateDescription() : null

onStop()


ちゃんと Destroy() されてませんでした。

 

Android Configuration Change

Configuration が変わったことをハンドルしたい場合、

AndroidManifest.xml の <activity> タグに

 android:configChanges

を設定すると、configuration が変わったときに、

 public void onConfigurationChanged(Configuration newConfig)

が呼ばれます。(Android 画面の縦横切り替え時に元の画面を保存)
例えば、

 android:configChanges="orientation"

とすると、画面が回転したときに onConfigurationChanged
が呼ばれます。

 getResources().getConfiguration()

で取得しておいた Configuration と 引数の newConfig の diff
をとることで、変化した configuration parameter の bit field
が取得できます。

しかし、この configChanges 設定をすると、Activity が再生成
されなくなるため(= onDestroy(), onCreate() は呼ばれなくなるため)、
画面のレイアウトが変わらない場合にはいいのですが、
縦横で画面のレイアウトを変えたい場合には使えません。

この設定をしない (= onConfigurationChanged が呼ばれない)
場合は、

 onDestroy() の中で、getChangingConfigurations()

を呼ぶことで、どの Configuration の変化によって
Activity が終了しようとしているかを検知することができます。

このメソッドの戻り値が変化した configuration parameter の
bit field になります。この bit field は ActivityInfo class
の Constants で定義されています。
ちなみに、orientation change の場合の戻り値は 128 =(0x00000080) です。

bit maskvalue
CONFIG_MCC1 (0x00000001)
CONFIG_MNC2 (0x00000002)
CONFIG_LOCALE4 (0x00000004)
CONFIG_TOUCHSCREEN8 (0x00000008)
CONFIG_KEYBOARD16 (0x00000010)
CONFIG_KEYBOARD_HIDDEN32 (0x00000020)
CONFIG_NAVIGATION64 (0x00000040)
CONFIG_ORIENTATION128 (0x00000080)
CONFIG_SCREEN_LAYOUT256 (0x00000100)
CONFIG_UI_MODE512 (0x00000200)
CONFIG_FONT_SCALE1073741824 (0x40000000)



@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy()");
Log.d(TAG, "isFinishing : " + isFinishing());
int change = getChangingConfigurations();
Log.d(TAG, "getChangingConfigurations() :" + change + " (" + String.format("0x%08x", change) + ")");
}

2010年10月2日土曜日

Android Home キーが押されたことを検知する。

Android で ホームキー (Home Key) を検知する方法はないものかと

Activity のメソッドを見ていて

 protected void onUserLeaveHint()

というのを見つけました。

------

Called as part of the activity lifecycle when an activity is about to go into the background as the result of user choice. For example, when the user presses the Home key, onUserLeaveHint() will be called, but when an incoming phone call causes the in-call Activity to be automatically brought to the foreground, onUserLeaveHint() will not be called on the activity being interrupted. In cases when it is invoked, this method is called right before the activity's onPause() callback.

This callback and onUserInteraction() are intended to help activities manage status bar notifications intelligently; specifically, for helping activities determine the proper time to cancel a notfication.

---

Activity のライフサイクルの一部として、Activity が ユーザーの選択の結果 background に行くときに呼ばれる。例えば、ユーザーが Home key を押したとき onUserLeaveHint() が呼ばれる。しかし、着信によって in-call Activity が自動で foreground に来るような場合は onUserLeaveHint() は呼ばれない。onUserLeaveHint() は onPause() の直前に呼ばれる。

このコールバックと onUserInteraction() は Activity がステータスバーの notification を賢く処理するために意図されたものである。特に、Activity が notification をキャンセルする適切な時間を決定するための指標となる。

---

で、試してみた。


-- Launcher から起動 --

onApplyThemeResource(Resources.Theme theme, int resid, boolean first)

onCreate()

onWindowAttributesChanged (WindowManager.LayoutParams params)

onContentChanged()

onStart()

(*onRestoreInstanceState(Bundle))

onPostCreate(Bundle)

onResume()

onPostResume()

onAttachedToWindow()

onWindowFocusChanged (boolean hasFocus) : true

-- home key 押下 --

onUserInteraction()

onUserLeaveHint()

onSaveInstanceState()

onResume()

onPostResume()

onWindowFocusChanged (boolean hasFocus) : false

onCreateDescription() : null

onStop()

-- Home key 長押しから起動 --

onRestart()

onStart()

onResume()

onWindowFocusChanged (boolean hasFocus) : true

-- Back key で終了 --

onUserInteraction()

onResume()

onWindowFocusChanged (boolean hasFocus) : false

onStop()

onDestroy()

onDetachedFromWindow()

-- Launcher から起動 --

onApplyThemeResource() : resid = 16973829, first = true

onCreate()

onWindowAttributesChanged (WindowManager.LayoutParams params)

onContentChanged()

onStart()

onResume()

onAttachedToWindow()

onWindowFocusChanged (boolean hasFocus) : true




 確かに Home key を押したときは呼ばれていて、
 Back key を押したときは呼ばれていない。


 

Android Drawable Mutations

Drawable Mutations

訳&試してみた
サンプル部分は全入れ替えですw

-----

Android の drawable はアプリを作る上でとても便利である。

Drawable は一般的に View に関連付けられている拡張可能な描画用コンテナであり、画像を表示するための BitmapDrawable や 図形やグラデーションなどを描画するための ShapeDrawable などがある。(Drawable Resources の一覧)これらを組み合わせて複雑なレンダリングを実現することができる。

Drawable を使うことで、簡単に widget のレンダリングをカスタマイズできる。実際、この機能はすごく便利なため、ほとんどの標準 Android apps と widgets は drawables を使って作られており、Android framework のコアには約700の drawables が使われている。

drawables はシステム内で非常によく使われているため、Android は resource から読み込むときにこれらを最適化している。例えば、Button を作成するたびに新しい drawable が framework resoruces (android.R.drawable.btn_default) から読み込まれる。これはアプリ中のすべてのボタンが異なる drawable instance をその背景として使うことを意味している。しかし、これらすべての drawable は "constant state" と呼ばれる通常の状態を共有している。

この通常状態は、使用する drawable のタイプによって変わるが、一般的には resource によって定義可能なすべてのプロパティを含む。Button の場合、通常状態は bitmap 画像を含む。このように、アプリ中のすべてのボタンは同じ bitmap を共有し多くのメモリを節約している。

以下は、2つの異なる view の background に同じ画像をリソースを割り当てたときに、どのエンティティが生成されるかを表したダイアグラムである。2つの drawable が生成されるが、それらは同じ constant state を共有する。



この状態共有の機能は、メモリの無駄遣いを大幅に節約できるが、個々の drawable のプロパティを編集しようとした場合に問題が起こる。

ListView の各要素に星画像★が付いているアプリがあるとする。例えば、お気に入りの場合は★を不透明にし、そうでない場合は透明にしたいとする。そのために、ListView の getView() を次のように実装したとする。
# めんどいので、position が奇数は不透明、偶数は透明にする


public View getView(int position, View convertView, ViewGroup parent)

ViewHolder holder;

if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item,
parent, false);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);

convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}

Drawable mIcon = mContext.getResources()
.getDrawable(R.drawable.ic_menu_star);

if ((position & 1) == 1) {
mIcon.setAlpha(255); // opaque
} else {
mIcon.setAlpha(70); // translucent
}

holder.text.setText(DATA[position]);
holder.icon.setImageDrawable(mIcon);

return convertView;
}


残念ながら、このコードだとすべての drawables が同じ透明度になる。




この結果は constant state で説明できる。リストの各アイテムに対して新しい drawable instance を取得したとしても、BitmapDrawable の場合、constant state は同じままになる。このように、1つの drawable instance の透明度を変化させると、他のすべての instance の透明度も変化する。この問題を Android 1.0 と 1.1 で関係するのは簡単ではなかった。

Android 1.5 以上なら、mutate() を使うことで、とても簡単にこの問題を解決できる。 このメソッドを drawable に対して発行すると、drawable の constant state が多重化されるので、他の drawable へ影響させずに個々のプロパティを変えることができる。ただし、drawable を mutate した後であっても、bitmap は共有されたままになる。

drawable で mutate() を呼び出すと次のようになる。



mutate() を使って、先のコードを変更する。

Drawable mIcon = mContext.getResources()
.getDrawable(R.drawable.ic_menu_star);

if ((position & 1) == 1) {
mIcon.mutate().setAlpha(255); // opaque
} else {
mIcon.mutate().setAlpha(70); // translucent
}


mutate() は自身の drawable を返すため、chain method call が可能である。ただし、これは新しいインスタンスを生成するわけではない。これで正しく表示されるようになる。




-----
全コードはこちら


public class MainActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drawablemutate);
}

static class ViewHolder {
TextView text;
ImageView icon;
}

private class MyAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mInflater;

private String[] DATA = { "Henry IV (1)", "Henry V",
"Henry VIII", "Richard II",
"Richard III", "Merchant of Venice",
"Othello", "King Lear" };

public MyAdapter(Context context) {
mContext = context;
mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
}

public int getCount() {
return DATA.length;
}

public Object getItem(int position) {
return position;
}

public long getItemId(int position) {
return position;
}

public View getView(int position, View convertView, ViewGroup parent)

ViewHolder holder;

if (convertView == null) {
convertView = mInflater.inflate(R.layout.list_item,
parent, false);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
holder.icon = (ImageView) convertView.findViewById(R.id.icon);

convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}

Drawable mIcon = mContext.getResources()
.getDrawable(R.drawable.ic_menu_star);

if ((position & 1) == 1) {
mIcon.setAlpha(255); // opaque
} else {
mIcon.setAlpha(70); // translucent
}

holder.text.setText(DATA[position]);
holder.icon.setImageDrawable(mIcon);

return convertView;
}
}

2010年10月1日金曜日

Chrome To Libraroid

 C2DM (Cloud to Device Messaging) を使って
 Libraroid に組み込んでみました。

 動画です。



 C2DMの invitation mail に

> Please note that by default, all new sender accounts
> are held to a development-only quota. This quota is
> sufficient for development and exploratory purposes.
> Before you launch a C2DM-enabled application to a
> large audience, please reply to this email to discuss a
> production-level quota.

 とあったので、かってにマーケットに解き放つのは
 ダメなのだろうと思って公開してません。


 GAE for Java + Google Chrome Extension + Android app
 という構成です。

 基本的には公開されている ChromeToPhone のソースコード
 をそのまま流用してます。
 http://code.google.com/p/chrometophone/
 Auth Token が若干面倒だったくらいです。

 C2DM server からの Intent を ChromeToLibraroid という
 Android app で受信して、取り出したキーワードで
 Libraroid に ACTION.SEARCH を投げています。
 こうすると、対象のアプリ(この場合の Libraroid)は変更せずに
 2.2 以上の端末のみ、機能として拡張できるのでメンテが楽です。

 ChromeToPhone のコードを Slim3 を使って
 変更したやつを近々公開よてい(ホントかw)