2011年10月31日月曜日

Bootcamp 2011 に行ってきました。

Bootcamp 2011 Japan

今年は去年と違って、土曜日開催(去年は平日) & Google Japan の六本木オフィスで行えたこともあって、たくさんの参加者に来ていただけました。よかった、よかった。

ハッシュタグは #bc2011jp です。

@Fumi さんが Togetter にまとめてくれました。
GTUG Bootcamp 2011 #bc2011jp - Togetter -

Barエリアで松尾さんに App Engine について熱く語ると App Engine Tシャツがもらえましたー。


セッションの資料、後でチェックしなきゃ。

■ セッション:「Fragment を使ってみよう」 (あんざいゆき)
資料

■セッション:「Google SketchUp モデリングの初歩」 = 初めてでも簡単、3次元の世界 =
(田中雅子、株式会社アルファコックス CTO)

■セッション:「はじめての Chrome Extension」 (吉川徹)
資料

■セッション:「App Engine 倹約のすゝめ」 (小川 信一)

■ハンズオン:「新聞風の1ページを作ってみよう!」(一條 美和子)
資料

■ハンズオン:Google Apps Script (中村敦)
資料1 資料2 資料3

■ハンズオン:「Spike WebView & Tough ICS」 (江川 崇)
資料

■ハンズオン:Google+ API の体験と Hangouts の紹介 (田中洋一郎、伊藤 千光、中嶋 信博)
資料 参考ブログ

2011年10月22日土曜日

Android 4.0 Platform

Android 4.0 Platformを意訳しました。訳が正しくない可能性もあります。正確な内容を理解するには原文を参照してください。

---
API Level: 14

Android 4.0 へようこそ!

Android 4.0 はユーザーと開発者むけの新しい機能を追加したメジャープラットフォームリリースです。 以下で議論される新しい機能と API に加え、Android 3.x からの API セットと Holographic テーマを小さい画面向けにも拡張した Android 4.0 は重要なプラットフォームリリースです。つまり、アプリ開発者として、Android 4.0 (API Level 14) 以上で動作する ハンドセット、タブレット、その他デバイスにおいて、それぞれに最適化されたユーザー体験を提供する単一の APK を開発し公開するための、1つのプラットフォームと単一の API フレームワークを手に入れたということです。

開発者は Android SDK のコンポーネントとして Android 4.0 platform をダウンロードできるようになりました。Android emulator を使って Android 4.0 でのアプリケーションの開発やテストを行うことができます。このプラットフォームには Android library と system image, emulator skin などが含まれていますが、外部のライブラリは含まれていません。

Android 4.0 向けの開発とテストを始めるには、Android SDK Manager を使って自分の SDK にプラットフォームをダウンロードします。より詳しい情報は Adding SDK Components を見てください。Android が初めての人は、まず download the SDK Starter Package から始めてください。

リマインダー: すでに Android application を公開している場合、できるだけ早く Android 4.0 上であなたのアプリケーションのテストと最適化をしてください。最新の Android-powered デバイスでベストな体験を提供していることを確認するべきです。

---

API Overview



■ Social APIs in Contacts Provider

ContactsContract provider によって定義されるコンタクトAPI が新しいソーシャル向け機能をサポートするように拡張されました。例えば、デバイス所有者のパーソナルプロフィールや、デバイスにインストールされているソーシャルネットワークへ個々のコンタクトを招待する機能などです。

User Profile

ContactsContract.Profile table で定義される、デバイス所有者を表すパーソナルプロフィールを持つようになりました。ユーザーのアイデンティティを管理するソーシャルアプリは ContactsContract.Profile 内に新しい ContactsContract.RawContacts エントリを作成することでユーザーのプロフィールデータを提供することができます。つまり、デバイスユーザーを表す raw contacts は、ContactsContract.RawContacts Uri によって定義されていたこれまでの raw contacts table には属さないということです。代わりに CONTENT_RAW_CONTACTS_URI にあるテーブルにプロフィール用の raw contact を追加しなければなりません。このテーブルの raw contact は、"Me" というラベルの1ユーザーのプロフィールとして集約されます。

プロフィール用の raw contact を追加するには WRITE_PROFILE パーミッションが必要です。同様に、プロフィールテーブルを読むには READ_PROFILE パーミッションが必要です。プロフィールへデータを提供するときであっても、ほとんどのアプリはプロフィールを読むべきではありません。ユーザーのプロフィールを読むことはセンシティブなパーミッションです。ユーザーがこのパーミッションを要求するアプリに対して懐疑的になると予想すべきでしょう。

Invite Intent

INVITE_CONTACT intent のアクションは、あるアプリに対して、ユーザーがソーシャルネットワークにコンタクトを追加したいことを示すアクションの呼び出しを許可します。このアクションを行うアプリとして選ばれたアプリは、ソーシャルネットワークに指定されたコンタクトを招待します。ほとんどのアプリはこの操作の受け手側になるでしょう。例えば、ビルトインの People アプリは、コンタクト詳細にリストされた特定のソーシャルアプリに対して、ユーザーが "Add connection" を選択したときにこのインテントを呼び出します。

あなたのアプリを "Add connection" リスト内に表示されるようにするには、ソーシャルネットワークからのコンタクト情報をシンクさせるための sync adapter を、あなたのアプリで提供しなければなりません。さらに、システムに対してあなたのアプリが INVITE_CONTACT intent に応答することを示さなければなりません。そのためには、あなたのアプリの sync configuration file に inviteContactActivity 属性を追加する必要があります。inviteContactActivity 属性には、システムが invite intent を送ったときに起動するための完全修飾の Activity 名も記述します。起動された Activity は intent のデータから指定された contact の URI を取得することができるので、ネットワークにコンタクトを招待したり、ユーザーのコネクションに人を追加するなど必要な処理を行えます。

例として Sample Sync Adapter アプリを見てください(特に、contacts.xml ファイルを見てください)。

Large photos

コンタクト用の高解像度写真をサポートするようになりました。contact record に写真を入れたとき、システムは 96x96 のサムネイル(これまでと同じ)と、新しいファイルベースの写真ストアに保存される 256x256 の "display photo" を生成します(システムが選択する実際のサイズは将来変わる可能性があります)。大きい写真を通常の PHOTO カラムに入れると、システムが適切なサムネイルを生成し、photo record に表示するので、コンタクトに大きい写真を追加できます。

Contact Usage Feedback

新しい ContactsContract.DataUsageFeedback API によって、人にコンタクトするための特定の方法がどの程度使用されたかをトラックすることができます。例えば、ユーザーがどのくらい頻繁に各電話番号やメールアドレスを使用したかなど。この情報は、各人に関連するコンタクトメソッドのランキング付けを改善し、コンタクトのより良い提案を行う助けになります。

■Calendar Provider

新しい Calendar API によって、カレンダー、イベント、参加者、リマインダー、アラートなどの Calendar Provider に保存されるデータを読み込み、追加、編集、削除することができます。

さまざまなアプリやウィジェットがこれらの API をつかってカレンダーのイベントを読んだり編集したりできます。しかし、最も魅力的なユースケースは sync adapter です。これにより、Calendar Provider を使って他のカレンダーサービスのユーザーカレンダーを同期することができ、ユーザーの全てのイベントを表示できる1つの場所を提供できます。Google Calendar のイベントは、Google Calendar Sync Adapter による Calendar Provider で同期され、 Android のビルトインカレンダーアプリに各イベントが表示されることを許可します。

カレンダーのデータモデルと、Calendar Provider 内のイベントに関連した情報は CalendarContract によって定義されます。ユーザーの全てのカレンダーデータは CalendarContract のいくつかのサブクラスで定義される複数のテーブルに保存されます。
  • CalendarContract.Calendars テーブルはカレンダー特有の情報を保持します。テーブルの各ローは1つのカレンダーの名前、色、同期情報などの詳細を含みます。
  • CalendarContract.Events テーブルはイベント特有の情報を保持します。テーブルの各ローは1つのイベントのタイトル、場所、開始時刻、終了時刻などの情報を含みます。イベントは1度だけ発生させることも、複数回繰り返し発生させることもできます。参加者、リマインダー、その他拡張可能なプロパティは別のテーブルに保存され、イベントの _ID がそれらとイベントのリンクに利用されます。
  • CalendarContract.Instances テーブルは1つのイベントの発生ごとの開始時刻と終了時刻を保持します。テーブルの各ローは1つの発生イベントを表します。1度だけのイベントではインスタンスとイベントは1対1でマッピングされます。繰り返し起こるイベントでは、そのイベントの複数の発生イベントに対する複数のローが自動的に生成されます。
  • CalendarContract.Attendees テーブルはイベントの参加者やゲストの情報を保持します。各ローは1つのイベントの1人のゲストを表します。その人のゲストタイプとイベントでの役割を指定します。
  • CalendarContract.Reminders テーブルはアラート/通知のデータを保持します。各ローはイベントに対する1つのアラートを表します。1つのイベントは複数のリマインダーを持つことができます。1イベントあたりのリマインダー数は与えられたカレンダーの sync adapter によってセットされる MAX_REMINDERS によって指定されます。リマインダーには、イベントのスケジュールされている時刻から何分前かと、ユーザーに通知するための方法(アラーム、メール、SMSなど)が指定されています。
  • CalendarContract.ExtendedProperties テーブルは sync adapter によって利用される不透明なデータフィールドを保持します。provider はこのテーブルのアイテムに対して、関連するイベントが削除されたときにアイテムを削除する以外はアクションを起こしません。

