Android XR の基礎を学ぶ: パート 1 - モードと空間パネル

1. 始める前に

学習内容

  • XR フォーム ファクタによる、独自のユーザー エクスペリエンス。
  • Jetpack Compose XR ライブラリで提供されるコンポーザブルを利用して、アプリを最大限に生かしながら Android XR ヘッドセットで動作させる方法の基礎。
  • Compose XR ライブラリで提供される UI 要素の使い方。
  • Android XR 用アプリの作成に関する情報の入手場所。

対象外:

必要なもの

作成するアプリの概要

この Codelab では、基本的な単一画面のアプリを、Android XR を利用して没入感のあるユーザー エクスペリエンスを提供するアプリに変身させます。

スターター コード

最終結果

2. 設定する

コードを取得する

  1. この Codelab のコードは、xr-codelabs GitHub リポジトリ内の xr-fundamentals ディレクトリにあります。次のコマンドを実行して、リポジトリのクローンを作成してください。
git clone https://github.com/android/xr-codelabs.git
  1. または、リポジトリを ZIP ファイルとしてダウンロードすることもできます。

プロジェクトを開く

  • Android Studio を起動し、xr-fundamentals/start ディレクトリのみを選択してプロジェクトをインポートします。xr-fundamentals/part1 ディレクトリにはソリューション コードが含まれています。不明な点がある場合や、プロジェクト全体を確認したいときは、いつでも参照できます。

コードを理解する

  • Android Studio でプロジェクトを開き、初期状態のコードを確認します。

3. XR のコンセプトを学ぶ: モードと空間パネル

この Codelab では、Android XR の 2 つのコンセプト、モードと空間パネルについて学びます。また、これらのコンセプトを Android XR デバイス上で動作するアプリに適用する方法についても学びます。

モード

Android XR デバイスでは、アプリは 2 つのモードのどちらかで動作します。ホームスペース モード、またはフルスペース モードです。

ホームスペース モード

d779257a53898d36.jpeg

ホームスペース モードでは、複数のアプリを並べて実行できるので、アプリ間でマルチタスクが可能です。Android アプリは、変更することなくホームスペース モードで動作させることができます。

フルスペース モード

c572cdee69669a23.jpeg

フルスペース モードでは、同時に動作するアプリは 1 つだけで、スペースに境界はありません。その他のすべてのアプリは非表示になります。アプリがフルスペース モードに移行し、そのモード特有の機能を利用できるようにするには、追加の作業が必要になります。

モードの詳細については​​ホームスペース モードとフルスペース モードをご覧ください。

空間パネル

空間パネルはコンテナ要素で、Android XR アプリの基本的な構成要素として機能します。

ホームスペース モードで動作しているときは、アプリは 1 枚のパネルに含められ、大きな画面を持つ Android デバイス上のデスクトップ ウィンドウのような操作性になります。

フルスペース モードで動作しているときは、アプリのコンテンツを 1 つ以上のパネルに表示して、より没入感のあるエクスペリエンスを提供できます。

パネルについての詳細は空間パネルをご覧ください。

4. Android XR エミュレータでアプリを実行する

Android XR 向けにアプリの拡張を始める前に、Android XR エミュレータでアプリを実行して、ホームスペース モードでアプリがどのように見えるか確認できます。

Android XR システム イメージをインストールする

  1. まず、Android Studio で SDK Manager を開き、まだ選択していない場合は [SDK Platforms] タブを選択します。SDK Manager ウィンドウの右下にある [Show package details] チェックボックスがオンになっていることを確認します。
  2. Android 14 セクションで、Android XR ARM 64 v8a または Android XR Intel x86_64 のエミュレータ イメージをインストールします。イメージは、同じアーキテクチャ(x86 / ARM)のマシンでのみ実行できます。

Android XR 仮想デバイス(AVD)を作成する

  1. Device Manager を開き、ウィンドウの左側にある [Category] 列で [XR] を選択します。次に、一覧から XR Device ハードウェア プロファイルを選択肢、[Next] をクリックします。

7a5f6b9c1766d837.png

  1. 次のページに移動したら、前の手順でインストールしたシステム イメージを選択します。[Next] をクリックし、必要に応じて詳細オプションを選択したら、[Finish] をクリックして AVD を作成します。
  2. 作成された AVD でアプリを実行します。

51a6b3eb02916d74.png

5. 依存関係を設定する

