2010年3月21日日曜日

Android Ubuntu で adb devices ???? を直す

前回のデ部で Xperia を触ることができたんですけど、
マイのぱそ(Ubuntu 9.10)に USB でつないでも
認識してくれないのです。

これは Droid とかでもなるんですけど、
こんなふうに ???? ってなってしまうんです。
(ちなみに、HT96HLF02010 は HT-03A)


$
List of devices attached
HT96HLF02010 device
???????????? no permissions


で、デバイスを認識するルールを設定すればいいそうで
(教えてもらったので詳細はよく知らないのです。。。)
/etc/udev/rules.d/ の中の **-android.rules をコピーして
idVendor を 0fce にするそうです。


$ cat /etc/udev/rules.d/51-android.rules
SUBSYSTEM=="usb",SYSFS{idVendor}=="0bb4",MODE="0666"
$ sudo cp /etc/udev/rules.d/51-android.rules /etc/udev/rules.d/52-android.rules
$ sudo emacs /etc/udev/rules.d/52-android.rules
$ cat /etc/udev/rules.d/52-android.rules
SUBSYSTEM=="usb",SYSFS{idVendor}=="0fce",MODE="0666"


それで、root 権限で adb をリスタートします。
リンクが通ってないので、adb をフルパスで実行します。
(/usr/local/android-sdk/ は自分の環境のパスに
置き換えてください。)


$ adb kill-server ; sudo /usr/local/android-sdk/tools/adb start-server
* daemon not running. starting it now *
* daemon started successfully *


Android developers のページだとここ
Setting up a Device for Development


メモ
ManufactureridVendor
Acer0502
Dell Inc.413c
HTC Corporation0bb4
Kyocera0482
LG Electronics Inc.1004
Motorola MDS22b8
Samsung Electronics Co., Ltd.04e8
Sharp Corporation04dd
Sony Ericsson Mobile Communications AB0fce


参照元
 ・idVendorの割り当てリスト - device139の日記 -
 ・http://www.usb.org/developers/tools/ 内の usb.if ファイル (Company List)


(Windows OS や Mac OS でのやり方は別途あると思います。。。)

Android Parcelable を使ってクラスのメンバを一時保存

さて、前回のエントリで、Bundle で状態を保存する方法を
書きました。
Android Bundle で状態を保存

ここでは、Bundle の Method (例えば putString と getString)
を使ってパラメータを保存する方法を紹介しました。

しかーし、ここで問題が発生

「独自にデータクラスを用意していて、このクラスのメンバごと保存したいんだけど…」

さぁ、この場合どうする?

ここで登場するのが Parcelable さんです。

Parcelable はリファレンス
http://developer.android.com/intl/ja/reference/android/os/Parcelable.html
に書いてあるように、Parcel にデータを書き/読みするためのインタフェースです。

"Interface for classes whose instances can be written to and restored from a Parcel. Classes implementing the Parcelable interface must also have a static field called CREATOR, which is an object implementing the Parcelable.Creator interface. "

Parcelable インタフェースを実装したクラスは、Parcelable.Creator インタフェースを実装するオブジェクトである CREATOR と呼ばれる静的フィールドを持たなくてはなりません。

そして、ご丁寧に Parcelable インタフェースを実装するクラスの例が載ってます。


public class MyParcelable implements Parcelable {
private int mData;

public int describeContents() {
return 0;
}

public void writeToParcel(Parcel out, int flags) {
out.writeInt(mData);
}

public static final Parcelable.Creator<MyParcelable> CREATOR
= new Parcelable.Creator<MyParcelable>() {
public MyParcelable createFromParcel(Parcel in) {
return new MyParcelable(in);
}

public MyParcelable[] newArray(int size) {
return new MyParcelable[size];
}
};

private MyParcelable(Parcel in) {
mData = in.readInt();
}
}



ここでは、Parcel に保存するパラメータとして、mData が1つですが、

例えば、名前、年齢、アドレスをメンバに持つ
クラス(ContactParcelable)を作るとこんな感じになります。



public class ContactParcelable implements Parcelable {
private String name;
private int age;
private String address;

public int describeContents() {
return 0;
}

public void writeToParcel(Parcel out, int flags) {
out.writeString(name);
out.writeInt(age);
out.writeString(address);
}

public static final Parcelable.Creator<ContactParcelable> CREATOR
= new Parcelable.Creator<ContactParcelable>() {
public ContactParcelable createFromParcel(Parcel in) {
return new ContactParcelable(in);
}

public ContactParcelable[] newArray(int size) {
return new ContactParcelable[size];
}
};

private ContactParcelable(Parcel in) {
name = in.readString();
age = in.readInt();
address = in.readString();
}

public ContactParcelable(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
}


writeToParcel(Parcel out, int flags)
で書き込む順番と
ContactParcelable(Parcel in)
で読み出す順番は同じにしなければなりません。

で、このクラスを
onSaveInstanceState(Bundle) で保存し、
onRestoreInstanceState(Bundle)
で読み出すには、こんな感じでOK


ContactParcelable contactParcelable;

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

contactParcelable = new ContactParcelable("あんどろいど", 2, "android@gmail.com");
}

@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/* ここで状態を保存 */
outState.putParcelable("contact", contactParcelable);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
/* ここで保存した状態を読み出して設定 */
contactParcelable = savedInstanceState.getParcelable("contact");
}



さらにさらに、
もし、メンバがデータクラスのリストだった場合はどうなるか
この場合も Parcelable を実装します。

ContactParcelable のリストをメンバに持つ AllContactParcelable
を一時保存する場合


public class AllContactParcelable implements Parcelable {

private ArrayList<ContactParcelable> contactList;

public int describeContents() {
return 0;
}

public void writeToParcel(Parcel out, int flags) {
out.writeTypedList(contactList);
}

public static final Parcelable.Creator<AllContactParcelable> CREATOR
= new Parcelable.Creator<AllContactParcelable>() {
public AllContactParcelable createFromParcel(Parcel in) {
return new AllContactParcelable(in);
}

public AllContactParcelable[] newArray(int size) {
return new AllContactParcelable[size];
}
};

private AllContactParcelable(Parcel in) {
contactList = in.createTypedArrayList(ContactParcelable.CREATOR);
}

public AllContactParcelable(ArrayList<ContactParcelable> contactList) {
this.contactList = contactList;
}
}


out.writeTypedList(contactList);
で書き込みし、
contactList = in.createTypedArrayList(ContactParcelable.CREATOR);
で読み出すのがポイント

onSaveInstanceState(Bundle)

onRestoreInstanceState(Bundle)
での実装は同じ



参考ページ
[Android] android.os.Parcelable / Parcel
adakodaさんありがとうございます!

http://developer.android.com/intl/ja/reference/android/os/Bundle.html
http://developer.android.com/intl/ja/reference/android/os/Bundle.html#putParcelable%28java.lang.String,%20android.os.Parcelable%29
http://developer.android.com/intl/ja/reference/android/os/Bundle.html#getParcelable%28java.lang.String%29

2010年3月19日金曜日

Android 特定の Intent (Action) を処理できる Activity (アプリ)の一覧を取得

なんか見つからなかったので作りました。

android.content.pm パッケージの
PackageManagerクラスの
 "queryIntentActivities (Intent intent, int flags)"