Calendar Provider を使ってユーザーのカレンダーデータにアクセスするには READ_CALENDAR パーミッション(読み出しアクセス用)や WRITE_CALENDAR(書き込みアクセス用)が必要です。

Event intent

行いたいことがユーザーのカレンダーにイベントを追加することだけなら、"vnd.android.cursor.item/event" MIME type の ACTION_INSERT intent を使って実現することができます。この intent を呼び出すと、カレンダーアプリの新しいイベントを作成する Activity が起動します。この intent を使う場合はパーミッションは必要ありません。intent の以下の extras でイベントの詳細を指定することができます。

■ Voicemail Provider

新しい Voicemail Provider によって、ユーザーの全ての voicemail を1カ所で表示するために、デバイスに voicemail を追加することができるようになりました。例えば、電話のサービスプロバイダーからの voicemail, VoIP からの voicemail, その他代わりの音声サービスからの voicemail など、ユーザーが複数の voicemail ソースを持つことができます。これらのアプリは Voicemail Provider API を使って彼らの voicemail をデバイスに追加することができます。そして、ビルトインの Phone アプリではユーザーに全ての voicemail を一カ所にまとめて表示します。システムの Phone アプリだけが全ての voicemail を読むことができますが、voicemail を提供している各アプリは、システムに追加された彼らの voicemail を読むことはできます(ただし、他のサービスが追加した voicemail を読むことはできません)。

現状では、API でサードパーティのアプリがシステムから全ての voicemail を読むことを許可していないので、サードパーティアプリが voicemail API を使ってすべきことはユーザーに届ける voicemail を持つことです。

VoicemailContract クラスは Voicemail Provider の content provider を定義します。サブクラスの VoicemailContract.VoicemailsVoicemailContract.Status は、デバイスのストレージに voicemail データを追加できるアプリを表すテーブルを提供します。voicemail provider アプリの例として Voicemail Provider Demo を見てください。

■ Multimedia

Android 4.0 では写真、ビデオ、音楽などのメディアとやりとりするアプリ向けの新しい API が追加されました。

Media Effects

新しい media effect フレームワークによって、様々なビジュアルエフェクトを画像やビデオにつけることができるようになりました。例えば、画像エフェクトでは簡単に赤目を直したり、画像をグレイスケール(モノクロ)に変換したり、明るさを調整したり、彩度を調整したり、画像を回転させたり、魚眼効果をつけたり、などができます。システムは全てのエフェクト処理を最大のパフォーマンスを得るために GPU 上で行います。

パフォーマンスの最大化のために、エフェクトは OpenGL テクスチャへ直接適用されます。そのため、あなたのアプリケーションではエフェクト API を使用する前に、適切な OpenGL コンテキストを持つ必要があります。エフェクトが適用されるテクスチャはビットマップやビデオやカメラからのものだと思いますが、これらのテクスチャには一定の制約があります。
  • 1. GL_TEXTURE_2D テクスチャ画像にバウンドされていなければならない
  • 2. 少なくとも1つの mipmap レベルを含んでいなければならない

1つの Effect オブジェクトは、画像フレームに適用できる 1つの media effect を定義します。
1つの Effect を作成するワークフローは
パラメータ名とパラメータ値を指定して setParameter() を呼ぶことでエフェクトのパラメータを調整することができます。各エフェクトタイプによって設定できるパラメータは異なります。どのパラメータを指定できるかはエフェクト名と一緒にドキュメントに記述されています。例えば EFFECT_FISHEYE は歪みの scale を設定するパラメータを持っています。

テクスチャにエフェクトを適用するには Effect 上で apply() を呼び、入力テクスチャ、それの横幅と縦幅、出力テクスチャを渡します。入力テクスチャは GL_TEXTURE_2D テクスチャにバウンドされていなければなりません(通常は glTexImage2D() 関数を呼ぶことで行えます)。複数の mipmap レベルを提供することができます。出力テクスチャがテクスチャイメージにまだバウンドされていない場合は、自動的にエフェクトによって mipmap レベルが1つ (0) の GL_TEXTURE_2D としてバウンドされ、 入力と同じサイズになります。

EffectFactory にリストされた全てのエフェクトはサポートが保証されています。しかし、外部ライブラリから利用可能な付加的エフェクトは全てのデバイスがサポートしているとは限りません。そのため、使いたいエフェクトが外部ライブラリからのものの場合、最初に isEffectSupported() を呼んでデバイスがサポートしているかチェックしなければなりません。

Remote control client

新しい RemoteControlClient によって、デバイスロックスクリーンなどのリモートコントロールクライアントからメディアプレイヤーの再生をコントロールすることができるようになります。また、メディアプレイヤーは現在再生中の曲のトラック情報やアルバムアートなどをリモートコントロール上に表示することもできるようになります。

あなたのメディアプレイヤーでリモートコントロールクライアントを有効にするには、RemoteControlClient のコンストラクタに ACTION_MEDIA_BUTTON をブロードキャストする PendingIntent を渡してインスタンスを生成します。さらに、インテントは ACTION_MEDIA_BUTTON イベントを処理するあなたのアプリ内の BroadcastReceiver コンポーネントを明示的に宣言しなければなりません。

どのメディアコントロール入力がプレイヤーで処理できるかを宣言するには setTransportControlFlags()RemoteControlClient 上で呼びます。そのさい、FLAG_KEY_MEDIA_PREVIOUSFLAG_KEY_MEDIA_NEXT などの FLAG_KEY_MEDIA_* 形式のフラグを渡します。

そうしたら、RemoteControlClientMediaManager.registerRemoteControlClient() に渡して登録する必要があります。一度登録したら、RemoteControlClient のインスタンスを生成するときに渡した BroadcastReceiver がリモートコントロール上のボタンが押されたときに ACTION_MEDIA_BUTTON イベントを受け取ります。受け取る intent には KeyEvent が含まれており、getParcelableExtra(Intent.EXTRA_KEY_EVENT) でどのメディアキーが押されたかを取得することができます。

再生中のメディアの情報をリモートコントロール上に表示するには、editMetaData() を呼び出して、返ってきた RemoteControlClient.MetadataEditor にメタデータを追加します。メディアのアートワーク用のビットマップや、経過時間などの数値情報、トラックタイトル等のテキスト情報を提供することができます。利用可能なキーの情報は MediaMetadataRetriever 内の METADATA_KEY_* 形式のフラグを見てください。

サンプル実装として、Random Music Player をみてください。このサンプルでは、Android 4.0 でリモートコントロールクライアントを有効にする互換性ロジックを提供しており、Android 2.1 以上のデバイスで引き続きサポートするようになっています。

Media player

  • MediaPlayer からオンラインメディアストリーミングでは INTERNET パーミッションが必要になりました。MediaPlayer をインターネット上のコンテンツを再生するのに使う場合は、INTERNET パーミッションがマニフェストに追加されていることを確認してください。そうしないと Android 4.0 からメディアの再生処理が動かなくなります。
  • setSurface() で video sink として振る舞う Surface を定義できます。
  • setDataSource() で HTTP(S) live streaming を便利にする付加的な HTTP ヘッダをリクエストに追加できます。
  • HTTP(S) live streaming はリクエスト全体に渡って HTTP cookies に関連します。

Media tyes

Android 4.0 で以下のサポートが追加されました。
  • HTTP/HTTPS live streaming protocol version 3
  • ADTS raw AAC audio encoding
  • WEBP images
  • Matroska video
より詳しい情報は Supported Media Formats を見てください。

■ Camera

Camera クラスに顔検出と、フォーカスと測定エリアのコントロールをする新しい API が追加されました。

Face detection

Camera アプリは Android の顔検出 API を使った機能をより高くすることができます。これまでは単に顔の位置を検出するだけでしたが、目や口の位置など顔の特徴も検出できるようになりました。

カメラアプリで顔を検出するには、まず setFaceDetectionListener() を呼んで Camera.FaceDetectionListener を登録する必要があります。そうしたら、camera surface を開始して、 startFaceDetection() を呼ぶことで顔検出をスタートさせることができます。

カメラがカメラシーン内に1つ以上の顔を検出すると、Camera.FaceDetectionListener 内の onFaceDetection() コールバックが呼ばれます。このとき、Camera.Face オブジェクトの配列が渡されます。