アプリに XR 用の機能を追加する前に、Jetpack Compose for XR ライブラリ androidx.xr.compose:compose の依存関係を追加する必要があります。このライブラリには、Android XR の特別なエクスペリエンスのアプリを構築するために必要なコンポーザブルが、すべて含まれています。

libs.version.toml

[versions]
...
xrCompose = "1.0.0-alpha04"

[libraries]
...
androidx-xr-compose = { group = "androidx.xr.compose", name = "compose", version.ref = "xrCompose" }

build.gradle.kts(:app モジュール)

dependencies {
    ...
    implementation(libs.androidx.xr.compose)
    ...
}

これらのファイルを更新した後、プロジェクトに依存関係をダウンロードするために、必ず Gradle の同期を実施してください。

6. フルスペース モードにする

パネルのような XR 機能を使用するには、アプリをフルスペース モードで実行する必要があります。アプリをフルスペース モードにするには、次の 2 つの方法があります。

  • アプリ内のユーザー インタラクションなど、プログラムを使用して移行する
  • アプリのマニフェストにディレクティブを追加して、起動時にすぐに移行する

プログラムを使用してフルスペース モードにする

プログラムを使用してフルスペース モードにするには、アプリの UI にアフォーダンスを用意して、ユーザーが使いたいモードを選べるようにします。また、アプリの使用状況に応じてフルスペース モードに移行することもできます。たとえば、動画コンテンツの視聴を始めるとフルスペース モードに移行し、再生が終わったら元のモードに戻ることができます。

まずは、上部のアプリバーにモードを切り替えるボタンを追加するのが簡単でしょう。

  1. com.example.android.xrfundamentals.ui.component パッケージに新しいファイル ToggleSpaceModeButton.kt を作成し、次のコンポーザブルを追加します。

ToggleSpaceModeButton.kt

package com.example.android.xrfundamentals.ui.component

import androidx.annotation.DrawableRes
import androidx.compose.material3.Icon
import androidx.compose.material3.FilledTonalIconButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.xr.compose.platform.LocalSpatialCapabilities
import androidx.xr.compose.platform.LocalSpatialConfiguration
import com.example.android.xrfundamentals.R
import com.example.android.xrfundamentals.ui.theme.XRFundamentalsTheme

@Composable
fun ToggleSpaceModeButton(modifier: Modifier = Modifier) {
    val spatialConfiguration = LocalSpatialConfiguration.current

    if (LocalSpatialCapabilities.current.isSpatialUiEnabled) {
        ToggleSpaceModeButton(
            modifier = modifier,
            contentDescription = "Request Home Space mode",
            iconResource = R.drawable.ic_home_space_mode,
            onClick = { spatialConfiguration.requestHomeSpaceMode() }
        )
    } else {
        ToggleSpaceModeButton(
            modifier = modifier,
            contentDescription = "Request Full Space mode",
            iconResource = R.drawable.ic_full_space_mode,
            onClick = { spatialConfiguration.requestFullSpaceMode() }
        )
    }
}

@Composable
fun ToggleSpaceModeButton(
    contentDescription: String,
    @DrawableRes iconResource: Int,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    FilledTonalIconButton(
        modifier = modifier,
        onClick = onClick
    ) {
        Icon(
            painterResource(iconResource),
            contentDescription
        )
    }
}
  1. アプリが XR デバイスで実行されているとき、ボタンを TopAppBar にアクションとして追加します。

XRFundamentalsTopAppBar.kt

import androidx.xr.compose.platform.LocalHasXrSpatialFeature

...

TopAppBar(
    ...,
    actions = {
        // Only show the mode toggle if the device supports spatial UI
        if (LocalHasXrSpatialFeature.current) {
            ToggleSpaceModeButton()
        }
    }
)

アプリを実行してみましょう。

アプリは起動時にホームスペース モードで動作します。パネル右上のボタンをタップすると、フルスペース モードに切り替わります。

フルスペース モードで動作しているアプリです。アプリを最小化したり閉じたりするシステム UI がなくなっていることに注目してください。パネル右上のボタンをタップすると、ホームスペース モードに戻ります。