で取得できます。

こんな感じ


PackageManager pm = getPackageManager();

List resolveInfo = pm.queryIntentActivities(new Intent(Intent.ACTION_SEARCH), PackageManager.MATCH_DEFAULT_ONLY);

StringBuffer sb = new StringBuffer();

for(int i = 0; i < resolveInfo.size(); i++) {
ResolveInfo info = resolveInfo.get(i);
ActivityInfo activityinfo = info.activityInfo;

sb.append(activityinfo.packageName + "\n");
}
textView.setText(sb.toString());


ACTION_SEND などは、setType() しないと
パッケージが引っかかりません。
なので、こんな感じで

PackageManager pm = getPackageManager();

Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("text/plain");

List resolveInfo = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);


さらにさらに、
 とってきた activity の一覧をソートするには
 "Collections" と "ResolveInfo.DisplayNameComparator"
 を使います。

こんな感じ

// sort
Collections.sort(resolveInfo, new ResolveInfo.DisplayNameComparator(pm));


Inflator を使って TableLayout にしました。

ソースとアプリも公開しちゃいます。
GetIntentList.apk

GetIntentList.zip


参考ページ
http://developer.android.com/intl/ja/reference/android/content/pm/PackageManager.html#queryIntentActivities(android.content.Intent, int)
http://developer.android.com/intl/ja/guide/topics/intents/intents-filters.html#imatch

Android Bundle で状態を保存

自分のアプリから Intent で別のアプリに飛ぶと onPause() が呼ばれて別のアプリが foreground に来ます。ここから自分のアプリに戻る場合、background にいる時間が短かったり他のアプリがメモリを要求しなければ onResume() が呼ばれて、Intent が呼ばれる前の画面の状態に戻れます。

しかし、Activity が長時間表示されなかったり、別のアプリがメモリを要求したりすると、自分のアプリに戻ったときに、前の画面の状態にはなりません。この場合に前の画面の状態にするには、その状態を意識的に保存する必要があります。

では、どうやって保存するのか、

1.タイミング

onSaveInstanceState(Bundle) で保存し、
onRestoreInstanceState(Bundle) で読み出します。

こんな感じ


@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/* ここで状態を保存 */
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
/* ここで保存した状態を読み出して設定 */
}



2.保存する

onSaveInstanceState の引数である
Bundle にパラメータの値を保存します。

Method は
http://developer.android.com/intl/ja/reference/android/os/Bundle.html
に載ってますが、主なものをあげると

void putBoolean(String key, boolean value)
void putByte(String key, byte value)
void putChar(String key, char value)
void putCharSequence(String key, CharSequence value)
void putDouble(String key, double value)
void putFloat(String key, float value)
void putInt(String key, int value)
void putLong(String key, long value)
void putParcelable(String key, Parcelable value)
void putShort(String key, short value)
void putString(String key, String value)

などがあります。


3.読み出す

では、保存した値を読み出すにはどうするのか。ここでも onRestoreInstanceState の引数の Bundle から読み出します。

主なものをあげると

Object get(String key)
boolean getBoolean(String key)
boolean getBoolean(String key, boolean defaultValue)
Byte getByte(String key, byte defaultValue)
byte getByte(String key)
char getChar(String key, char defaultValue)
char getChar(String key)
CharSequence getCharSequence(String key)
double getDouble(String key, double defaultValue)
double getDouble(String key)
float getFloat(String key, float defaultValue)
float getFloat(String key)
int getInt(String key)
int getInt(String key, int defaultValue)
long getLong(String key, long defaultValue)
long getLong(String key)
<T extends Parcelable> T getParcelable(String key)
short getShort(String key)
short getShort(String key, short defaultValue)
String getString(String key)

などがあります。


4.例

ということで、こんな感じになります。


private String name;
private int age;
private String address;


@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("nameKey", name);
outState.putInt("ageKey", age);
outState.putString("addressKey", address);
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
name = savedInstanceState.getString("nameKey");
age = savedInstanceState.getInt("ageKey", -1);
address = savedInstanceState.getString("addressKey");
}



 

2010年3月18日木曜日

Android 画面の縦横切り替え時に元の画面を保存

画面の縦横が切り替わると、Activity は一度終了(onPause(), onStop(), onDestroy() )させられちゃいます。
(Activity が foreground や visibleだった時は、新たなインスタンス(前のインスタンスがonSaveInstanceState メソッドで作った savedInstance を伴って)が作られます)

どうしてそうなるかと言うと、
 デバイスのコンフィグレーション(画面の向き、入力デバイス、言語など)
 が変更されたら、UIはそのコンフィグレーションにマッチするように更新
 する必要があるからなんです。
 例えば、縦用のレイアウトと横用のレイアウトが用意されている場合は、
 リソースの再取得が必要になりますよね。

でも、コンフィグレーションの変更があっても Activity の再スタートしたくない場合はどうすればいいのでしょう?

その場合は AndroidManifest.xml の activity タグに

android:configChanges=["mcc", "mnc", "locale",
           "touchscreen", "keyboard",
           "keyboardHidden",
           "navigation", "orientation",
           "fontScale"]

を追加します。
複数指定する場合は "|" でつなぎます。こんな感じ。
"locale|navigation|orientation"

"mcc"
  IMSI mobile country code (MCC) が変わったとき
  (つまり、SIMが検出されて、MCCがアップデートされたとき)

"mnc"
  IMSI mobile network code (MNC) が変わったとき
  (つまり、SIMが検出されて、MNCがアップデートされたとき)

"locale"
  ロケールが変わったとき(例えば、言語を変えたとき)

"touchscreen"
  タッチスクリーンが変わったとき(通常は呼ばれることはない)

"keyboard"
  キーボードのタイプが変わったとき
  (例えば、外部キーボードを接続したとき)

"keyboardHidden"
  キーボードへのアクセスが変わったとき
  (例えば、ハードキーボードをスライドインしたとき(とか))

"navigation"
  ナビゲーションタイプが変わったとき(通常は呼ばれることはない)

"orientation"
  画面の縦横が切り替わったとき

"fontScale"
  フォントサイズが変更されたとき

ここで宣言した種類のコンフィグレーションの変更があると、アクティビティは再スタートされる代わりに onConfigurationChanged が呼ばれます。



参考ページはここです。
 ・Handling the Configuration Change Yourself
 ・Android SDK WG 第1回 セッション(2008.10.25)
 ・http://developer.android.com/intl/ja/guide/topics/manifest/activity-element.html
 

エミュレータで画面の縦横を切り替えるには(ctrl + F12)です。
このときは、
 orientation と keyboardHidden (G1を意識している?)
の2つのコンフィグ値が変更になります

2010年3月16日火曜日

Amazon API レビュー情報を取得する

ResponseGroup に Reviews を入れればOK

こんな感じで

ResponseGroup=Request,Medium,Reviews

2010年3月9日火曜日

Androidアプリ Libraroid - 図書館予約 - 使い方

Libraroid は図書館に本があるかどうか調べて予約できるアプリです。

公式のサポートページはこちらです。
http://d.hatena.ne.jp/libraroid/


使い方

- 本を調べる

調べたい本のタイトルや著者名、キーワードなどを入力して検索ボタンをクリックしてください。