Camera.Face クラスのインスタンスは顔検出についての複数の情報を含みます:
  • 顔の領域を表す Rect 。カメラの現在の view フィールドからの相対値
  • その物体が人間の顔であるとシステムが認識した信頼度をあらわす 1 - 100 までの整数値
  • 複数の顔をトラックするのに利用できる識別ID
  • 目や口の位置を表すいくつかの Point オブジェクト

注意: 顔検出はいくつかのデバイスでサポートされていない可能性があります。そのため、まず getMaxNumDetectedFaces() を呼んで、戻り値が 0 より大きいことをチェックしてください。また、いくつかのデバイスでは目や口の識別をサポートしていないことがありえます。その場合、Camera.Face オブジェクトには null が入ります。

Focus and metering areas

カメラアプリで、カメラがフォーカスやホワイトバランスや露光の測定に使う領域をコントロールできるようになりました。両方の機能とも新しい Camera.Area クラスを使って、フォーカスや測定される現在のビューの領域を指定します。1つの Camera.Area には領域を表す Rect と、その領域が他の領域と比べてどれだけ重要かを表すレベル(整数値)が含まれます。

フォーカス領域と測定領域を設定する前に、まず getMaxNumFocusAreas()getMaxNumMeteringAreas() を呼んで、デバイスがそれぞれの昨日をサポートしているかチェックしてください。戻り値が 0 より大きければサポートされています。

使用されるフォーカス領域や測定領域を設定するには、 setFocusArea()setMeteringArea() を呼びます。そのとき、フォーカスや測定として考慮する情報を含んだ Camera.Area オブジェクトのリストをそれぞれに渡します。例えば、プレビューでユーザーがタッチした領域にフォーカスするような機能を実装する場合、タッチされた領域を Camera.Area オブジェクトに変換して、その領域をフォーカス領域としてセットします。その領域のシーンが変わっても、その領域のフォーカスと露光は継続的にアップデートされます。

Continuous auto focus for photos

写真を撮るときに連続して自動フォーカス (confinuous auto focusing : CAF) させることができるようになりました。あなたのカメラアプリで CAF を有効にするには、setFocusMode()FOCUS_MODE_CONTINUOUS_PICTURE を渡します。写真をとる準備ができたら、autoFocus() を呼びます。あなたの Camera.AutoFocusCallback はフォースできたかどうかを表すコールバックを直ちに受け取ります。コールバックを受け取ったあとに CAF を再開するには、cancelAutoFocus() を呼ぶ必要があります。

注意: 連続自動フォーカス(CAF) はビデオ撮影時もサポートしています。その場合は API level 9 で追加された FOCUS_MODE_CONTINUOUS_VIDEO を指定します。

Other camera features

  • ビデオを撮影している間、takePicture() を呼ぶことでビデオセッションを中断せずに写真を撮ることができます。これを行う前に、isVideoSnapshotSupported() を呼んで、ハードウェアがサポートしているかどうか確かめるべきです。
  • 自動的に露光とホワイトバランスのプロパティが変化するのを防ぐために、setAutoExposureLock()setAutoWhiteBalanceLock() を呼んでこれらの値をロックすることができるようになりました。
  • setDisplayOrientation() をプレビューが走っている間も呼べるようになりました。これまではプレビューが始まる前しか呼ぶことができなかったのですが、いつでも向きを変えられるようになりました。

Camera broadcast intents

  • Camera.ACTION_NEW_PICTURE: ユーザーが新しく写真を撮影したことを示すインテントです。ビルトインのカメラアプリは写真が撮られたあとにこのブロードキャストを投げます。サードパーティのカメラアプリも写真を撮ったあとはこのインテントをブロードキャストするべきです。
  • Camera.ACTION_NEW_VIDEO : ユーザーが新しくビデオを撮影したことを示すインテントです。ビルトインのカメラアプリはビデオが録画されたあとにこのブロードキャストを投げます。サードパーティのカメラアプリもビデオを撮影したあとはこのインテントをブロードキャストするべきです。

■ Android Beam (NDEF Push with NFC)

Android Beam は "NDEF Push" として知られているプロセスを使って、NDEF message をあるデバイスから別のデバイス送る、新しい NFC 機能です。Android Beam をサポートしている2台の Android デバイスがかなり近づく(4cm くらい、通常は背面同士をタッチする)と、データ転送が初期化されます。NDEF message 内にはデバイス間で共有したいさまざまなデータを入れることができます。例えば、Android Beam を使って、People アプリはコンタクトを共有しますし、YouTube アプリは動画を共有し、ブラウザアプリは URL を共有します。

Android Beam を使ってデバイス間でデータを転送するには、Activity が foreground にある間に、共有したい情報を持った NdefMessage を作成する必要があります。それから、以下の2つの内、いずれかの方法を使ってシステムに NdefMessage を渡します。 システムが他のデバイスへの NDEF message の送信に成功したときに特定のコードを1度だけ実行したこともあるでしょう。そのときは、NdefAdapter.OnNdefPushCompleteCallback を実装し、setNdefPushCompleteCallback() でその実装を渡すことでできます。システムはメッセージが配送されたときに onNdefPushComplete() を呼び出します。

受信側のデバイスでは、システムは NDEF Push メッセージを通常の NFC タグと同じ方法で処理します。システムは Activity を開始するために、NdefMessage 内の最初の NdefRecord の URL もしくは MIME type がセットされた ACTION_NDEF_DISCOVERED のインテントを投げます。応答したい Activity の intent filter にその URL や MIME type を設定して処理したいものにだけ反応することができます。Tag Dispatch のより詳しい情報は NFC developer guide を見てください。

もし、NdefMessage で URI を運びたいなら、そのための便利な createUri メソッドを使って、1つの文字列もしくは1つの Uri オブジェクトから NdefRecound を作成することができます。URI が Android Beam イベント中にあなたのアプリが受信したい特別なフォーマットであるならば、渡された NDEF message を受け取るために、同じ URI scheme を使って Activity に intent filter をセットしてください。

また、別のアプリが同じ intent action の filter を設定していても、渡された NDEF message をあなたのアプリが確実に処理することを保証するために、渡す NdefMessage に "Android application record" も含めてください。アプリのパッケージ名を渡して createApplicationRecord() を呼ぶことで Android application record を作成することができます。他のデバイスが application record の入った NDEF message を受信して、その intent を処理できる activity を含むアプリが複数あった場合、システムは常にそのメッセージを application record との一致によってあなたのアプリの activity に渡します。ターゲットデバイスにあなたのアプリがインストールされていない場合は、Android application record を使ってシステムは Android Market を起動し、そのアプリをインストールできるようユーザーを導きます。

NDEF Push でメッセージ送る NFC API をあなたのアプリが使っていない場合、Android は通常の振る舞いを提供します。あなたのアプリがデバイス foreground にあって、Android Beam が他の Android デバイスから呼ばれたら、一方のデバイスはあなたのアプリを識別する Android application record を含んだ NDEF message を受け取ります。受け取ったデバイスにそのアプリがインストールされていたら、システムはそれを起動し、インストールされていなかったら、Android Market を開いて、アプリをインストールできるようにユーザーを導きます。

NFC Basics developer guide で Android Beam と NFC 機能についてより詳しく読むことができます。Android Beam を使ったサンプルコードは Android Beam Demo を見てください。

■ Wi-Fi Direct

hotspot やインターネット接続なに Android デバイス間や他のタイプのデバイスと peer-to-peer (P2P) 接続するための Wi-Fi Direct をサポートするようになりました。Android フレームワークは Wi-Fi P2P API のセットを提供します。これを使うことで、Wi-Fi Direct をサポートしているデバイスを見つけて接続し、Bluetooth 接続よりも長い距離で速い通信ができます。

新しい android.net.wifi.p2p パッケージには Wi-Fi で peer-to-peer 接続を行うための全ての API が含まれています。もっとも重要なクラスは WifiP2pManager です。 getSystemService(WIFI_P2P_SERVICE) を呼ぶことで取得することができます。WifiP2pManager に含まれる API では以下のことができます:
  • initialize() を呼んで P2P 接続のための初期化を行う
  • descoverPeers() を呼んで近くのデバイスを見つける
  • connect() を呼んで P2P 接続を開始する
  • その他いろいろ