注目すべき新しい API を、以下で簡単に説明します。

  • LocalSpatialConfiguration は、アプリの現在の空間設定にアクセスするためのコンポジション ローカルです。モード変更を要求するメソッドに加えて、アプリが属するボリュームのサイズなどに関する情報も含まれています。
  • LocalSpatialCapabilities は、アプリが現在どの空間機能を利用できるのか判断するために使用されるコンポジション ローカルです。モード(ホームスペースまたはフルスペース)に加えて、空間オーディオや 3D コンテンツのサポートなどの機能も含まれています。
  • LocalHasXrSpatialFeature は、アプリが、空間 UI 機能をサポートするデバイス上で動作しているかどうかを判断するために使用されるコンポジション ローカルです。内部的には、デバイスが android.software.xr.immersive システム機能を持っているかどうかをチェックしています。

起動時にフルスペース モードにする

アクティビティをフルスペース モードで開始するよう OS に指示するには、対応する <activity> 要素内に、次の属性を持った <property> 要素を含めます。この方法は、ユーザーが他のアプリも同時に利用する可能性が高い場合には推奨されません。

AndroidManifest.xml

<activity
    android:name=".MainActivity" 
    ... >
    <property
        android:name="android.window.PROPERTY_XR_ACTIVITY_START_MODE"
        android:value="XR_ACTIVITY_START_MODE_FULL_SPACE_MANAGED" />
</activity>

これで、アプリは起動時すぐにフルスペース モードになります。

5eec781e78280eda.gif

次に進む前に、前述の <property> 要素をマニフェストから削除してください。これによって、アプリはデフォルトの動作(ホームスペース モードで開く)に戻ります。

7. UI を複数のパネルに分ける

これでアプリのフルスペース モードの切り替えができるようになりました。次で、フルスペース モードをさらに活用する方法を説明します。良い活用方法の一つが、アプリのコンテンツを複数のパネルに分割して空間を覆うことです。これにより、ユーザーが必要に応じて、好きなようにパネルの移動やサイズ変更できるようになります。

アプリをサブスペースに組み込む

まず、XRFundamentalsApp コンポーザブル内にある Scaffold コンポーザブルの後に、Subspace コンポーザブルを追加します。サブスペースは、アプリ内の 3D 空間を区切ったパーティションです。これにより、3D レイアウトの構築(空間パネル追加など)、3D モデルの配置、2D コンテンツへの奥行き追加ができるようになります。

非 XR デバイスで動作するときは、Subspace コンポーザブルのコンテンツが、コンポジションに含まれることはありません。XR デバイスで動作する場合、アプリがフルスペース モードで動作しているときにだけ、コンテンツはコンポジションに含まれます。

XRFundamentalsApp.kt

import androidx.xr.compose.spatial.Subspace

...

HelloAndroidXRTheme {
    Scaffold(...)
    Subspace {
    }
}

アプリを実行してみましょう。

8969b8fd2a79a669.gif

アプリに Subspace コンポーザブルが含まれる場合、そのコンテンツのみが表示されます。このため、ボタンをクリックしてフルスペース モードにすると、何も表示されなくなります。次の数ステップで 2 つの空間パネル(プライマリ コンテンツを含むパネルとセカンダリ コンテンツを含むパネル)を追加し、この問題を改善しましょう。

プライマリ コンテンツのパネルを追加する

プライマリ コンテンツをフルスペース モードで表示するには、Subspace コンポーザブル内に SpatialPanel を追加します。

これはアプリのプライマリ パネルなので、この中に Scaffold を含めることで、上部のアプリバーにコントロールを表示されたままにできます。

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.SpatialPanel
import androidx.xr.compose.subspace.layout.SubspaceModifier
import androidx.xr.compose.subspace.layout.height
import androidx.xr.compose.subspace.layout.width

...

Subspace {
    SpatialPanel(
        modifier = SubspaceModifier
            .width(1024.dp)
            .height(800.dp)
    ) {
        Scaffold(
            topBar = { XRFundamentalsTopAppBar() }
        ) { innerPadding ->
            Box(Modifier.padding(innerPadding)) {
                PrimaryCard(
                    modifier = Modifier
                        .padding(16.dp)
                        .verticalScroll(rememberScrollState())
                )
            }
        }
    }
}

デフォルトでは、SpatialPanel のサイズは、その中に含まれるコンテンツのサイズに基づいて決定されますが、SubspaceModifier パラメータを指定することで決定することもできます。サブスペース修飾子は、修飾子と似ていて、パネルのような空間レイアウト コンポーネントを変更するために使用します。

この空間パネルのコンテンツには自身のサイズが指定されていないため、パネルに幅と高さを設定するには SubspaceModifier を指定する必要があります。

c3db4b5ec6f3b39e.gif

セカンダリ コンテンツのパネルを追加する