各本の
  ・画像
  ・タイトル
  ・著者名
  ・出版社
  ・本の形式
  ・値段
  ・出版日
  ・レビュー

を見ることができます。

音声入力する場合は、検索ボタンを長押しすることで
マイク画面が起動します。
(設定で「音声検索を有効にする」にチェックを入れる必要があります)




次の本を見る場合は右矢印ボタン(>)を押してください。
前の本に戻るときは左矢印ボタン(<)を押してください。

図書館にその本があるかどうかが下に表示されます。




図書館に所蔵がない場合は次のようなメッセージが表示されます。



商品のレビューを見る場合は本の画像をタップしてください。
(設定で「レビューを表示」にチェックを入れる必要があります)



タイトル、著者名、出版者で再検索する場合はタイトル、著者名、出版者
などが表示されている部分をロングタップしてください。




Amazon の商品ページにジャンプするには、下の左端のカートボタンをタップしてください。




直前に入力した検索キーワードから検索したい場合は、ハードキーの検索ボタンを押してください。
次のような画面になります。
ここから、入力候補を選択することで検索できます。




過去に Libraroid で検索したキーワードであれば、クイック検索ボックス(ホーム画面の検索バーのこと)にキーワードを入力したときに、Libraroid での検索が候補に出る場合があります。





- 検索条件を変える

本の検索条件を変更できます。
トップの画面で [menu] を押すと、 [Libraroidについて] と [設定]
がでるので、[設定] を押してください。



設定を押すと、次の画面になります。



検索パラメータとして、
 ・検索対象
 ・検索条件
 ・表示順
が設定できます。

検索対象は "和書" と "洋書" から選べます。



検索条件は "キーワード", "タイトル", "著者名", "出版社" から選べます。



表示順は "キーワードに関連", "売れている順", "発売日順", "アルファベット順"
から選べます。




- 検索図書館の設定

[menu] -> [設定] -> [図書館] を押すと、現在選択している図書館の
アカウントや受け取り図書館などが設定できます。



別の図書館を設定するには、ここで図書館をクリックします。
クリックすると、簡易地図が表示されます。
設定したい図書館がある地域をタップしてください。



地域をタップすると、その地域の都道府県リストが出ます。
設定したい図書館がある都道府県を選択してください。



都道府県をタップすると、その都道府県で対応している図書館のリスト
が表示されます。設定した図書館をタップしてください。




v2.5.42 から2つの図書館で同時検索できるようになりました。
注:この機能を使うと図書館2つ分を検索するので、あたりまえですが時間がかかります。(Amazonの検索にかかる時間はかわりません)

図書館2も同時に検索 にチェックをいれると、図書館2の設定が有効になります。
設定方法は1つ目の図書館と同じです。





- アプリの設定

・コミックを検索対称からはずす
・設定から図書館を変更して、メインページに戻ったときに自動で再検索する
・予約メッセージ完了のメッセージをダイアログで確認する
・レビューを表示する

かどうかが設定できます。
設定は [menu] -> [設定] -> 各設定 checkbox から行えます。






- 本の予約

本を予約するには、図書館のオンライン予約アカウントが必要になります。
またこのアプリで予約した本の状況は各図書館のホームページで
確認できます。

アカウントは [menu] -> [設定] -> [図書館] から設定できます。
カード番号・パスワード・受取希望館・連絡方法 など
を設定してください。




検索した本が図書館にある場合、図書館の検索結果の右端に、ボタンが表示されるようになります。
このボタンをクリックすると予約確認ダイアログが表示されます。
カード番号と受取図書館を確認して予約ボタンを押してください。




- MyBookList

MyBookListに本を記録しておいて、そのリストから図書館検索ができます。

MyBookListに本を追加するには、下の右端のプラスマークのボタンをクリックしてください。



MyBookListを表示するには、下の右から2番目の本マークボタンをクリックしてください。

MyBookList は次のようなリストで表示されます。



リストを長押しすると、次のようにアクションメニューが表示されます。



図書館検索を押すと、蔵書検索ができます。


- バーコードから検索

バーコードリーダーがインストールされている場合、
右上(検索ボタンの左側)にバーコードボタンが表示されます。

このボタンをクリックすると、バーコードリーダーが起動するので、
書籍の背面のバーコードを読み込んでください。
読み込んだISBNから図書館検索が実行されます。

- 図書館の所蔵情報を送る

左下の吹き出しボタンをクリックすると、所蔵情報をどのアプリに送るか
の選択画面がでます。
(対応できるアプリが1つしかない場合は選択画面はでません)
送りたいアプリをクリックしてください。





対応図書館マップ

より大きな地図で Libraroid対応図書館マップ を表示

-----

エラーのご報告やご要望は
anzai.y.aa@gmail.com
までお願いします。

もっともっと改善していきたいと思ってます。
今後ともよろしくお願いします。

Androidアプリ Libraroid - 図書館予約 - アップデート情報

-アップデート

・v2.5.42

 ・検索ボックスから検索したときにソフトキーボードが消えるように修正

 ・2つまで図書館を同時検索可能になりました。
  設定から「図書館2も同時に検索」にチェックをいれて図書館2を設定してください。

 ・レイアウトの変更

 ・Quick Search Box で検索できるように改良 

 ・ACTION_SEARCH の検索で過去の入力が候補として出るように改良


・v1.5.42
 ・対応図書館の追加
  ・東京都
   ・品川区立図書館
  ・秋田県
   ・秋田市立図書館
 ・調布市立図書館の予約完了メッセージの表示バグを修正

 ・バグ修正

 ・横浜図書館に接続できない問題を解消しました

 ・ネット接続の問題で検索に失敗した場合、そのことを表示するように変更(いままでは一致する本がないと表示されていた)

 ・ACTION_SEARCH に対応するように改良


・v1.4.40
 ・対応図書館の追加
  ・東京都
   ・三鷹市立図書館
   ・調布市立図書館
  ・神奈川県
   ・逗子市立図書館
   ・二宮町立図書館
  ・埼玉県
   ・新座市立図書館
  ・徳島県
   ・松茂町立図書館


・v1.3.35
  ・対応図書館の追加
    ・川口市立図書館
  ・小平市立図書館の予約時のメッセージのバグを修正
  ・世田谷区立図書館の予約できないバグを修正
  ・御前崎市立図書館の予約できないかもしれないバグを修正
  ・富士吉田市立図書館の予約できないかもしれないバグを修正
  ・西東京市立図書館の予約できないかもしれないバグを修正
  ・既にMyBookListにある本はMyBookListに追加できない&メッセージを表示
  ・コミックを検索対象外にするかどうか選択できる設定を追加
  ・図書館を変更した場合に、自動で再検索するかどうか選択できる設定を追加
  ・予約したときのメッセージをToastで表示かDialogで表示かを選択できる設定を追加
  ・検索する図書館を簡易地図から選択できるように変更


・v1.2.33
  ・検索条件をタイトルバーの真ん中に表示
  ・テキストボックスでEnterを押すことで検索開始
  ・UIデザインの変更
  ・バーコードから本を検索
  ・図書館の蔵書情報をツイッタークライアントやメーラーに渡す

  ・対応図書館の追加
    京都市立図書館
    山口市立図書館
    新潟市立図書館
    札幌市立図書館(検索のみ)