その他いくつかのインタフェースとクラスも同じく必要です:
  • WifiP2pManager.ActionListener インタフェースによって、デバイスの探索や接続の処理が成功または失敗したときにコールバックを受信できます。
  • WifiP2pManager.PeerListListener インタフェースによって、見つけた peers の情報を受け取ることができます。コールバックが提供する WifiP2pDeviceList から範囲内の各デバイスの WifiP2pDevice オブジェクトが取得でき、これにはデバイス名、アドレス、デバイスタイプ、デバイスがサポートするWPS設定などの情報が入っています。
  • WifiP2pManager.GroupInfoListener インタフェースによって、P2P グループの情報を受け取ることができます。コールバックが提供する WifiP2pGroup オブジェクトには、オーナー、ネットワーク名、パスフレーズなどのグループ情報がふくまれています。
  • WifiP2pManager.ConnectionInfoListener インタフェースによって、現在の接続についての情報を受け取ることができます。コールバックが提供する WifiP2pInfo オブジェクトには、グループが形成されたかどうかや、誰がグループのオーナーかなどの情報が含まれています。
Wi-Fi P2P API を使うには、次のユーザーパーミッションが必要です。
  • ACCESS_WIFI_STATE
  • CHANGE_WIFI_STATE
  • INTERNET (あなたのアプリが技術的にインターネットに接続しなくても、標準の java socket で Wi-Fi Direct peers を使って通信するにはインタネットのパーミッションが必要です)
Android システムは Wi-Fi P2P の特定のイベントごとに異なるアクションをブロードキャストします。 より詳しい情報は WifiP2pManager ドキュメントを見てください。また、サンプルアプリは Wi-Fi Direct Demo です。

■ Bluetooth Health Devices

Bluetooth Health Profile デバイスをサポートするようになりました。これにより、心拍モニターや血液メーター、体温計、測定器などの医療デバイスと Bluetooth を使って通信するアプリを作成できます。

通常のヘッドセットや A2DP profile デバイスと同じように、getProfileProxy()BluetoothProfile.ServiceListenerHEALTH profile タイプを渡して呼んで、profile proxy オブジェクトとの接続を確立します。

一度 Health Profile proxy (BluetoothHealth オブジェクト) を取得したら、次の新しい Bluetooth クラスを呼び出して、ペアの医療デバイスと接続や通信を行います。
  • BluetoothHealthCallback : このクラスを継承して、アプリの登録状態と Bluetooth channel 状態の変化のアップデート受け取るためのコールバックメソッドを実装する必要があります。
  • BluetoothHealthAppConfiguration : あなたの BluetoothHealthCallback へコールバックしている間、このオブジェクトのインスタンスを受け取ります。これには利用可能な Bluetooth 医療デバイスについての設定情報が含まれています。これらの情報は、 BluetoothHealth API で接続の初期化や終了などいくつかの操作を実行するのに必要です。
Bluetooth Health Profile のより詳しい情報は BluetoothHealth ドキュメントを見てください。

■ Accessibility

Android 4.0 では視覚的にハンディのあるユーザーの accessibility を改善しました。新しく explore-by-touch mode が追加され、view コンテンツについてより情報を提供したり、高度な accessibility サービスを開発するために API が拡張されました。

Explore-by-touch mode

画面上をタッチしたりドラックすることでコンテンツの説明する音声を聞くことができ、視覚のない人でも画面上のコンテンツを探すことができます。explore-by-touch mode は仮想的なカーソルとして動作し、d-pad や trackball の操作でスクリーンリーダーが説明文を見つけるのと同じ方法で、画面上での "hover" をシミュレートして音声案内する情報を見つけます。読まれる情報は android:contentDescriptionsetContentDescription() で提供されます。つまり、アプリの view に対して説明文を提供するよう心がけてください。特に ImageButton, EditText, ImageView など通常状態で説明文を含まないものは気をつけてください。

Accessibility for views

スクリーンリーダーのような accessibility サービスで利用できる情報を拡張するために、カスタム View コンポーネントで accessibility イベントで新しいコールバックメソッドを実装できます。

最初に知っておくべき重要なことは、Android 4.0 で sendAccessibilityEvent() メソッドの動作が変わったということです。以前の Android バージョンでは、ユーザーがデバイスの accessibility サービスを有効にして、クリックやホバーなどの入力イベントがおこると、対応する view の sendAccessibilityEvent() が呼ばれていました。以前は sendAccessibilityEvent() の実装は AccessibilityEvent を初期化し、AccessibilityManager に送っていました。新しい動作では、view や親 view がコンテキスト情報をイベントに追加できるようにするための追加されたコールバックメソッドが含まれます : View クラスを継承するときに便利な上記の新しいメソッドに加えて、AccessibilityDelegate を継承して view に setAccessibilityDelegate() を設定することで、任意の View のイベントコールバックに介入することもできます。こうした場合、view 内の各 accessibility メソッドは delegate 内の対応するメソッドに呼び出しを持ち越します。例えば、view が onPopulateAccessibilityEvent() の呼び出しを受けたとすると、それは View.AccessibilityDelegate 内の同じメソッドにも渡されます。delegate で処理されないメソッドはすぐにデフォルトの動作に戻ってきます。これにより、View クラスを継承せずに必要なメソッドだけを上書きできます。

新しい accessibility API をサポートしつつ、Android バージョン 4.0 以前との互換性を維持したい場合、後方互換性を持つようにデザインされた accessibility API を提供する最新バージョンの v4 support library (Compatibility Package, r4 に含まれる) のユーティリティクラスセットを使って実現できます。

Accessibility services

accessibility service を開発しているなら、いくつかの accessibility イベントについての情報は、ユーザーにより高度な accessibility フィードバックを可能にするために大幅に拡張されています。特に、イベントは View の物体に基づいて生成され、よりよいコンテキスト情報を提供し、accessibility service は付加的な view 情報の取得と特別なケースを扱うために view 階層をさかのぼることができます。 もしあなたがスクリーンリーダーのような accessibility service を開発しているなら、次の手順で付加的なコンテンツ情報の追加と view 階層をさかのぼることができます。 あなたのアプリを accessibility service としてシステムに公開するには、AccessibilityServiceInfo に対応する XML 設定フィアルを宣言する必要があります。accessibility service を作るためのより詳しい情報は AccessibilityServiceSERVICE_META_DATA を見てください。

Other accessibility APIs

デバイス accessibility 状態に興味があるなら、AccessibilityManager で以下のようないつくかの新しい API が使えます :

■ Spell Checker Services

新しい spell checker フレームワークによって、input method フレームワーク(IME用)と似た方法でスペルチェッカーをアプリで作ることができるようになりました。新しいスペルチェッカーを作るには、SpellCheckerService を継承したクラスを実装し、インタフェースのコールバックメソッドで提供される文字列に基づいてスペル候補を提供する SpellCheckerService.Session クラスを継承する必要があります。SpellCheckerService.Session コールバックメソッドでは、SuggestionsInfo オブジェクトとしてスペル候補を返します。

spell checker service を持つアプリは、このサービスによって必要とされる BIND_TEXT_SERVICE パーミッションを宣言しなければなりません。また、そのサービスの intent filter として <action android:name="android.service.textservice.SpellCheckerService" />を宣言し、<meta-data> エレメントでスペルチェッカーの設定情報を宣言する必要もあります。

サンプルコードはサンプルアプリの Spell Checker を見てください。

■ Text-to-speech Engines

Android の text-to-speech (TTS) API が大幅に拡張され、アプリがより簡単に custom TTS eingine を実装できるようになりました。TTS エンジンを使いたいアプリはエンジンを選択するためのいくつかの新しい API を使うことができます。

Using text-to-speech engines

Android の以前のバージョンでは、システムが提供する TTS engine や setEngineByPackageName() を使ってセットされた custom engine を利用して text-to-speech(TTS) 操作を行う TextToSpeech クラスを使うことができました。Android 4.0 では、setEngineByPackageName() メソッドは廃止され、TTS engine のパッケージ名を受け取る新しい TextToSpeech コンストラクタを使って engine を指定できるようになりました。

また、getEngines() で利用可能な TTS engines を検索することもできます。このメソッドは TextToSpeech.EngineInfo オブジェクトのリストを返します。このオブジェクトには engine のアイコン、ラベル、パッケージ名などのメタデータが含まれます。

Building text-to-speech engines

以前は、custom engine はドキュメント化されていない native header file を使ってビルドされている必要がありました。Android 4.0 では、TTS engine を作成するためのフレームワーク API のセットがそろっています。

ベースのセットアップとして、INTENT_ACTION_TTS_SERVICE intent に応答する TextToSpeechService の実装が必要です。TTS engine の一番重要な処理は TextToSpeechService を継承したサービス内の onSynthesizeText() 中に行います。システムはこのメソッドに次の2つのオブジェクトを渡します :
  • SynthesisRequest: 合成するテキスト、ロケール、話す速度、声の高さなどいくつかのデータを含む
  • SynthesisCallback: 話されたデータの結果を streaming audio として TTS engine が届けるためのインタフェース。最初に engine は start() を呼んで engine が audio を届ける準備ができていることを示す。それから、audioAvailable() を呼んで byte buffer の audio data を渡す。全ての audio を渡し終わったら done() を呼ぶ
