OEM 向け統合ガイド

この記事では、VHAL でロータリー入力を処理する方法、ロータリー サービスを含めるようにビルドを設定する方法、すべてのアプリでロータリー エクスペリエンスをカスタマイズする方法について説明します。OEM 提供のランチャーなど、プリインストールされた OEM アプリについては、Car UI ライブラリ(car-ui-library)をご覧ください。

VHAL

ロータリー コントローラでは次のアクションがサポートされています。

  • 上下左右に移動する。
  • 時計回り / 反時計回りに回転する。
  • 中央ボタンを押す。
  • 戻るボタンを押す。
  • ホームボタンを押す。
  • その他のボタン(電話やメディアなど)を押す。

システム プロパティと、対応する int32Values のドキュメントについては、hardware/interfaces/automotive/vehicle/2.0/types.hal をご覧ください。

VHAL は以下のアクションを処理します。

移動

ユーザーがロータリー コントローラを右に押すと、VHAL は次の int32Values を指定して HW_KEY_INPUT プロパティを使用し、Android にイベントを送信します。

  1. ACTION_DOWN
  2. KEYCODE_SYSTEM_NAVIGATION_RIGHT
  3. ターゲット ディスプレイ。

ユーザーがロータリー コントローラを離すと、VHAL は ACTION_UP を指定して同じプロパティとキーコードを使用します。別の方向への移動では、対応するキーコードを使用します。

対角線のキーコードはありませんが、ハードウェアが対角線をサポートしている場合、VHAL は水平イベントと垂直イベントを組み合わせて対角線を生成できます。たとえば、左と上に移動した場合は次のようになります。

  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_DOWN
  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_DOWN

いずれかの順番で(続けて)ロータリー コントローラを離すと、次のようになります。

  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_UP
  • HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_UP

ユーザーはロータリー コントローラを離す前に垂直方向に押すことがあります。 たとえば、次のシナリオをご覧ください。

垂直方向
図 1. 垂直方向

これにより、次のイベント シーケンスが生成されます。

  1. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_DOWN
  2. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_DOWN
  3. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_LEFT ACTION_UP
  4. HW_KEY_INPUT KEYCODE_SYSTEM_NAVIGATION_UP ACTION_UP

ロータリー コントローラが 1 つの方向に押されているときに、繰り返しイベントが生成されないようにしてください。

回転

ユーザーがロータリー コントローラを時計回りに 1 デテント(クリック)回転すると、VHAL は次の int32Values を指定して HW_ROTARY_INPUT プロパティを使用し、イベントを Android に送信します。

  1. ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION
  2. 1 デテント。
  3. ターゲット ディスプレイ。

イベントのタイムスタンプは、ナノ秒単位の経過時間に設定する必要があります。

反時計回りの回転を 1 デテント行うと、同じイベントが生成されますが、デテントの数は -1 になります。

同方向への回転で複数のデテントが連続して発生する場合、VHAL はシステムに過剰な負荷がかからないように、各デテントを 1 つのイベントに結合します。この場合、イベントのタイムスタンプは、回転のデテントが最初に発生したときの時刻になります。 int32Values には、連続する回転のデテント間のナノ秒数を含める必要があります。

たとえば、次の一連の回転があるとします。

  • t0 の時刻に、ユーザーは反時計回りに 1 デテント回転しました。
  • t0 + 5 ns の時刻に、ユーザーは反時計回りに 1 デテント回転しました。
  • t0 + 8 ns の時刻に、ユーザーは反時計回りに 1 デテント回転しました。

上記の回転により、次のイベントが生成されます。

  • プロパティ: HW_ROTARY_INPUT
  • タイムスタンプ: t0
  • int32Values:
    1. ROTARY_INPUT_TYPE_SYSTEM_NAVIGATION
    2. -3(反時計回りに 3 デテント)
    3. ターゲット ディスプレイ。
    4. 1 回目と 2 回目のデテントの間の 5 ns。
    5. 2 回目と 3 回目のデテントの間の 3 ns。