・v1.2.29
  ・「Libraroidについて」をMenuに追加
  ・対応図書館の追加
   ・茨城県
     ・取手市立図書館
   ・埼玉県
     ・三郷市立図書館
     ・三芳町立図書館
     ・東松山市立図書館
     ・入間市立図書館
   ・山梨県
     ・富士吉田市立図書館
   ・滋賀県
     ・野洲市立図書館
   ・神奈川県
     ・藤沢市立図書館
     ・平塚市立図書館
   ・静岡県
     ・御前崎市立図書館
     ・三島市立図書館
     ・富士宮市立図書館
   ・東京都
     ・荒川区立図書館
     ・中央区立図書館
     ・豊島区立図書館
     ・墨田区立図書館
     ・小平市立図書館
     ・西東京市立図書館
     ・多摩市立図書館
   ・徳島県
     ・阿南市立図書館


・v1.2.9
  ・対応図書館の追加
   ・大田区立図書館
   ・練馬区立図書館
   ・世田谷区立図書館
   ・葛飾区立図書館
   ・武蔵野市立図書館
   ・沖縄県沖縄市立図書館
   ・沖縄県金武町立図書館
  ・バグフィックス


・v1.0.1
  ・横浜市立図書館
  ・レイアウトの変更
  ・検索対象の本の情報を全部見れる(前はランキングの10位まで)
  ・翻訳者、監修者なども表示
  ・設定画面で現在の設定値を表示


・v1.0.0
  ・川崎市立図書館対応

2010年3月8日月曜日

Android SearchManager ソフトキーボードを消すぜ!

入力ボックスに文字を入れて、ボタンを押したときに
ソフトキーボードを消す方法は以前のエントリに載せました。

Android ボタンを押したときにソフトキーボードを消す

InputMethodManager を使う方法です。

で、

SearchManager で onNewIntent() でこれをやっても
ソフトキーボードが消えない消えない。

まぁ。よくよく考えれば、検索ボックスにソフトキーボードがバインド
されているんだから、intent が帰ってきたところじゃなくて、
検索ボックスが消された(=dismiss) されたときに処理しないと
だめじゃん。ということに気づきました。

ということで、

SearchManager の onDismissListener でやりましょう。

コードはこんな感じ

onCreate() の中です。

SearchManager searchManager = (SearchManager) getSystemService(SEARCH_SERVICE);
searchManager.setOnDismissListener(new SearchManager.OnDismissListener() {

@Override
public void onDismiss() {
InputMethodManager inputMethodManager = (InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(getCurrentFocus().getWindowToken(), 0);
}
});

2010年3月6日土曜日

Android ScrollBar (ScrollView) スクロールバーをカスタマイズ -

スクロールバーの見た目を変えてみました。

■ スクロールバーの幅を変更

 android:scrollbarSize を指定します。

 寸法値("14.5sp"のような単位付き浮動小数点)で指定
 利用可能な単位は
  px (ピクセル)、
  dp(dip) (密度非依存ピクセル)
  sp (倍率非依存ピクセル)
  in (インチ)
  mm (ミリメータ)


■ 横スクロールバーの色を設定

 android:scrollbarThumbHorizontal を指定します。


<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:scrollbarThumbHorizontal="@drawable/scrollbar_thumb">
</HorizontalScrollView>


drawable/scrollbar_thumbl.xml


<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#FF0000"
android:endColor="#FFFFFF"
android:angle="0" />
<corners
android:radius="20dp" />
</shape>



■ 縦スクロールバーの色を設定

 android:scrollbarThumbVertical を指定します


<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbarThumbVertical="@drawable/scrollbar_thumb">
</ScrollView>


drawable/scrollbar_thumbl.xml


<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#FF0000"
android:endColor="#FFFFFF"
android:angle="90" />
<corners
android:radius="20dp" />
</shape>





■ 横スクロールバーのトラックの色を設定

 android:scrollbarTrackHorizontal を指定します。


<HorizontalScrollView
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:scrollbarTrackHorizontal="@drawable/scrollbar_track">
</HorizontalScrollView>


drawable/scrollbar_thumbl.xml


<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#FF0000"
android:endColor="#FFFFFF"
android:angle="0" />
<corners
android:radius="20dp" />
</shape>





■ 縦スクロールバーのトラックの色を設定

 android:scrollbarTrackVertical を指定します


<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:scrollbarTrackVertical="@drawable/scrollbar_track">
</ScrollView>


drawable/scrollbar_thumbl.xml


<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient
android:startColor="#FF0000"
android:endColor="#FFFFFF"
android:angle="90" />
<corners
android:radius="20dp" />
</shape>



■ 表示するスクロールバーを指定

 android:scrollbars を指定します

 設定できる値は
  "none"
  "horizontal"
  "vertical"

 "none" を指定すると、スクロール領域の端のフェードアウトのみで、
 バーは表示されない
 この場合、
  android:fadingEdgeLength="25dip"
 にようにフェードアウトする領域の幅を指定できる


<ScrollView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none"
android:background="#000000"
android:fadingEdgeLength="25dip">
</ScrollView>


本の情報の部分が、バーが出ない代わりに、下の部分がフェードアウトしている




■ フェードアウトを非表示

 デフォルトだと、スクロールバー + フェードアウト
 なのですが、このフェードアウトを無くして
 スクロールバーだけにしたいときは

 android:fadingEdge を指定します

 設定できる値は
  "none"
  "horizontal"
  "vertical"

 で、"none" を指定すれば無くなります

2010年3月3日水曜日

Android SearchManager 検索ボックスを使うぜ!

Gmail アプリで menu から検索を押すと、上にニュッて検索ボックスがでてきます。
ちなみに、検索ボタン(ハードのボタンね)を押しても出てきます。
エミュレータだと F5 です。

こんなやつ。過去の入力が候補として出てきてくれます。



これを実装してみたので方法をまとめておきます~。

公式な reference はこちら
http://developer.android.com/intl/ja/reference/android/app/SearchManager.html

Quick Search Box については、こちら
http://android-developers.blogspot.com/search/label/Quick%20Search%20Box

これの大事なところをピックアップしつつ訳しました。。。が、長いので

ようはまとめると
ここのまとめが全部を含んでいます。なので以下に必ず目を通してください。

 (0. 特定のボタンから検索バーを呼び出す処理を追加)
    ボタンのアイコンの画像は
    android.R.drawable.ic_search_category_default を使う



    (ボタンの文字列は SearchManager.MENU_KEY)
    ボタンが押された時の処理に以下を実装

onSearchRequested();



 1.検索用 Activity を用意する
 (Main の Activity でもいいし、専用の Activity を用意してもいい)


   onCreate() で、
    1. ACTION_SEARCH Intent を受け取って処理する
    2. Quick Search Box から検索した場合の処理を追加
      この場合、ACTION_VIEW の Intent で呼び出され、
      Flag が FLAG_ACTIVITY_NEW_TASK (= 0x10000000)
      になっている
   
   onNewIntent() で、
    検索用 Activity から 検索ボックスを呼び出したときの処理を追加


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

final Intent queryIntent = getIntent();
final String queryAction = queryIntent.getAction();

// ACTION_SEARCH の Intent で呼び出された場合
if (Intent.ACTION_SEARCH.equals(queryAction)) {
doSearchWithIntent(queryIntent);
}

// Quick Search Box から呼び出された場合
if (Intent.ACTION_VIEW.equals(queryAction)){
if(queryIntent.getFlags() == Intent.FLAG_ACTIVITY_NEW_TASK) {
doSearchWithIntent(queryIntent);
}
}
}