フレームワークは TTS engine を作成するための(本当の) API をサポートし、native code をサポートするための実装は削除されました。compatibility layer についてのブログポストを見てください。古い TTS engine を新しいフレームワークに変換する方法がわかります。

新しい API を使った TTS engine の例は Text To Speech Engine サンプルアプリを見てください。

■ Network Usage

Android 4.0 では、アプリケーションが使っているネットワークデータがどのくらいあるのかを、ユーザーが正確なビジュアル(グラフとか)で見れるようになりました。ユーザーは設定アプリで、個々のアプリに対してネットワークデータ使用のリミットを設定できたり、バックグラウンドデータの使用を無効にすることもできます。ユーザーがアプリのデータへのアクセスを無効にするのを避けるために、効果的にデータ接続を使い、接続に依存した使用方法を調整する戦略を立てるべきです。

もしあなたのアプリが多くのネットワーク通信を行っているなら、ユーザーがアプリのデータ習慣(どのくらいの頻度でデータをシンクするのか、Wi-Fi のときだけアップロード/ダウンロードを行うかどうか、ローミング中にデータを使うかどうか、など)をコントロールするための設定を提供するべきです。これらのコントロールが利用可能であれば、データ通信がリミットに達して、ユーザーがあなたのアプリのデータへのアクセスを無効にすることはより少なくなるでしょう。これらの設定に preference activity を提供するのであれば、マニフェストで ACTION_MANAGE_NETWORK_USAGE action の intent filter を定義しておくべきです。例えば : <activity android:name="DataPreferences" android:label="@string/title_preferences"> <intent-filter> <action android:name="android.intent.action.MANAGE_NETWORK_USAGE" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> この intent filter はシステムにこの activity はアプリケーションのデータ使用をコントロールするものだということを示します。こうすると、ユーザーが設定アプリからあなたのアプリのデータ使用量をみたときに、"View application settings" ボタンが有効になっていて、ボタンを押すとあなたのアプリの preference activity を開けるので、ユーザーがあなたのアプリのデータ使用量をよりよくすることができます。

さらに、getBackgroundDataSetting() は廃止になり常に true を返すようになりました。代わりに getActiveNetworkInfo() を使ってください。ネットワーク通信を試みる前に、常に getActiveNetworkInfo() を呼んで、現在のネットワークを表す NetworkInfo を取得し、 isConnected() でデバイスが接続しているかどうかチェックしてください。デバイスがローミング中かどうかや Wi-Fi で接続しているかどうかなど、その他の接続プロパティも取得することができます。

■ RenderScript

RenderScript に3つのメジャー機能が追加されました。
  • framebuffer object への Off-screen レンダリング
  • view 内部でのレンダリング
  • RS for each from the framework APIs (←意味がわからなかったので訳せませんでした m(_ _)m)
Allocation クラスが新しく USAGE_GRAPHICS_RENDER_TARGET メモリースペースをサポートし、これにより直接 Allocation にレンダリングしたり、 framebuffer object として扱ったりできるようになりました。

RSTextureViewView の内部に RenderScript グラフィクスを表示する手段を提供します。別の window を作る RSSurfaceView とは異なります。この大きな違いによって、Activity のレイアウト上の、内側に RenderScript グラフィック を描画した View を移動したり、変形したりアニメーションすることができます。

Script.forEach() メソッドで VM レベルから RenderScript 計算スクリプトを呼ぶことができ、自動的にデバイスの利用可能なコアへ割当てられます。このメソッド直接使わないでください。しかし、あなたが書いた計算する RenderScript は、リフレクトされた RenderScript クラスで呼べるように forEach() メソッドを持ってください。プロセスに入力 Allocation を、結果を書くところに出力 Allocation を渡すことで、リフレクトされた forEach() メソッドを呼ぶことができます。そして、 RenderScript の場合の FieldPacker データ構造にはより情報が必要です。Allocation だけが不可欠なもので、データ構造はオプションです。

■ Enterprise

Android 4.0 で企業向けアプリの機能として以下の内容が拡張されました。

VPN service

新しい VpnService によって、アプリが自身の VPN (Virtual Perivate Network) を構成し、Service として走らせることができます。 VPN サービスは自身のアドレスとルーティング規則で virtual network へのインタフェースを作成し、ファイルディスクリプタですべての読み込みと書き込みを実行します。

VPN サービスを作成するには、ネットワークアドレス、DNS サーバー、ネットワークルートなどを指定できる VpnService.Builder を使います。指定が完了したら、ParcelFileDescriptor を返す establish() を呼び出してインタフェースを確立します。

VPN サービスはパケットに介入するため、セキュリティへの影響があります。そのため、VpnService を実装するサービスは、システムだけがこれにバインドできることを確実にするために、 BIND_VPN_SERVICE を要求しなければなりません(システムだけがバインドするパーミッションをもち、アプリはリクエストできません)。そして、あなたの VPN サービスを使うには、ユーザーが手動でシステム設定からこれを有効にする必要があります。

Device policies

デバイスの制限を管理するアプリで setCameraDisabled()USES_POLICY_DISABLE_CAMERA プロパティ(ポリシー設定ファイルで <disable-camera /> に適用される)を使ってカメラを無効にできるようになりました。

Certificate management

新しい KeyChain クラスは証明書をシステムキーストアにインポートやアクセスする API を提供します。証明書はクライアント証明書(ユーザーの身元を検証する)と認証局の証明書(サーバーの身元を検証する)の両方のインストールを合理化します。Web ブラウザやメールクライアントなどのアプリは、サーバーにユーザーを認証させるためにインストールされた証明書にアクセスできます。より詳しい情報は KeyChain ドキュメントを見てください。

■ Device Sensors

Android 4.0 で新しいセンサータイプが追加されました。 TYPE_AMBIENT_TEMPERATURETYPE_RELATIVE_HUMIDITY センサーの両方をデバイスがサポートしているなら、これらを露点と絶対湿度を計算するのに使えます。

以前の温度センサーである TYPE_TEMPERATURE は廃止になりました。代わりに TYPE_AMBIENT_TEMPERATURE センサーを使ってください。

加えて、Android の 3つの合成センサーがかなり改善され、レイテンシが低くなり、出力がスムーズになりました。これらのセンサーには重力センサー (TYPE_GRAVITY)、回転ベクトルセンサー (TYPE_ROTATION_VECTOR)、線形加速センサー (TYPE_LINEAR_ACCELERATION) が含まれます。改善されたセンサーは、出力を改善するためにジャイロスコープセンサーに頼っています。そのため、これらのセンサーはジャイロスコープが搭載されたデバイスにだけ現れます。

■ Action Bar

いくつかの新しい動作をサポートするために ActionBar がアップデートされました。もっとも重要なのことは、全ての画面サイズでオプショナルなユーザー操作を提供するために、小さい画面サイズのときにシステムが action bar のサイズと設定を優雅に管理することです。例えば、画面が狭いとき(ハンドセットで縦画面のときなど)、action bar のナビゲーションタブはメインの action bar の真下に表れる "stacked bar" 内に表示されます。また、 "split action bar" を opt-in することもできます。これにより画面が狭いときに全ての action item を画面下部の separate bar に配置することができます。

Split action bar

もし action bar に複数の action item が含まれる場合、全ての項目を狭い画面上の action bar に合わせることはありません。システムはそれらのうちのいくつかを overflow menu に置きます。しかし、Android 4.0 から "split action bar" を有効にできるようになりました。これにより、より多くの action item を画面下部のセパレートバー上に表示できるようになりました。split action bar を有効にするには、"splitActionBarWhenNarrow" を指定した android:uiOptions 属性を manifest ファイルの <application> タグもしくは個々の <activity> タグに指定します。有効の場合、画面が狭いときに全ての action item 用にシステムは付加的なバーを画面下部に追加します(優先的な(上部の) action bar には action item が表示されなくなります)。

ActionBar.Tab API によって提供されるナビゲーションタブを使いたいが、上部にメインの action bar は必要ない場合(上部にタブだけを表示したい場合)、上で説明した split action bar を有効にして、さらに setDisplayShowHomeEnabled(false) を呼ぶと action bar のアプリアイコンが無効になります。メインの action bar の左には何も表示されず、画面上部にナビゲーションバー、画面下部に action item が置かれるようになります。

Action bar styles

action bar のカスタマイズしたスタイルを適用したい場合は、新しいスタイルプロパティの backgroundStackedbackgroundSplit で背景の画像や色をスタックするバーと区切りバーにそれぞれ適用することができます。また、ランタイム中に setStackedBackgroundDrawable()setSplitBackgroundDrawable() でこれらのスタイルをセットすることもできます。

Action provider

新しい ActionProvider クラスによって action item を処理する特別なハンドラを作成できます。1つの action provider は 1つの action view、デフォルトの action 動作、関連した各 action item のサブメニュー を定義することができます。動的な動作(可変な action view やデフォルト動作やサブメニュー)を行う action item を作成したい場合は、fragment や activity 内で action item を変換するよりも、再利用可能なコンポーネントを作成するために、ActionProvider を拡張するのがいい解決法です。