中央ボタン

ユーザーが中央ボタンを押すと、VHAL は次の int32Values を指定して HW_KEY_INPUT プロパティを使用し、Android にイベントを送信します。

  1. ACTION_DOWN
  2. KEYCODE_DPAD_CENTER
  3. ターゲット ディスプレイ。

ユーザーがロータリー コントローラを離すと、VHAL は ACTION_UP を指定して同じプロパティとキーコードを使用します。

中央ボタンが押されているときに繰り返しイベントを生成しないでください。

戻るボタン

ユーザーが戻るボタンを押すと、VHAL は次の int32Values を指定して HW_KEY_INPUT プロパティを使用し、Android にイベントを送信します。

  1. ACTION_DOWN
  2. KEYCODE_BACK
  3. ターゲット ディスプレイ。

ユーザーがロータリー コントローラを離すと、VHAL は ACTION_UP を指定して同じプロパティとキーコードを使用します。

中央ボタンが押されているときに繰り返しイベントが生成されないようにしてください。

ホームボタン

ホームボタンは戻るボタンと同じように処理しますが、KEYCODE_BACK ではなく KEYCODE_HOME を使用します。

その他のボタン

ロータリー コントローラにその他のボタンが含まれている場合、Android の観点からはロータリーの一部とは見なされないため、OEM は VHAL によってそれらのボタンをどのようにでも処理できます。これらは通常、戻るボタンやホームボタンと同様に処理されますが、異なるキーコード(KEYCODE_CALLKEYCODE_MUSIC など)が使用されます。

ビルド構成

ロータリー ナビゲーションは、RotaryService というユーザー補助サービスによって提供されます。 デバイスのシステム イメージにこのサービスを含めるには、makefile に次の行を追加します。

PRODUCT_PACKAGES += CarRotaryController

デバッグビルドに次のパッケージを含めることもできます。

ロータリー サービスは、デバイスの起動時とユーザーの切り替え時に自動的に有効になります。これにより、ユーザーはセットアップ中にロータリー コントローラを使用できるようになります。

ロータリー コントローラの有無にかかわらず、同じビルドを自動車に使用する場合は、上記のように CarRotaryController を追加して、必要なコードがビルドに含まれるようにします。ロータリーのない自動車でロータリー サービスが有効化されないようにするには、静的 RRO を作成して、packages/services/Car/servicerotaryService 文字列リソースを空の文字列でオーバーレイします。ロータリー デバイスと非ロータリー デバイスでは同じビルドを使用しますが、プロダクト構成が異なります。オーバーレイが含まれているのは後者のみです。

カスタマイズ

OEM は、次の場所にあるリソース オーバーレイを使用して、フォーカス検出ロジック、フォーカス ハイライト、その他のアイテムをカスタマイズできます。

  • car-ui-library は packages/apps/Car/libs/car-ui-lib にあります。
  • RotaryServicepackages/apps/Car/RotaryController にあります。
  • Coreframeworks/base/core にあります。

移動履歴

OEM は、2 種類の移動履歴をそれぞれ有効にするかどうかと、有効にした場合のキャッシュ サイズと有効期限ポリシーを構成できます。これはすべて、各種の car-ui-library リソースをオーバーライドすることで行います。

フォーカス履歴のキャッシュ