// 検索用 Activity から呼び出されたとき
@Override
protected void onNewIntent(Intent intent) {
doSearchWithIntent(intent);
}

private void doSearchWithIntent(final Intent queryIntent) {
// 検索文字列は SearchManager.QUERY というキーに入っている
final String queryString = queryIntent.getStringExtra(SearchManager.QUERY);
doSearchWithQuery(queryString);
}



 2.検索UIのXMLファイルを用意する

  res/xml/ フォルダに searchable.xml ファイルを作成する
  (ファイル名はなんでもいいです)


<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint" />


 3.検索UIのXMLファイルへのリファレンスと ACTION_SEARCH Intent を
 受け取る宣言を AndroidManifest.xml に入れる



<activity android:name="MySearchActivity"
android:label="Search"
android:launchMode="singleTop" >
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>


 ・検索を起動する Activity と処理する Activity を同じにする場合は
  AndroidManifest.xml の activity タグに
    android:launchMode="singleTop"
  を追加し、
  onNewIntent() を override して、intent がアップデートされた
  ときの処理を入れる
  
 ・metadata で提供する値は、ローカルの各検索 Activity にだけ
  適用される。よって、同じアプリ内でも Activity ごとに異なる
  特徴の検索を行うことができる。
  その場合は、各activity に上記の宣言を入れる


 4.検索キーワードの入力候補を設定する

  4.1 SearchRecentSuggestionsProvider を 拡張した Provider を
   作成する


  こんな感じ

import android.content.SearchRecentSuggestionsProvider;

public class YourSuggestionProviderClass extends SearchRecentSuggestionsProvider {

public YourSuggestionProviderClass(){
setupSuggestions("myproviderauthority", YourSuggestionProviderClass.DATABASE_MODE_QUERIES);
}
}

  myproviderauthority にはパッケージ名などを使います。


  4.2 作成した Provider を manifest で宣言する



<provider android:name="YourSuggestionProviderClass"
android:authorities="myproviderauthority"
android:syncable="false" />



  4.3 Activity の検索 UI の XML ファイル(searchable.xml)の
    searchable タグで Provider について宣言する
 


<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint"

android:searchSuggestAuthority="myproviderauthority"
android:searchSuggestSelection=" ? "

android:searchSuggestIntentAction="android.intent.action.VIEW" />


  android:searchSuggestAuthority と android:searchSuggestSelection
  は必須です。
  android:searchSuggestIntentAction はオプショナルです。



  4.4 検索 Activity で ユーザー生成のクエリを捕えて、
    saveRecentQuery(String, String) で記録する


final String queryString = queryIntent.getStringExtra(SearchManager.QUERY);

SearchRecentSuggestions suggestions =
new SearchRecentSuggestions(this, "myproviderauthority",
YourSuggestionProviderClass.DATABASE_MODE_QUERIES);
suggestions.saveRecentQuery(queryString, null);



 5. 検索候補を Quick Search Box へ公開する
  検索UIのXMLファイル の searchable タグに以下を追加


android:includeInGlobalSearch="true"
android:searchSettingsDescription="@string/settings_description"

  settings_description で定義した値は、
  ・2.1 以前  
   Home画面で
   [Menu]ー[設定]ー[検索]ー[検索対象]での、自分のアプリ
   の下に出る文字列
  ・2.2 以降
   Home画面で[検索キー]ー[検索ボックス左の g マークをタップ]
   ー[左上の設定ボタン]での、自分のアプリの下に出る文字列

  こんな感じで追加される(Libraroid が追加されている)


 7.検索用以外の Activity で検索キーなどが押されたときに、
  検索用 Activity の検索処理を呼び出すようにする


<application>
<meta-data android:name="android.app.default_searchable"
android:value=".MySearchActivity" />


</application>


  過去の入力が候補として出るようになりました!


  Quick Search Box での検索にも引っかかる
  一番下に "Libraroid: 1" と出ている


  もっと見るをタップするとこんな感じに



  Quick Search Box でよく Libraroid の結果を選ぶと
  最初にでてくるようになります。
  これをタップした場合は、ACTION_VIEW の
  FLAG_ACTIVITY_NEW_TASK (= 0x10000000)
  が呼ばれます。




ということで、

公式な reference
http://developer.android.com/intl/ja/reference/android/app/SearchManager.html
の訳はここから、


1. Developer Guide (何ができるの?どうなるの?)

これを使うと、アプリは
 ・どのように検索を呼び出すか
 ・ダイアログの見た目
 ・検索結果のどのタイプが利用可能なのか
 (ユーザーのタイプとして利用可能な入力候補を含む)

をカスタマイズすることができます。

"検索可能でないアプリであっても、Quick Search Box
(システムの 'global search')のトリガーになる、
検索の呼び出しをデフォルトでサポートしてます。"
 ・・・これは知らなかった。
* 2.2 からなくなりました。(追記 2010.12.11)


2. How Search Is Invoked (どうやって呼び出すの?)

"不可能または不適切でない限り、すべてのアプリケーションは、検索のUIの呼び出しをサポートする必要があります。"
 ・・・すごいこと書いてあるな。google先生。実装しろってことね。

"検索コマンドとは、(一般的に) menu の中の「検索」やショートカットキー「S」や検索ボタン(ハードキー)が押されたというコマンドのことです。"
 ・・・ハードキーボートがついている端末持ってないから
    ショートカットキー試せない。。。><
    今なら IS01 がある〜(追記 2010.12.11)


"もし、検索が可能な実装をしていない場合は、Quick Search Box のような 'global search' が呼び出されます。この場合、検索ボタンを押すとブラウザが開いて web base の検索を行います。"

実装してるとこんな感じ。


実装してないとこんな感じ。ここで検索ボタンを押すとブラウザに飛ばされる。



* この上にニュッと出るやつを "検索UI" と言ってます。


"一般的に、この検索コマンドを受け取って、検索UIを表示・操作する SearchManager を呼び出す実装は、 Activity もしくは Activity が base のクラスに対して行います。"
"もちろん検索コマンド以外から(例えば特定のキー操作など)も SearchManager を呼び出すことができます。"

"検索UIはフローティングウィンドウとして表示され、Activity stack のいかなる変化も引き起こさない。もしユーザーが検索をキャンセルした場合は、以前の Activity が再度現れる。"

"ユーザが検索を起動すると、検索 Intent が送られる。そして、自分のアプリの Activity は pause されて、一般的な intent 処理シーケンスがアクティブになる。"