例えば、ShareActionProviderActionProvider を拡張して簡単にアクションバーから "share" action を行えます。ACTION_SEND intent を呼び出すこれまでの action item を使うかわりに、この action provider を使って ACTION_SEND intent を処理するアプリ一覧のドロップダウンリストの action view を表示できます。ユーザーがリストからアプリを選択すると、ShareActionProvider はその選択を記憶して、そのアプリでシェアするためにより速くサクセスするために action view にそれを提供します。

1つの action item に対して1つの action provider を宣言するには Activity の options menu の <item> エレメントに android:actionProviderClass 属性に action provider のクラス名を指定します。例えば : <item android:id="@+id/menu_share" android:title="Share" android:showAsAction="ifRoom" android:actionProviderClass="android.widget.ShareActionProvider" /> あなたの Activity の onCreateOptionsMenu() コールバックメソッドでは、menu item から action provider のインスタンスを取得して、intent をセットします。 public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.options, menu); ShareActionProvider shareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share).getActionProvider(); // Set the share intent of the share action provider. shareActionProvider.setShareIntent(createShareIntent()); ... return super.onCreateOptionsMenu(menu); } ShareActionProvider を使った例として、ApiDemos の ActionBarActionProviderActivity を見てください。

Collapsible action views

action view を提供する action item で、 action view 状態とこれまでの action item 状態を切り替えられるようになりました。以前は SearchView だけが action view として使われたときに閉じるようサポートされてしました。しかし、任意の action item を action view として追加し、展開状態(action view が見えている)と閉じている状態(action item が見えている)を切り替えることができるようになりました。

閉じられる action view を含む action item を宣言するには menu の XML ファイルの <item> エレメントの android:showAsAction 属性に "collapseActionView" フラグを入れます。

action view が展開した状態と閉じた状態が切り替わったことをコールバックで受け取るには、MenuItem.OnActionExpandListener のインスタンスを setOnActionExpandListener() を呼んでそれぞれの MenuItem でに登録します。典型的には、onCreateOptionsMenu() コールバックの中で行ってください。

閉じれる action view をコントロールするには、collapseActionView()expandActionView() を各 MenuItem に対して呼びます。 カスタム action view を作る場合は、CollapsibleActionView インタフェースを実装して view が開いたり閉じたりしたときのコールバックを受け取ることができます。

Other APIs for action bar

  • setHomeButtonEnabled() で アイコン/ロゴ がホームもしくは "up" をナビゲートするボタンとして動作するかどうかを指定できる(true を渡すとボタンとして動作する)。
  • setIcon()setLogo() でランタイム中に Action Bar のアイコンやロゴを設定できます。
  • Fragment.setMenuVisibility() によって fragment で定義されたメニューアイテムの可視状態を有効/無効にすることができる。Activity に Fragment が追加されたが、それが不可視状態であるときに menu item を隠すのに使えます。
  • FragmentManager.invalidateOptionsMenu() によって Activity から同等のメソッドが利用できない可能性がある Fragment ライフサイクルのいくつかの状態の間 activity の options menu を無効にできます。

■ User Interface and Views

Android 4.0 で追加された新しい view とその他 UI コンポーネントについて紹介します。

GridLayout

GridLayout は子 view を格子状に配置する新しい view group です。TableLayout と違い、GridLayout はフラットな階層に依存し、table rows のような構造を作るための中間の view を必要としません。代わりに子 view は配置される行(rows(s))と列(column(s))を指定します(セルは複数の行や列をまたぐことができます)。デフォルトでは順番に格子の行と列を占めるように配置されます。子 view の間のスペースは新しい Space view のインスタンスを使ったり、関連するマージンパラメータを子viewに設定したりすることで調整できます。

サンプルは ApiDemosGridLayout です。

TextureView

TextureView はビデオや OpenGL シーンなどの content stream を表示できる新しい view です。SurfaceView と似ていますが、TextureView は別の window をつくるのではなく、通常の view のように振る舞うという点でユニークです。そのため、他の View object と同じように扱うことができます。例えば、ViewPropertyAnimator を使って変形やアニメーションをさせたり、setAlpha() で透明度を調整できたりします。

TextureView は hardware accelerated window 内でのみ動作するので注意してください。

より詳しい情報は TextureView ドキュメントをみてください。

Switch widget

新しい Switch ウィジェットは一方を反対側にスライドして(もしくは、単にタップして)2つの状態を切り替えるものです。

android:textOnandroid:textOff 属性を使ってスイッチ上に表示される文字を指定できます。android:text 属性でスイッチの横に置かれるラベル名を指定できます。

スイッチを使ったサンプルは switches.xml レイアウトファイルと関連する Switches activity を見てください。

Popup menus

Android 3.0 で、小さいコンテキストメニューを指定したアンカーポイント(通常は選択されたアイテムの場所)にポップアップする PopupMenu を紹介しました。Android 4.0 では PopupMenu を拡張して幾つかの便利な機能を追加しました :
  • menu resource ID で infalte() を呼ぶことで、popup menu のコンテンツを XML menu resource から簡単に作れるようになりました。
  • メニューが消えたことを受け取るコールバックの PupupMenu.OnDismissListener を作れるようになりました。

Preferences

新しい TwoStatePreference 抽象クラスは2つの状態から選択するオプション用の preference を提供します。この TwoStatePreference を継承した新しいクラスが SwitchPreference です。この preference は Switch ウィジェットを使った preference view を提供し、ユーザーは別のスクリーンやダイアログを開くこと無く設定の on/off を切り替えられます。例えば、設定アプリでは Wi-Fi や Bluetooth の設定に SwitchPreference を使っています。

System themes

Android 4.0 をターゲットとする(targetSdkVersionminSdkVersion"14"以上に設定されている)全てのアプリケーションのデフォルトテーマは "device default" テーマの: Theme.DeviceDefault になります。これは dark Holo テーマか、特定のデバイスによって定義された dark theme になるでしょう。

Theme.Holo ファミリーのテーマは同じバージョンの Android が動いている場合は、デバイスによって変わらないことが保証されます。Theme.Holo テーマをあなたの activity に明示的に適用するなら、同じプラットフォームバージョン内のデバイスならテーマによる特徴が変わらないので安心できます。

もしデバイス全体のテーマ(例えば、異なる OEM が提供するシステム用の異なるデフォルトテーマ)にアプリを合わせたいなら、明示的に Theme.DeviceDefault ファミリーを適用してください。

Options menu button

Android 4.0 からハンドセットが Menu ハードボタンを必要としなくなったことに気づいたと思います。あなたのすでにあるアプリケーションで option menu を使っていて Menu ボタンがあることを期待していても心配する必要はありません。既にあるアプリがそれらが期待した動作をちゃんとできるように、システムは Android の古いバージョン用にデザインされたアプリでは、スクリーン上に Menu ボタンを提供します。

ベストなユーザー体験のために、メニューアイテムへのアクセスとして ActionBar を使うようにアプリを新しくするかアップデートしてください。また、targetSdkVersion"14" をセットして最新のフレームワークのデフォルトの動作を活用してください。

Controls for system UI visibility

以前の Android では、status bar として知られていたシステムが管理する UI コンポーネントがありました。ハンドセットデバイスの上部に常駐してキャリアシグナルな時間、通知などの情報を表示していました。Android 3.0 では、画面の下部に常駐してシステムのナビゲーションコントロール(Home、Back、など) やこれまで status bar で提供してきた項目へのインタフェースを提供する system bar がタブレットデバイス用に追加されました。Android 4.0 では、navigation bar と呼ばれる新しいタイプのシステム UI が提供されます。navigation bar はハンドセット向けにデザインされた system bar の re-tuned バージョンだと思うかもしれません。システムをナビゲーションするためのハードウェア部分(ハードキーとか)を持たないデバイスに対して、ナビゲーションコントロールを提供しますが、 system bar の通知 UI と設定コントロールを残します。このように navigation bar を提供するデバイスでは上部に status bar も持ちます。

これまでは、ハンドセット上で FLAG_FULLSCREEN フラグを使って status bar を隠すことができました。Android 4.0 では、system bar の可視状態をコントロールする API は system bar と navigation bar 両方の動作をよりよく反映するようにアップデートされています。
  • SYSTEM_UI_FLAG_LOW_PROFILE フラグは STATUS_BAR_HIDDEN フラグを置き換えるものです。セットされた場合、このフラグは system bar もしくは navigation bar の "low profile" モードを有効にします。ナビゲーションボタンは薄暗くなって system bar 内の他の項目は隠れます。システムナビゲーションボタンが邪魔せず、より没入できるゲームを作るときに便利です。
  • SYSMTE_UI_FLAG_VISIBLE フラグは STATUS_BAR_VISIBLE フラグを置き換えるものです。system bar もしくは navigation bar が可視状態になることを要求します。
  • SYSTEM_UI_FLAG_HIDE_NAVIGATION は navigation bar を完全に隠すことを要求する新しいフラグです。これは navigation bar を使っているいくつかのハンドセットでのみ動作することに注意してください(タブレット上の system bar は隠せません)。navigation bar はシステムがユーザー入力を受け取るとすぐに view として返ってきます。つまり、ビデオのプレイバックなど、画面全体が必要とするがユーザーの入力が必要ではないときに便利です。