これで、フルスペース モードでアプリを実行することができ、パネルを使ってプライマリ コンテンツを表示できるようになりました。次は、セカンダリ コンテンツを専用のパネルに移動させましょう。空間パネル内で Surface を使用することに注意してください。そうしないと、空間パネル自体が透明になり、セカンダリ カードのバックグラウンドがなくなってしまいます(Scaffold コンポーザブルは、前述のステップでこれに対処しました)。

XRFundamentalsApp.kt

import androidx.compose.material3.Surface

...

Subspace {
    SpatialPanel() { ... }
    SpatialPanel(
        modifier = SubspaceModifier
            .width(340.dp)
            .height(800.dp)
    ) {
        Surface {
            SecondaryCardList(
                modifier = Modifier
                    .padding(16.dp)
                    .verticalScroll(rememberScrollState())
            )
        }
    }
}

アプリをもう一度実行します。一見すると、セカンダリ パネルが表示されていないと思うかもしれませんが、実際にはプライマリ パネルの裏側に隠れています。

34936c6d0d82adb8.gif

パネルを一列に並べる

2D コンテンツと同じように、行と列を使うことで、コンポーザブルを重ならないように横に並べることができます。パネルなどの空間コンポーネントでは、SpatialRowSpatialCurvedRowSpatialColumn などのコンポーザブルを使うことで、これを実現できます。

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.SpatialCurvedRow

...

Subspace {
    SpatialCurvedRow(
        curveRadius = 825.dp
    ) {
        SpatialPanel(...) { ... }
        SpatialPanel(...) { ... }
    }
}

アプリをもう一度実行すると、パネルが一列に並んでいるはずです。

SpatialCurvedRow コンポーザブルを使用しているため、パネルは 1 つの平面上ではなく、ユーザーの周りに湾曲した形で並べられ、取り囲まれたようなエクスペリエンスを提供しています(SpatialRow の場合とは異なります)。

2be1a054de7a8106.png

パネルのサイズを変更可能にする

サブスペース修飾子 resizable を使ってパネルのサイズを変更できるようにすることで、ユーザーがアプリの外観を制御できるようになります。

サイズ変更可能なパネルは、デフォルトでは制限なく拡大、縮小できてしまいます。このため、含まれるコンテンツに応じて、適切に minimumSize パラメータと maximumSize パラメータを設定した方が良いでしょう。

resizable 修飾子がサポートするすべてのパラメータの詳細については、リファレンス ドキュメントをご覧ください。

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.layout.resizable

...

SpatialPanel(
    modifier = SubspaceModifier
        ...
        .resizable(true)
)

466c1154c0e1fda3.gif

パネルを移動可能にする

同様に、Subspace 修飾子 movable を使って、パネルを移動可能にできます。

XRFundamentalsApp.kt

import androidx.xr.compose.subspace.layout.movable

...

SpatialPanel(
    modifier = SubspaceModifier
        ...
        .movable(true)
)

330b6a4d1a688a72.gif

movable 修飾子がサポートするすべてのパラメータの詳細については、リファレンス ドキュメントをご覧ください。

8. マニフェストを更新する

空間機能をアプリに追加したので、マニフェストを更新して、アプリがこれらの機能をサポートしていることを示す必要があります。これにより、Google Play で Android XR デバイス向けに差別化されたアプリとしてアプリがハイライト表示されます。

これを行うには、AndroidManifest.xml ファイルに次の <uses-feature> 要素を追加します。

AndroidManifest.xml

<application ...>
    ...
    <uses-feature android:name="android.software.xr.api.spatial" android:required="false"/>
    ...
</application>

android:required 属性に false の値を使用すると、アプリは利用可能な場合はその機能を使用しますが、その機能が搭載されていないデバイス(スマートフォンなど)にアプリを配信することを妨げることはありません。これにより、モバイル リリース トラックからスマートフォンと XR デバイスの両方に配信できるようになります。XR 専用のトラックを使用する場合は、代わりに android:requiredtrue に設定します。

9. 完了

Android XR を最大限に活用する方法についてさらに学ぶには、次の Codelab「Android XR の基礎を学ぶ: パート 2 - オービターと空間環境」と、次のリソースと演習をご覧ください。

参考資料

課題

  • サブスペース修飾子 resizablemovable で利用可能な、他のパラメータも使ってみましょう。
  • 他のパネルを追加してみましょう。
  • 空間ダイアログなどの、他の空間コンポーネントを使ってみましょう。

リファレンス ドキュメント