(Android 11 QPR3、Android 11 Car、Android 12
この FocusArea 単位のキャッシュには、FocusArea に戻るときにフォーカスできるように、FocusArea 内で最後にフォーカスされたビューが保存されます。このキャッシュは、次の car-ui-library リソースをオーバーレイすることで構成できます。

  • car_ui_focus_history_cache_type:
    1. キャッシュが無効になっています。
    2. しばらくするとキャッシュが期限切れになります(以下を参照)。
    3. キャッシュに有効期限はありません。
  • car_ui_focus_history_expiration_period_ms: キャッシュ タイプが 2 に設定されている場合に、キャッシュが期限切れになるまでのミリ秒数(上記参照)。

FocusArea の履歴のキャッシュ

(Android 11 QPR3、Android 11 Car、Android 12
このキャッシュには、反対方向に移動したときに同じ FocusArea にフォーカスを戻せるように、移動の履歴が保存されています。このキャッシュは、次の car-ui-library リソースをオーバーレイすることで構成できます。

  • car_ui_focus_area_history_cache_type:
    1. キャッシュが無効になっています。
    2. しばらくするとキャッシュが期限切れになります(以下を参照)。
    3. キャッシュに有効期限はありません。
  • car_ui_focus_area_history_expiration_period_ms: キャッシュ タイプが 2 に設定されている場合に、キャッシュが期限切れになるまでのミリ秒数(上記参照)。
  • car_ui_clear_focus_area_history_when_rotating: ユーザーがコントローラを回転させたときにキャッシュを無効にするかどうか。

回転

(Android 11 QPR3、Android 11 Car、Android 12
OEM は、RotaryService 内の 2 つの整数リソースをオーバーライドして、アクセラレーションの有無を指定できます。これには、次のような回転時のマウス アクセラレーションなどがあります。

  • rotation_acceleration_3x_ms: Google が回転のデテントでコントローラの回転を加速するかどうかを決定するために使用される時間間隔(ミリ秒単位)。このデテントと前の回転デテントの間の間隔がこの値より小さい場合は、3 回の回転デテントとして扱われます。3 倍のアクセラレーションを無効にするには、この値を 2147483647 に設定します。
  • rotation_acceleration_2x_ms: rotation_acceleration_3x_ms に似ており、2 倍のアクセラレーションに使用されます。2 倍のアクセラレーションを無効にするには、これを 2147483647 に設定します。

アクセラレーションは、VHAL で要求されるとおり、回転の各デテントに個別のタイムスタンプがある場合に最適に動作します。タイムスタンプを使用できない場合、RotaryService は、回転のデテントが等間隔であると想定します。

/**
     * Property to feed H/W rotary events to android
     *
     * int32Values[0] : RotaryInputType identifying which rotary knob rotated
     * int32Values[1] : number of detents (clicks), positive for clockwise,
     *                  negative for counterclockwise
     * int32Values[2] : target display defined in VehicleDisplay. Events not
     *                  tied to specific display must be sent to
     *                  VehicleDisplay#MAIN.
     * int32values[3 .. 3 + abs(number of detents) - 2]:
     *                  nanosecond deltas between pairs of consecutive detents,
     *                  if the number of detents is > 1 or < -1
     *
     * VehiclePropValue.timestamp: when the rotation occurred. If the number of
     *                             detents is > 1 or < -1, this is when the
     *                             first detent of rotation occurred.
     *
     * @change_mode VehiclePropertyChangeMode:ON_CHANGE
     * @data_enum RotaryInputType
     * @access VehiclePropertyAccess:READ
     */
    HW_ROTARY_INPUT = (
        0x0A20
        | VehiclePropertyGroup:SYSTEM
        | VehiclePropertyType:INT32_VEC
        | VehicleArea:GLOBAL),

フォーカス ハイライト

OEM は、Android フレームワークのデフォルトのフォーカス ハイライトと、car-ui-library のいくつかのフォーカス ハイライト リソースをオーバーライドできます。

デフォルトのフォーカス ハイライト

Android フレームワークでは、属性 selectableItemBackground を通じてデフォルトのフォーカス ハイライトが提供されます。Theme.DeviceDefault では、この属性は Coreitem_background.xml を参照します。OEM は、item_background.xml をオーバーレイして、デフォルトのフォーカス ハイライト ドローアブルを変更できます。

通常、このドローアブルは StateListDrawable で、これにより android:state_focusedandroid:state_pressed など、さまざまな状態の組み合わせに基づいて背景を調整します。ユーザーがロータリー コントローラを使用してビューにフォーカスを設定すると、android:state_focusedtrue になりますが、android:state_pressedfalse になります。その後ユーザーがロータリー コントローラの中央ボタンを押すと、ボタンを押している間は android:state_focusedandroid:state_pressed の両方が true になります。ユーザーがボタンを離すと、android:state_focused のみが true のままになります。

car-ui-library は、Theme.DeviceDefault から派生したテーマを使用しています。そのため、このオーバーレイは、このライブラリを使用するアプリや、Theme.DeviceDefault から派生したテーマを使用するアプリに影響します。無関係なテーマ(Theme.Material など)を使用するアプリには影響しません。

car-ui-library のフォーカス ハイライト リソース

OEM は、複数の car-ui-library リソースをオーバーライドして、長方形以外(丸やピル型など)のフォーカス ハイライトがあるビューと、Theme.DeviceDefault から派生していないテーマを使用するアプリで、フォーカス ハイライトをどのように表示するかを制御できます。これらのリソースは、フォーカス ハイライトがデフォルトのフォーカス ハイライト ドローアブルと一致するようにオーバーレイする必要があります。

(Android 11 QPR3、Android 11 Car、Android 12)。
次のリソースは、ビューがフォーカスされているが、押されていない状態を示すために使用されます。

  • car_ui_rotary_focus_fill_color: 塗りつぶしの色。
  • car_ui_rotary_focus_stroke_color: 枠線の色。
  • car_ui_rotary_focus_stroke_width: 枠線の太さ。

(Android 11 QPR3、Android 11 Car、Android 12
次のリソースは、ビューがフォーカスされ、かつ押されている状態を示すために使用されます。

  • car_ui_rotary_focus_pressed_fill_color: 塗りつぶしの色。
  • car_ui_rotary_focus_pressed_stroke_color: 枠線の色。
  • car_ui_rotary_focus_pressed_stroke_width: 枠線の太さ。

次の例に示すように、ユーザーの注意を引くためにボタンに無地の背景色が使用されることがあります。これにより、フォーカス ハイライトが見づらくなります。

背景が無地のボタン
図 2. 背景が無地のボタン

その場合、デベロッパーはセカンダリ カラーを使用してカスタム フォーカス ハイライトを指定できます。
  • (Android 11 QPR3、Android 11 Car、Android 12
    car_ui_rotary_focus_fill_secondary_color
    car_ui_rotary_focus_stroke_secondary_color
  • (Android 12
    car_ui_rotary_focus_pressed_fill_secondary_color
    car_ui_rotary_focus_pressed_stroke_secondary_color

たとえば、塗りつぶしのみ、または枠線のみが必要な場合は、任意の色を透明にしたり、いずれかのサイズをゼロにしたりできます。

FocusArea のハイライト

(Android 11 QPR3、Android 11 Car、Android 12
FocusArea は、子孫の 1 つがフォーカスされた場合に 2 種類のハイライトを描画できます。必要に応じて両方を組み合わせて使用することも可能です。この機能は、AOSP ではデフォルトで無効になっていますが、car-ui-library リソースをオーバーライドすることで有効にできます。

  • car_ui_enable_focus_area_foreground_highlight: FocusArea とその子孫の上にハイライトを描画します。AOSP では、このドローアブルは FocusArea を囲む枠線です。OEM は、car_ui_focus_area_foreground_highlight ドローアブルをオーバーライドできます。
  • car_ui_enable_focus_area_background_highlight: FocusArea の上、子孫の背後にハイライトを描画します。AOSP では、このドローアブルは単色塗りつぶしです。 OEM は、car_ui_focus_area_background_highlight ドローアブルをオーバーライドできます。

インプット メソッド エディタ

インプット メソッド エディタ(IME)とは、画面キーボードなどの入力方法のことです。

(Android 11 QPR3、Android 11 Car、Android 12
OEM は、RotaryService 内の default_touch_input_method 文字列リソースをオーバーレイすることで、タッチベースの IME の ComponentName を指定する必要があります。たとえば、OEM が Android Automotive で提供される IME を使用する場合は、com.google.android.apps.automotive.inputmethod/.InputMethodService を指定する必要があります。

(Android 11 QPR3、Android 11 Car、Android 12)。
OEM がロータリー専用の IME を作成した場合は、rotary_input_method リソースで ComponentName を指定する必要があります。このリソースがオーバーレイされている場合、ユーザーがロータリー コントローラの移動、回転、中央ボタンでヘッドユニットを操作するたびに、指定された IME が使用されます。ユーザーが画面に触れると、以前の IME が使用されます。戻るボタン(およびロータリー コントローラのその他のボタン)が IME の選択に影響することはありません。このリソースがオーバーレイされていない場合、IME の切り替えは行われません。Carboard はロータリー コントローラをサポートしていないため、OEM がロータリー IME を提供していない場合、ユーザーはロータリー コントローラからテキストを入力できません。

RotaryIME はデモのロータリー IME です。基本的な機能しか備えていないものの、上記のような IME の自動的な切り替えを試すには十分です。RotaryIME のソースコードは packages/apps/Car/tests/RotaryIME/ にあります。

画面外への移動

デフォルトでは、ユーザーが画面外に移動しようとしても何も起こりません。OEM は、以下の組み合わせを指定して、4 つの方向それぞれに対する処理を設定できます。

  1. AccessibilityService で定義されたグローバル アクション(GLOBAL_ACTION_BACK など)。
  2. キーコード(KEYCODE_BACK など)。
  3. URL で表されるアクティビティを起動するインテント。

(Android 11 QPR3、Android 11 Car、Android 12
これらは、RotaryService で次の配列リソースをオーバーレイすることで指定します。

  • off_screen_nudge_global_actions: ユーザーが画面外に向けて上下左右に移動するときに実行するグローバル アクションの配列。この配列の関連要素が -1 の場合、グローバル アクションは行われません。
  • off_screen_nudge_key_codes: ユーザーが画面外に向けて上下左右に移動するときに挿入するクリック イベントのキーコードの配列。この配列の関連要素が 0(KEYCODE_UNKNOWN)の場合、イベントは挿入されません。
  • off_screen_nudge_intents: ユーザーが画面外に向けて上下左右に移動するときにアクティビティを起動するインテントの配列。この配列の関連要素が空の場合、アクティビティは起動されません。

その他の構成

次の RotaryService リソースをオーバーレイする必要があります。

  • (Android 11 QPR3、Android 11 Car、Android 12
    config_showHeadsUpNotificationOnBottom: ヘッドアップ通知を上ではなく下に表示するかどうかを表すブール値。この値は、frameworks/base/packages/CarSystemUI/res/values/config.xmlconfig_showHeadsUpNotificationOnBottom ブール値リソースと同じにする必要があります。
  • (Android 11 QPR3、Android 11 車、Android 12
    notification_headsup_card_margin_horizontal: ヘッドアップ通知ウィンドウの左右のマージン。この値は、packages/apps/Car/Notification/res/values/dimens.xmlnotification_headsup_card_margin_horizontal サイズリソースと同じ値にする必要があります。
  • (Android 12
    excluded_application_overlay_window_titles: オーバーレイ ウィンドウと見なさないウィンドウのタイトルの配列。これには、TaskViews または TaskDisplayAreas を表すアプリ ウィンドウのタイトルを含める必要があります。デフォルトでは、このリストには「マップ」のみが含まれます。

次の RotaryService リソースをオーバーレイできます。

  • (Android 11 QPR3、Android 11 Car、Android 12
    long_press_ms: 中央ボタンを何ミリ秒押し続けると長押しがトリガーされるかを表す整数値。ゼロは、システムのデフォルトの長押しタイムアウトが使用されることを示し、これがデフォルト値となります。