■ What you need to do. (で、結局なにすればいいの?)

 まず、呼び出された検索を扱う方法を選ぶべし。大きくわけて次の4つ(一部かぶってるけど)

 * 検索UIを直接呼び出す "検索ボタン" や "menu item" などをアプリ
  に入れて、検索コマンドを自分で受ける

 * type-to-search (文字入力で検索を呼び出す)の機能を提供する。この場合、
  ユーザーがある文字を入力したときに自動で検索が呼び出される。

 * もし、あなたのアプリが検索機能を実装していなくても、"検索ボタン"
  や "menu item" を通して、global search を実行できる。
  2.2 からなくなったけどね!(追記 2010.12.11)

 * 完全に検索できない。これはとってもレアケース。
  2.2 からなくなったから、もはやレアじゃなくね?(追記 2010.12.11)


■ How to define a search menu. (検索メニューをどうやって決める?)

 menu に検索 item を追加するのにとっても便利なリソースが提供されてます。

 次の2つ
  * android.R.drawable.ic_search_category_default
   検索用に使えるアイコン(多分使えよ!ってことだと思う)
  * SearchManager.MENU_KEY は推奨されるアルファベットの
   ショートカット(普通はたぶん s)


■ How to invoke search directly. (どうやって検索を直接よびだす?)

 "ボタン"(検索ハードキーではないよ!) や "menu item" から検索を直接呼ぶには、onSearchRequested を呼べばOK!


onSearchRequested();



■ How to implement type-to-search.
 (文字入力で検索起動をどうやって実装する?)


 アプリの Activity をセットしている間に、setDefaultKeyMode を呼ぶべし!


// search within your activity
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
// search using platform global
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_GLOBAL);


DEFAULT_KEYS_SEARCH_LOCAL は自分のアプリ(Activity)内で検索
DEFAULT_KEYS_SEARCH_GLOBAL は global search で検索

このエントリを書いた 2010年3月には手元のハードキーボードつきの端末がなかったので検証できなかったのですが、今 IS01 で試したら、EditText とかにフォーカスされていない状態でタイプすると、LOCAL のときは Activity に実装した検索が起動されて、GLOBAL のときは Global Search が起動しました!(追記 2010.12.11)


■ How to enable global search with Quick Search Box
 (Quick Search Box で global search を有効にする方法)
 
 アプリや Activity 内での検索に加えて、device と web を横断的に検索できる Quick Search Box というのがある。

 この検索バーも Quick Search Box


 platform-global search を呼び出すためにも Search Manager を使うことができる。

 これを実現する方法は、次の2つ

  * アプリや Activity 内で、global search を意味する検索を定義する。
   (これについては、Searchability Metadata の章で詳しく説明する。)
   簡単に言うと、自分のアプリに対するデフォルト検索を "*" と定義した
    single meta-data entry を AndroidManifest.xml に追加する。
   これは、アプリ指定の検索 Activity が提供されていないことを
   システムに示す。よって、システムは代わりに platfrom-global search
   による検索を立ち上げる。

  * 単純になにもしない。onSearchRequested() のデフォルトの
   実装は global search のトリガーになる。
   2.2 からはこれだけだとトリガーになりません
   (もちろん、startSearch(String, boolean, Bundle, boolean)
   直接呼ぶことで、いつでも検索のトリガーになれる。この方法は
   ローカル検索を提供していて、かつ global 検索にアクセスしたい
   場合には最も使いやすい方法)


■ How to disable search from your activity
 (Activity からの検索を利用不可にするには…)

 検索は system-wide な特徴で、ユーザーはそれがすべての contexts
 で利用可能なことを期待(予測)する。
 もし、自分の UI デザインで、検索の起動を完全に不可能にするには、
 次の様に onSearchRequeted を override する。
   ・・・なんだろう。ゲームアプリとかかな?


@Override
public boolean onSearchRequested() {
return false;
}



■ Managing focus and knowing if search is active
 (focus の操作と検索がアクティブか知る方法)


 検索UIは Activity とは切り分けられていない。
 なので、検索UIが呼び出されたり開放されたとき、自分の Activity は
 (典型的には)一時停止(paused, resumed)される。
 もしくは、" Application Fundamentals: Activity Lifecycle " 内で
 定義されたメソッドによって通知(notified)される。

 検索UIは他のシステムUIエレメント
 (例えば、notifications, screen locks, system alerts など)と
 同じ方法で扱われる。

 ● 検索UIが現れたとき、自分の Activity は input focus を失う

 ● 検索 Activity が開放されたとき、次の3つの結果が可能である。

  * ユーザーが単純に検索UIをキャンセルした場合:
    自分の Activity は再び input focus を得て、前の続きになる。
    検索ダイアログが開放された通知を直接受け取りたい場合は、
    setOnDismissListener(SearchManager.OnDismissListener)
    と 
    setOnCancelListener(SearchManager.OnCancelListener)
    を見てね。

  * ユーザーが検索を立ち上げ、これが、検索 Intent の受け取りと
   処理のために他の activity への切り替えを要求した場合: 
    自分の Activity は、 activity pause もしくは stop 通知
    である通常のシーケンスを受け取る

  * ユーザーが検索を立ち上げ、現在の Activity が検索 Intent の
   受取手だった場合 :
    自分の Activity は onNewIntent() メソッドを介して通知
    を受け取る。

 このリストは、自分の Activity が検索UIと相互作用する方法を明確に
 するために提供されている。
 検索 Activity と 検索 Intent のより詳しい内容は次の章で提供される。


3. Implementing Search for Your App
 (自分のアプリに検索を実装する方法)

 次の6ステップを実行すればOK!

  1. 上記で説明した "検索の呼び出し" を実装する
   (ここの括弧の内容がウケるな。。。"ぶっちゃけると、
    意味かぶってるけどね。でも"search-invoking" じゃなくて
    "searchable" って感じなの" だって)

  2. 検索文字列を受け取って、結果のリストに変換する Activity
   を実装する。これは、最初に表示される Activity でもいいし、
   検索結果用の Activity でもいい。
   検索可能なアプリでは、この機能をもつ Activity を必ず1つは
   持っていなければならない。

  3. 検索可能な Activity 内の onCreate() で、ACTION_SEARCH
   Intent を受け取って処理する実装を入れる。
   検索キーワード (query string) は
   getStringExtra(SearchManager.QUERY) を呼ぶことで
   得られる。

 コードはこんな感じ


@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);

final Intent queryIntent = getIntent();
final String queryAction = queryIntent.getAction();
if (Intent.ACTION_SEARCH.equals(queryAction)) {
doSearchWithIntent(queryIntent);
}
}

private void doSearchWithIntent(final Intent queryIntent) {
final String queryString = queryIntent.getStringExtra(SearchManager.QUERY);
doSearchWithQuery(queryString);
}



  4. 自分の検索 Activity を識別・サポートする。
   検索の設定パラメータを提供する XML file を用意し、
   これへのリファレンスを manifest の Activity entry に入れる。
   さらに、ACTION_SEARCH intent を受け取れるという定義を
   intent-filter に追加する。
   詳しくは SearchabilityMetadata

  5. 検索 Activity への global reference を提供する
   metadata entry も manifest に必要。
   これは、デフォルトの検索 context として自分のアプリを
   使うために、自分のアプリの 他の Activity からの検索を
   検索 Activity の検索UIに紐付ける。
   詳しくは SearchabilityMetadata

  6. 最後に、launchMode flag に singleTop を指定することで、
   検索結果 Activity を single-top にする。
   これは、ある Activity からの検索処理として、Activity stack
   上のそれらの pile を生成せずに、同じ Activity を起動する
   ことをシステムに許可する。
   これをした場合、 onNewIntent を override して、intent が
   アップデートされた (新しい query を受け取った)ときの処理を
   入れなければならない。
   詳しくは
   Exposing Search Suggestions to Quick Search Box