Activity 内のいずれかの view 上で setSystemUiVisibility() を呼ぶことで、 system bar と navigation bar に対してこれらのフラグをセットできます。window manager は window 内の全ての view からの全フラグを組み合わせて(もしくは一緒にして)、window が入力フォーカスを持つのと同じくらい長くシステム UI に適用します。window が入力フォーカスを失うと(ユーザーがあなたのアプリから離れたり、ダイアログが表示されたり)、フラグの効果は中止されます。同様に、view 階層からそれらの view を取り除くと、フラグは適用されなくなります。

システム UI の可視状態の変化に他のイベントを同期させる(例えば、アクションバーを隠したり、他の UI コントロールをシステム UI が隠れたときに隠すなど)には、system bar や navigation bar の可視状態が変わったことを通知する View.OnSystemUiVisibilityChangeListener を登録してください。 システム UI オプションの違いのデモは OverscanActivity を見てください。

■ Input Framework

Android 4.0 でカーソルのホバーイベント、新しいスタイラスとマウスボタンのイベントをサポートするようになりました。

Hover events

View クラスで新しく "hover" イベントがサポートされるようになりました。これにより、マウスなど画面上にカーソルを表示するポインタでバイスを使うときによりリッチなインタラクションが可能になります。

View でホバーイベントを受け取るには、View.OnHoverListener を実装し setOnHoverListener() で登録します。View 上でホバーイベントが起こったら、あなたのリスナーの onHover() が呼び出され、イベントを受け取った View と起こったホバーイベントのタイプを持つ MotionEvent を受け取ります。ホバーイエベントは次のうちのいずれかになります : ホバーイベントを処理する場合は、あなたの View.OnHoverListeneronHover() で true を返してください。false を返すと、通常のように親 view にイベントが送られます。

あなたのアプリでボタンのように現在の状態に応じて見た目が変わる widget を使っているなら、カーソルのホバー状態に応じて背景画像を変えるための android:state_hovered 属性を state list drawable で利用できます。

ホバーイベントのデモとして ApiDemos の Hover クラスを見てください。

Stylus and mouse button events

digitizer tablet やスタイラスが使えるタッチスクリーンなど、スタイラス入力デバイスからの入力を受ける API が提供されるようになりました。

スタイラスによる入力はタッチやマウス入力と同じように動作します。スタイラスが digitizer に接触したとき、アプリは指が画面をタッチしたときと同じようなタッチイベントを受け取ります。スタイラスが digitizer 上に浮いているとき、アプリはマウスポインターがボタンを押さずに画面上を動くのと同じようなホバーイベントを受け取ります。

getToolType() を使って MotionEvent のポインタに対して "tool type" を取得して、入力が指なのか、マウスなのか、スタイラスなのか、イレイサーなのか区別することができます。現在定義されている tool type は : TOOL_TYPE_UNKNOWN, TOOL_TYPE_FINGER, TOOL_TYPE_MOUSE, TOOL_TYPE_STYLUS, TOOL_TYPE_ERASER です。tool type を見ることで指やマウスでの入力と異なる処理をスタイラス入力で行うようにできます。

getButtonState() を使って MotionEvent の "button state" を調べることで、マウスやスタイラスのボタンが押されているかどうかわかります。現在定義されている button state は : BUTTON_PRIMARY, BUTTON_SECONDARY, BUTTON_TERTIARY, BUTTON_BACK, BUTTON_FORWARD です。利便性のために、マウスのバックとフォーワードボタンは自動的に KEYCODE_BACKKEYCODE_FORWARD キーに割り振られます。マウスボタンのバックとフォーワード操作をサポートするためにこれらのキーを利用できます。

加えて、接触の位置と圧力を正確をはかるために、いくつかのスタイラス入力デバイスはスタイラスチップと digitizer の間の距離、スタイラスの傾き角度、スタイラスの回転角度を報告します。AXIS_DISTANCE, AXIS_TILT, AXIS_ORIENTATION getAxisValue() を使ってこの情報を取得できます。

tool type, button state, 新しい axis code のデモとして ApiDemos の TouchPaint を見てください。

■ Properties

新しい Property クラスは、一般的にオブジェクトに対して set/get で呼ばれる値であるプロパティを速く、効果的に、簡単に指定する方法を提供します。また、フィールドやメソッド周りのリファレンスを渡すことを可能にし、フィールドやメソッドの詳細を知らなくてもプロパティの値を set/get するコードが可能になります。

例えば、オブジェクト foo のフィールド bar に値をセットする場合、これまではこうしていました : foo.bar = value; private field bar に対する setter を呼ぶときは、これまではこうしていました : foo.setBar(value); しかし、foo インスタンスの周りに渡したり、bar value をセットする他のコードがある場合、Android 4.0 以前では行う方法がまったくありませんでした。

Property クラスを使うと、Foo クラスに Property オブジェクト BAR を宣言して、Foo クラスの foo インスタンスのフィールドをこのようにセットできます : BAR.set(foo, value); View クラスでは、この Property クラスを利用して、Android 3.0 で追加された変形プロパティ (ROTATION, ROTATION_X, TRANSLATION_X など) など、いくつかのフィールドを設定できるようになりました。

ObjectAnimator クラスも Property クラスを使うようになりました。そのため、Property を使ってより速く、効率的に、文字列ベースのアプローチよりもより型安全に ObjectAnimator を作成できます。

■ Hardware Acceleration

Android 4.0 から、targetSdkVersion もしくは minSdkVersion"14" 以上がセットされているアプリでは、デフォルトで hardware acceleration が全ての window で有効になっています。hardware acceleration の結果として、一般的にはアニメーションがスムーズになったり、スクロールがスムーズになったり、ユーザー操作へのレスポンスとパフォーマンスが全体的によくなったりします。

必要であれば手動で hardware acceleration を無効にすることができます。それには個々の <activity> エレメントもしくは <application> エレメントの hardwareAccelerated 属性に false を設定します。また、個々の view で setLayerType(LAYER_TYPE_SOFTWARE) を呼ぶことでも hardware acceleration を無効にすることができます。

サポートしていない描画操作のリストなどのを含む、hardware acceleration のより詳しい情報は Hardware Acceleration ドキュメントを見てください。

■ JNI Changes

以前の Android バージョンでは、JNI local reference は間接的に扱うことができず、Android は直接ポインタを使っていました。これはガーベージコレクターがオブジェクトを動かさなかった間は問題ありませんでしたが、一見動くように見える状態はバグを含むコードを書いてしまう可能性があります。Android 4.0 では、システムはこれらのバグを検出するために間接的な reference を使うようになりました。

JNI local reference の入力と出力は JNI Tips の "Local and Global References" に記述されています。Android 4.0 では、これらのエラーを検出するために CheckJNI が拡張されています。Android Developer Blog を見て、一般的な JNI references のエラーとそれをどのように直すかのポストがくるのをチェックしてください。

JNI 実装のこの変更は targetSdkVersionminSdkVersion"14"以上の Android 4.0 をターゲットとするアプリにのみ影響します。これらの属性にこれより小さい値をセットした場合は、JNI local reference は以前のバージョンと同じように動作します。

■ WebKit

  • WebKit が version 534.30 にアップデートされました。
  • Indic fonts (Devanagari, Bengali, and Tamil, including the complex character support needed for combining glyphs) を WebView とビルトインのブラウザでサポート
  • Ethiopic, Georgian, and Armenian fonts を WebView とビルトインのブラウザでサポート
  • WebView を使うアプリのテストを楽にする WebDriver をサポート

Android Browser

ブラウザアプリに web アプリをサポートするための以下の機能が追加されました。

■ Permissions

以下は新しいパーミッションです。

■ Device Features

以下はデバイスの新しい機能です。
  • FEATURE_WIFI_DIRECT : アプリが Wi-Fi の peer-to-peer 通信を使うことを宣言します。




2011年10月6日木曜日

Android SimpleCursorTreeAdapter を使う

1段のリストを作る ListView 用の Adapter として、 Object配列やリストをひもづける SimpleAdapter や データベースを検索して得られる Cursor をひもづける SimpleCursorAdapter などがあります。