4. Search Suggestions (検索の入力候補)

 検索システムの特徴の1つが "検索キーワードの候補"
 各アプリは、異なる、ユニークな正しい方法で、候補を実装する。
 候補たくさんのソースから書かれる、例えば

  * 実際の検索結果 (例えば、アドレス帳の名前など)
  * 最近入力された検索キーワード
  * 最近みたデータや結果
  * 文脈上適切なクエリや結果
  * 可能な結果のまとめ

 一度、アプリ検索候補を提供するために設定されると、それらの同じ
 候補は system-wide Quick Search Box で(簡単に)利用可能にできる。
 そうすると、中央の目立つ場所からそのコンテンツへより早く
 アクセスできるようになる。

 候補の主な形式はクエリ候補として知られており、それはユーザーが
 すでにタイプしているクエリテキストに基づく。
 これは一般的に利用可能なデータとの部分一致である。

 特定の状況 
  ー クエリとしてまだ何もタイプされていない場合、ー
   アプリは zero-query 候補を提供するかどうかを選ぶこともできる。
   これらは、典型的に同じデータソースから記述される。
   しかし、利用可能な部分クエリテキストが無いと、それらは
   他のファクター(もっとも最近のクエリ or 結果)に基づいた
   重み付けになる。


■ Overview of how suggestions are provided
 (どのように候補を提供する?)


 候補は Cotent Provider を介してアクセスする。

 search manager が特定の Activity を検索用と識別すると、
 候補のソースも存在するかどうか metadata を確かめる。
 もし候補が提供されているなら、次のステップが行われる。

  * metadeta 内のフォーマット情報を使って、ユーザーの
   クエリテキストをフォーマッテイングして候補の
   Content Povider に送る。

  * 候補の Content Provider は 可能な候補の Cursor を作成する。

  * search manager は Cursor の各行のデータを表示するための
   リストを追加し、ユーザーへ候補を表示する。

  * もし、ユーザーがなにかキーを打ったなど、クエリが変化したら、
   上のステップはリピートされて、候補リストがアップデートされ
   表示される。

  * もし、ユーザが "GO" ボタンをクリック(タップ)したら、候補は
   無視され、通常の ACTION_SEARCH タイプの Intent による検索が
   立ち上がる

  * もし、ユーザーが候補リストにフォーカスする操作をした場合、
   ユーザーが候補から候補へナビゲートしている間、クエリテキストは
   アップデートされる
   ユーザーはアップデートされたクエリをクリック(タップ)できる。
   もしユーザーが入力フォームへ戻る処理をすると、元々タイプ
   されていたクエリが再ストアされる。

  * もし、ユーザーが特定の候補をクリック(タップ)すると、
   Cursor と metadeta 内で見つかった値の組み合わせが
   Intent とシンクロするのに使われる。
   そして、それをアプリへ送る。
   Activity のデザインと検索の実装方法に応じて、これは
   ACTION_SEARCH か ACTION_VIEW か特定のデータを直接
   表示するか、のいずれかになる。


■ Simple Recent-Query-Based Suggestions
 (最近のクエリに基づいた候補)


 Android framework は 最近のクエリを保持し返すための
 Search Suggestion provider を提供している。

 (多くのアプリにとってはこれで十分)
 使う方法は次のステップでOK

  * 上記のセクションでかかれたクエリ検索を実装してテストする。
  * 自分のアプリ内で SearchRecentSuggestionsProvider
   拡張した Provider を生成
  * 自分の provider について定義した manifest entry を作る
  * 自分の検索 Activity の XML 設定ファイルに provider についての
   情報を入れる
  * 検索 Activity で ユーザー生成のクエリを捕えて、
   saveRecentQuery(String, String) で記録する。

 より詳しい実装については SearchRecentSuggestionsProvider を参照


SearchRecentSuggetionsProvider

 このスーパークラスは自分のアプリの簡単な検索候補 provider を
 作るためのものです。
 最近の入力クエリ and/or 最近の view に基づいた候補を生成する。

 このクラスを使うために、次のことを行わなければならない
  * SearchManager で記述された test query search を実装.
   (この provider は標準的な ACTION_SEARCH Intent を通して
    すべての候補クエリを送る)

  * SearchRecentSuggestionsProvider を拡張して
   Content Provider を自分のアプリ内に作る
   このクラスはすごくシンプル。典型的にはコンストラクタが
   1つだけ。
   しかし、コンストラクタはすごく重要な責任がある:
   setupSuggestions(String myauthorities, int mode)
   を呼ぶときに、自分の検索 Activity の要求とマッチする
   provider を設定する

   よって、こんな感じ

import android.content.SearchRecentSuggestionsProvider;

public class YourSuggestionProviderClass extends SearchRecentSuggestionsProvider {

public YourSuggestionProviderClass(){
setupSuggestions("your.suggestion.authority", YourSuggestionProviderClass.DATABASE_MODE_QUERIES);
}
}


  * 自分の provider を定義した entry を manifest に入れる。
   典型的には、次のようなかんじ



<provider android:name="YourSuggestionProviderClass"
android:authorities="your.suggestion.authority" />


  * コードから直接この content provider のインスタンスを
   作らないこと!
   これは検索ダイアログが候補を探すときに、システムの
   Content Resolver によって自動的に行われる。

  * これを行う Content Resolver のために、検索 Activity の
   XML 設定に content provider の情報を入れる。
   こんな感じで


android:searchSuggestAuthority="your.suggestion.authority"
android:searchSuggestSelection=" ? "

  * 自分の検索 Activity で、ユーザーが生成したクエリを捕えて、
   次の検索のために
   SearchRecentSuggestions.saveRecentQuery()
   を呼んで保存しておく


■ Creating a Customized Suggestions Provider
 (Suggetions Provider をカスタマイズする方法)

ぱす!


■ Configuring your Content Provider to Receive Suggestion Queries
 (候補のクエリを受け取るための Content Provider の設定)

 suggetion provider と communicate する方法
 
 自分のパッケージ(アプリ)が候補を提供する場合は、
 最初に Search Manager を決めなければならない。
 これは、seachable meta-data XML file の検査で実行される。

 android:searchSuggestAuthority
 が XML にある場合は候補を得て表示する

 各クエリは Uri を含み、Search Manager は Uri を次のように
 フォーマットする

   content:// your.suggest.authority / your.suggest.path / SearchManager.SUGGEST_URI_PATH_QUERY


 自分の Content Provider は2つの方法でクエリテキストを受け取ることができる

  * クエリは、検索引数として提供
   もし、android:searchSuggestSelection の値を定義し、
   文字列を含むなら、この文字列は Content Provider の
   クエリ関数に検索パラメータをパスする。
   '?' 文字を使って単一検索引数を定義しなければならない。
   ユーザーのクエリテキストは検索引数配列の最初の要素として
   自分のアプリにパスされる。

  * クエリは、Data Uri で提供
   もし、android:searchSuggestSelection の値を定義しないなら、
   Search Manager はユーザークエリの最後に "/" を追加する。
   クエリは Uri エンコーディングルールを使ってエンコードされる
   (デコードするのを忘れないでね!)
   (getPathSegments()
    getLastPathSegment() が助けになるよ)

 Content Provider へアクセスできるように permission を追加。
 manifest に android:readPermission を定義した場合は、
 "android.permission.GLOBAL_SEARCH" へのアクセスを
 android:readPermission に与える path-permission を含むことに
 よって、検索候補パスへのアクセスを提供しなければならない。
 
 search infrastructure に明示的に与えたアクセスは、それが自分の
 provider を保護している他のパーミッションの詳細を知らなくても
 検索候補にアクセスできることを保証する。 
 Content Provider はすでに search infrastructure を利用可能
 なので、パーミッションを必要としない。
 
 パーミッションで provider のアクセスを保護している例


<provider android:name="MyProvider" android:authorities="myprovider"
android:readPermission="android.permission.READ_MY_DATA"
android:writePermission="android.permission.WRITE_MY_DATA">
<path-permission android:path="/search_suggest_query"
android:readPermission="android.permission.GLOBAL_SEARCH" />
</provider>


■ Handling empty queries (入力がないときのクエリの扱い)

 方法はたくさんあるけど、ここでは2つ紹介

  * local data の単純な検索フィルターの場合、単にすべての
   データセットを与える(例えば:People)
  * クエリ検索の場合、最も最近のクエリを表示する。

 
■ The Format Of Intents Sent By Search Suggestions
 (検索候補によって送られる Intent の形式)

 これらの Intent を設定する方法はたくさんが、ここではいくつかを紹介

  * Launch a query
   このモデルでは、各候補は自分の検索 Activity が実行できる
   クエリを表し、Intent は ユーザーがクエリテキストを入力して
   "GO" ボタンを押したときに正しくフォーマットされる。
    o Action: ACTION_SEARCH
         provided using your XML metadata
     (android:searchSuggestIntentAction).
    o Data: empty (not used).
    o Query: query text supplied by the cursor.

  * 完全な Data Uri を使って結果に直接飛ぶ
   このモデルでは、ユーザーは特定の結果へ直接行く
    o Action: ACTION_VIEW
    o Data: a complete Uri, supplied by the cursor,
     that identifies the desired data.
    o Query: query text supplied with the suggestion
     (probably ignored)

  * シンクロした Data Uri を使って結果に直接飛ぶ
   これは上と同じ結果になるが、提供する Data Uri は異なる
    o Action: ACTION_VIEW
    o Data: The search manager will assemble a Data Uri
     using the following elements: a Uri fragment provided
     in your XML metadata
     (android:searchSuggestIntentData),
     followed by a single "/", followed by the value found
     in the SUGGEST_COLUMN_INTENT_DATA_ID entry
     in your cursor.
    o Query: query text supplied with the suggestion
     (probably ignored)

 このリストは網羅するものではないので自由にカスタマイズしてくだされ。


■ Suggestion Rewriting.

 パス


5. Exposing Search Suggestions to Quick Search Box 
(検索候補を Quick Search Box へ公開する)


 searchable metadata file で

  android:includeInGlobalSearch = "true"

 にすることで、セットアップした検索候補を Quick Search Box で使えるようになる

 重要なお知らせ:デフォルトでは、自分のアプリは Quick Search Box
         の検索対象にはなりません。
         検索対象にするには、アプリをインストール後に
         [設定]ー[検索]ー[検索対象]で、自分のアプリに
         チェックを入れてもらう必要があります。

 Source Ranking: どれだけ自分のアプリの結果を選ばれたかで、
         自分アプリの検索結果の表示される位置かわる

 Search Settings: アプリケーションの名前の下に、そのアプリで何が
         検索できるかを示す必要がある。 
         searchable metadata の
         android:searchSettingsDescription で定義する。

 Shortcuts: パスww


6. Action Keys

ぱす


7. Searchability Metadata 
(ちょっとだけ自分のアプリの検索をカスタマイズ)

 アプリを検索可能にしておく

 metadata で提供する値は、ローカルの各検索 Activity にだけ
 適用される。 よって、同じアプリ内でも Activity ごとに
 異なる特徴の検索を行うことができる。

 自分のアプリのどのアクティビティが検索可能かを定義しなければ
 ならない。
 manifest の activity の項目で、次の2つを提供すべし

  * ACTION_SEARCH Intent を受けて処理できることを
   intent-filter に記述
  * (典型的に searchable.xml と呼ばれる) XML file への
   リファレンスを記述

 こんな感じ


<activity android:name="MySearchActivity"
android:label="Search"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>


 次に、../xml/ フォルダに manifest で指定した xml ファイルを作成する。

 こんな感じ

<searchable xmlns:android="http://schemas.android.com/apk/res/android"
android:label="@string/search_label"
android:hint="@string/search_hint" />


■ Styleable Resources in your Metadata

表示される文字列をランドスケープとポートレイトで変えたい場合は、

  * .../res/values-land/strings.xml
  * .../res/values-port/strings.xml
  * .../res/values/strings.xml

 にそれぞれ値を入れる
 詳細は Resources and Internationalization: Alternate Resources


■ Metadata for non-searchable activities

 検索アプリの中の Activity ではあるが、それ自身には検索を
 実装したくない場合(検索用のActivityでの検索に飛ばしたい場合とか)
 は manifest に次のように記述する。
  * 検索 Activity の前に "." をつけて、同じパッケージにあることを
   明示するか、   value="*" として、デフォルトの検索
   Activity を呼び出すように指定


<application>
<meta-data android:name="android.app.default_searchable"
android:value=".MySearchActivity" />


</application>



■ Additional metadata for search suggestions

 検索候補を生成する content provider をシステムに公開するために
 XML ファイルに次の項目を追加する

 最初に、manifest に以下を追加



<provider android:name="YourSuggestionProviderClass"
android:authorities="your.suggestion.authority" />


 次に、searchable XML ファイルに次の追加



android:searchSuggestAuthority="your.suggestion.authority"


android:searchSuggestSelection="field =?"


android:searchSuggestIntentAction="intent action string"
android:searchSuggestIntentData="intent data Uri"



8. Passing Search Context

 検索を向上させるために、付加情報を検索 Activity に渡すことが
 できる。例えば、Map アプリでは、現在の位置情報を付加することで、
 検索精度をあげることができる。

 検索が呼び出されたときに Bundle object に保存できる長さの分だけ、
 データを渡すことができる。

 アプリのデータを Search Manager に渡すには、onSearchRequested
 を override する


@Override
public boolean onSearchRequested() {
Bundle appData = new Bundle();
appData.put...();
appData.put...();
startSearch(null, false, appData, false);
return true;
}


Search Manager からデータを受け取るには、 ACTION_SEARCH Intent から次の用に抜き出す


final Bundle appData = queryIntent.getBundleExtra(SearchManager.APP_DATA);
if (appData != null) {
appData.get...();
appData.get...();
}




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

もし、この SearchManager クラスに直接アクセスしたい場合は、
インスタンスを作成するのではなく、

 context.getSystemService(Context.SEARCH_SERVICE)

すること。