同じように、2段のリストを作る ExpandableListView 用の Adapter として、Object の配列やリストをひもづける SimpleExpandableListAdapter というのがあります。 実は、Cursor データを ExpandableListView にひもづけるための実装クラスの Adapter はありません。変わりに抽象クラスの SimpleCursorTreeAdapter というものがあります。

# SimpleExpandableListAdapter の使い方は API DemosExpandableList3.java がわかりやすいです。

このクラスを継承した実装クラスを作って利用します。

public class SimpleCursorTreeAdapterSampleActivity extends ExpandableListActivity { private static final String[] CONTACTS_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME }; private static final int GROUP_ID_COLUMN_INDEX = 0; private static final String[] PHONE_PROJECTION = new String[] { Phone._ID, Phone.TYPE, Phone.NUMBER }; public class ExpandableListAdapter extends SimpleCursorTreeAdapter { public ExpandableListAdapter(Context context, Cursor c, int groupLayout, String[] groupFrom, int[] groupTo, int childLayout, String[] childrenFrom,int[] childrenTo) { super(context, c, groupLayout, groupFrom, groupTo, childLayout, childrenFrom, childrenTo); } @Override protected Cursor getChildrenCursor(Cursor groupCursor) { Uri.Builder builder = Contacts.CONTENT_URI.buildUpon(); ContentUris.appendId(builder, groupCursor.getLong(GROUP_ID_COLUMN_INDEX)); builder.appendEncodedPath(Contacts.Data.CONTENT_DIRECTORY); Uri phoneNumbersUri = builder.build(); Cursor c = getContentResolver().query(phoneNumbersUri, PHONE_PROJECTION, Phone.MIMETYPE + "=?", new String[] { Phone.CONTENT_ITEM_TYPE}, null); startManagingCursor(c); return c; } } private SimpleCursorTreeAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Cursor c = getContentResolver().query(Contacts.CONTENT_URI, CONTACTS_PROJECTION, Contacts.HAS_PHONE_NUMBER + "=1", null, null); mAdapter = new ExpandableListAdapter( this, c, android.R.layout.simple_expandable_list_item_1, new String[] { Contacts.DISPLAY_NAME }, new int[] { android.R.id.text1 }, android.R.layout.simple_expandable_list_item_1, new String[] { Phone.NUMBER }, new int[] { android.R.id.text1 }); startManagingCursor(c); setListAdapter(mAdapter); } }


SimpleCursorTreeAdapter にも SimpleCursorAdapter 同様 ViewBinder を設定して表示方法を変更することができます。

public class SimpleCursorTreeAdapterSampleActivity extends ExpandableListActivity { private static final String[] CONTACTS_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME }; private static final int GROUP_ID_COLUMN_INDEX = 0; private static final String[] PHONE_PROJECTION = new String[] { Phone._ID, Phone.TYPE, Phone.NUMBER }; private static final int[] COLOR_LIST = new int[] { Color.parseColor("#002A42"), Color.parseColor("#3DC3EA"), Color.parseColor("#99417B"), Color.parseColor("#F2AE30"), Color.parseColor("#F2D338"), }; public class ExpandableListAdapter extends SimpleCursorTreeAdapter { public ExpandableListAdapter(Context context, Cursor c, int groupLayout, String[] groupFrom, int[] groupTo, int childLayout, String[] childrenFrom,int[] childrenTo) { super(context, c, groupLayout, groupFrom, groupTo, childLayout, childrenFrom, childrenTo); } @Override protected Cursor getChildrenCursor(Cursor groupCursor) { Uri.Builder builder = Contacts.CONTENT_URI.buildUpon(); ContentUris.appendId(builder, groupCursor.getLong(GROUP_ID_COLUMN_INDEX)); builder.appendEncodedPath(Contacts.Data.CONTENT_DIRECTORY); Uri phoneNumbersUri = builder.build(); Cursor c = getContentResolver().query(phoneNumbersUri, PHONE_PROJECTION, Phone.MIMETYPE + "=?", new String[] { Phone.CONTENT_ITEM_TYPE}, null); startManagingCursor(c); return c; } } private SimpleCursorTreeAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Cursor c = getContentResolver().query(Contacts.CONTENT_URI, CONTACTS_PROJECTION, Contacts.HAS_PHONE_NUMBER + "=1", null, null); mAdapter = new ExpandableListAdapter( this, c, android.R.layout.simple_expandable_list_item_1, new String[] { Contacts.DISPLAY_NAME }, new int[] { android.R.id.text1 }, android.R.layout.simple_expandable_list_item_2, new String[] { Phone.TYPE, Phone.NUMBER }, new int[] { android.R.id.text1, android.R.id.text2 }); mAdapter.setViewBinder(new SimpleCursorTreeAdapter.ViewBinder() { public boolean setViewValue(View view, Cursor cursor, int columnIndex) { if (cursor.getColumnName(columnIndex).equals(Phone.TYPE)) { int type = cursor.getInt(columnIndex); String text = (String) Phone.getTypeLabel(getResources(), type, null); ((TextView) view).setText(text); ((TextView) view).setTextColor(COLOR_LIST[type % 5]); return true; } return false; } }); startManagingCursor(c); setListAdapter(mAdapter); } }


Y.A.M の 雑記帳: Android AsyncQueryHandler を使う - でも触れたように、UI スレッドで Cursor を取得するためのクエリを走らせると UI スレッドをブロックしていまいます。それを避けるために SimpleCursorTreeAdapter に対しても AsyncQueryHandler を使うようにすることができます。

public class AsyncQuerySimpleCursorTreeAdapterSampleActivity extends ExpandableListActivity { private static final String[] CONTACTS_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME }; private static final int GROUP_ID_COLUMN_INDEX = 0; private static final String[] PHONE_PROJECTION = new String[] { Phone._ID, Phone.TYPE, Phone.NUMBER }; private static final int[] COLOR_LIST = new int[] { Color.parseColor("#002A42"), Color.parseColor("#3DC3EA"), Color.parseColor("#99417B"), Color.parseColor("#F2AE30"), Color.parseColor("#F2D338"), }; private static final int TOKEN_GROUP = 0; private static final int TOKEN_CHILD = 1; private static final class QueryHandler extends AsyncQueryHandler { private CursorTreeAdapter mAdapter; public QueryHandler(Context context, CursorTreeAdapter adapter) { super(context.getContentResolver()); this.mAdapter = adapter; } @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { switch (token) { case TOKEN_GROUP: mAdapter.setGroupCursor(cursor); break; case TOKEN_CHILD: int groupPosition = (Integer) cookie; mAdapter.setChildrenCursor(groupPosition, cursor); break; } } } public class ExpandableListAdapter extends SimpleCursorTreeAdapter { public ExpandableListAdapter(Context context, int groupLayout, int childLayout, String[] groupFrom, int[] groupTo, String[] childrenFrom,int[] childrenTo) { super(context, null, groupLayout, groupFrom, groupTo, childLayout, childrenFrom, childrenTo); } @Override protected Cursor getChildrenCursor(Cursor groupCursor) { Uri.Builder builder = Contacts.CONTENT_URI.buildUpon(); ContentUris.appendId(builder, groupCursor.getLong(GROUP_ID_COLUMN_INDEX)); builder.appendEncodedPath(Contacts.Data.CONTENT_DIRECTORY); Uri phoneNumbersUri = builder.build(); mQueryHandler.startQuery(TOKEN_CHILD, groupCursor.getPosition(), phoneNumbersUri, PHONE_PROJECTION, Phone.MIMETYPE + "=?", new String[] { Phone.CONTENT_ITEM_TYPE }, null); return null; } } private QueryHandler mQueryHandler; private SimpleCursorTreeAdapter mAdapter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mAdapter = new ExpandableListAdapter( this, android.R.layout.simple_expandable_list_item_1, android.R.layout.simple_expandable_list_item_2, new String[] { Contacts.DISPLAY_NAME }, new int[] { android.R.id.text1 }, new String[] { Phone.TYPE, Phone.NUMBER }, new int[] { android.R.id.text1, android.R.id.text2 }); mAdapter.setViewBinder(new SimpleCursorTreeAdapter.ViewBinder() { public boolean setViewValue(View view, Cursor cursor, int columnIndex) { if (cursor.getColumnName(columnIndex).equals(Phone.TYPE)) { int type = cursor.getInt(columnIndex); String text = (String) Phone.getTypeLabel(getResources(), type, null); ((TextView) view).setText(text); ((TextView) view).setTextColor(COLOR_LIST[type % 5]); return true; } return false; } }); setListAdapter(mAdapter); mQueryHandler = new QueryHandler(this, mAdapter); mQueryHandler.startQuery(TOKEN_GROUP, null, Contacts.CONTENT_URI, CONTACTS_PROJECTION, Contacts.HAS_PHONE_NUMBER + "=1", null, null); } @Override protected void onDestroy() { super.onDestroy(); mAdapter.changeCursor(null); mAdapter = null; } }