1. 始める前に
対象外:
- Android Auto および Android Automotive OS 向けのメディアアプリ(音楽、ラジオ、ポッドキャストなどのオーディオ アプリ)を作成する方法については説明していません。これらのアプリの作成方法について詳しくは、自動車向けメディアアプリを作成するをご覧ください。
必要なもの
- 最新の Android Studio。
- Kotlin の基本的な使用経験。
- Android 仮想デバイスを作成して Android Emulator で実行した経験。
- Jetpack Compose に関する基本的な知識。
- Android Automotive OS 用の駐車時向けアプリを作成してテストするの Codelab で説明した内容を含め、Android Automotive OS 用の駐車時向けアプリを作成する方法を理解していること。
作成するアプリの概要
この Codelab では、モバイルと Android Automotive OS デバイスの両方をサポートする既存のアプリである Road Reels に、運転中のオーディオのサポートを追加する方法について学びます。
作業開始時のバージョンは、ユーザー エクスペリエンスの制限が有効な間は再生を一時停止します。 | 完成したバージョンのアプリは、ユーザー エクスペリエンスの制限が有効な間も再生を続けます。 |
学習内容
- Android Automotive OS の動画アプリでオーディオのバックグラウンド再生を有効にする方法
2. 設定する
コードを取得する
- この Codelab のコードは、
car-codelabs
GitHub リポジトリ内のbuild-a-parked-app
ディレクトリにあります。クローンを作成するには、次のコマンドを実行します。
git clone https://github.com/android/car-codelabs.git
- または、リポジトリを ZIP ファイルとしてダウンロードすることもできます。
プロジェクトを開く
- Android Studio を起動し、
build-a-parked-app/end
ディレクトリのみを選択してプロジェクトをインポートします。build-a-parked-app/audio-while-driving
ディレクトリにはソリューション コードが含まれています。不明な点がある場合や、プロジェクト全体を確認したいときは、いつでも参照できます。
コードを理解する
- Android Studio でプロジェクトを開き、初期状態のコードを確認します。
3. Android Automotive OS 用の駐車時向けアプリについて理解する
駐車時向けアプリは、Android Automotive OS でサポートされているアプリカテゴリのサブセットです。執筆時点では、動画ストリーミング アプリ、ウェブブラウザ、ゲームからなります。電気自動車の普及が進んでおり、車の充電中にドライバーや同乗者がアプリを利用する機会が増えることを考えると、これらのアプリと Google 搭載のハードウェアを備えた自動車の相性は非常に良いと言えます。
自動車に搭載されている画面は、多くの点で他の大画面デバイス(タブレット、折りたたみ式デバイスなど)と似ています。同程度のサイズ、解像度、アスペクト比のタッチスクリーンが、縦向きまたは横向きで設置されています(画面の向きが固定されている点はタブレットと異なります)。また、ネットワーク接続が頻繁に切り替わるコネクテッド デバイス、という点も似ています。以上の点を考慮すると、大画面向けに最適化されているアプリであれば、最小限の追加作業で自動車向けの優れたユーザー エクスペリエンスを実現できそうです。
大画面アプリと同様、自動車用アプリにも品質レベルがあります。
- レベル 3 - 自動車対応: 大画面に対応しており、駐車時に使用できるアプリです。自動車向けに最適化された機能は搭載していませんが、他の大画面 Android デバイスと同じようにアプリを使用できます。これらの要件を満たすモバイルアプリは、自動車対応モバイルアプリ プログラムを通じて自動車に配信できます。
- レベル 2 - 自動車向けに最適化: 自動車のセンター コンソール ディスプレイで優れたエクスペリエンスを提供できるアプリです。このレベルの要件を満たすには、アプリのカテゴリに応じて自動車専用の変更を加え、運転モードや駐車モードで使用できる機能を含める必要があります。
- レベル 1 - 自動車向けに差別化: 自動車のさまざまなハードウェアで動作するように構築されており、運転モードと駐車モードでエクスペリエンスを調整できるアプリです。センター コンソール、計器類、追加の画面(たとえば高級車に搭載されているパノラマ ディスプレイ)など、車内のさまざまな画面に合わせて設計された最適なユーザー エクスペリエンスを提供します。
この Codelab では、Tier 1 機能である運転中のオーディオ再生を実装し、アプリを自動車向けに差別化します。
4. Android Automotive OS エミュレータでアプリを実行する
Automotive with Play Store のシステム イメージをインストールする
- まず、Android Studio で SDK Manager を開き、まだ選択していない場合は [SDK Platforms] タブを選択します。SDK Manager ウィンドウの右下にある [Show package details] チェックボックスがオンになっていることを確認します。
- Generic System Image の追加の一覧から、いずれかの API 34 Android Automotive エミュレータ イメージをインストールします。イメージは、同じアーキテクチャ(x86 / ARM)のマシンでのみ実行できます。
Android Automotive OS Android Virtual Device を作成する
- デバイス マネージャーを開き、ウィンドウの左側にある [Category] 列で [Automotive] を選択します。次に、バンドルされたハードウェア プロファイルの一覧から Automotive (1408p landscape) を選択し、[Next] をクリックします。
- 次のページに移動したら、前の手順で作成したシステム イメージを選択します。[Next] をクリックし、必要に応じて詳細オプションを選択したら、[Finish] をクリックして AVD を作成します。
アプリを実行する
前のステップで作成したエミュレータで、app
実行構成を使用してアプリを実行します。プレーヤー画面に移動して運転をシミュレートし、アプリの動作をテストします。
5. 運転中のオーディオのサポートを検出する
運転中のオーディオ再生はすべての自動車でサポートされているわけではないため、現在のデバイスがこの機能をサポートしているかどうかを検出し、それに応じてアプリの動作を調整する必要があります。そのためには、androidx.car.app:app
ライブラリの CarFeatures
クラスを使用します。
androidx.car.app:app
アーティファクトへの依存関係を追加します。
libs.version.toml
[versions]
...
carApp = "1.7.0-rc01"
[libraries]
...
androidx-car-app = { group = "androidx.car.app", name = "app", version.ref = "carApp" }
build.gradle.kts(:app モジュール)
implementation(libs.androidx.car.app)
RoadReelsPlayer
で、この機能がサポートされているかどうかを判断し、shouldPreventPlay
の値の計算に使用されるロジックを更新できます。
RoadReelsPlayer.kt
if (packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
...
val isBackgroundAudioWhileDrivingSupported = CarFeatures.isFeatureEnabled(
context,
CarFeatures.FEATURE_BACKGROUND_AUDIO_WHILE_DRIVING
)
shouldPreventPlay = !isBackgroundAudioWhileDrivingSupported &&
carUxRestrictionsManager.currentCarUxRestrictions.isRequiresDistractionOptimization
invalidateState()
carUxRestrictionsManager.registerListener { carUxRestrictions: CarUxRestrictions ->
shouldPreventPlay = !isBackgroundAudioWhileDrivingSupported &&
carUxRestrictions.isRequiresDistractionOptimization
...
}
}
変更を加えたアプリを再度実行し、運転をシミュレートします。アプリの UI がシステムによって隠されていても、オーディオ再生が続行されるようになりました。ただし、まだ完了ではありません。すべての要件を満たすには、さらにいくつか変更する必要があります。
6. バックグラウンド再生をサポートする
現在、アプリのメディア再生はアクティビティによって処理されています。そのため、ユーザー エクスペリエンスの制限が有効になり、アクティビティが隠された後も、メディアの再生がしばらく続くことがあります。しかし、最終的にはアプリがシステムによってキャッシュに保存され、再生が終了します。
長時間の再生をサポートするには、サービスを使用して再生を処理するようにアプリを更新する必要があります。これは Media3 MediaSessionService
を使用して実現できます。
MediaSessionService を作成する
- [Project] ウィンドウで
com.example.android.cars.roadreels
パッケージを右クリックし、[New] > [Kotlin Class/File] を選択します。ファイル名として「PlaybackService
」と入力し、[Class] タイプをクリックします。 PlaybackService.
の次の実装を追加します。MediaSessionService
の詳細については、MediaSessionService によるバックグラウンド再生をご覧ください。
PlaybackService.kt
import androidx.media3.common.util.UnstableApi
import androidx.media3.session.MediaSession
import androidx.media3.session.MediaSessionService
@UnstableApi
class PlaybackService : MediaSessionService() {
private var mediaSession: MediaSession? = null
override fun onCreate() {
super.onCreate()
val player = RoadReelsPlayer(this)
mediaSession = MediaSession.Builder(this, player).build()
}
override fun onDestroy() {
mediaSession?.run {
player.release()
release()
mediaSession = null
}
super.onDestroy()
}
override fun onGetSession(controllerInfo: MediaSession.ControllerInfo): MediaSession? {
if (controllerInfo.isTrusted || controllerInfo.packageName == packageName) {
return mediaSession
}
return null
}
}
- マニフェスト ファイルに次の
<uses-permission>
要素を追加します。メディアの再生はフォアグラウンド サービスを使用して処理されます。
AndroidManifest.xml
<manifest ...>
...
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
...
</manifest>
- マニフェストでも
PlaybackService
を宣言します。
AndroidManifest.xml
<application ...>
...
<service
android:name=".PlaybackService"
android:foregroundServiceType="mediaPlayback"
android:exported="true">
<intent-filter>
<action android:name="androidx.media3.session.MediaSessionService"/>
</intent-filter>
</service>
</application>
PlaybackService を使用するように PlayerViewModel を更新する
PlaybackService
がMediaSession
を作成して公開するため、PlayerViewModel
が MediaSession を作成する必要がなくなりました。次の行と、この変数への参照をすべて探して削除します。
PlayerViewModel.kt
private var mediaSession: MediaSession? = null
- 次に、
RoadReelsPlayer
をインスタンス化するinit
ブロックのセクションを、MediaController
を使用してアプリをPlaybackService
に接続する次のコードに置き換えます。
PlayerViewModel.kt
import android.content.ComponentName
import androidx.media3.session.MediaController
import androidx.media3.session.SessionToken
import com.example.android.cars.roadreels.PlaybackService
import com.google.common.util.concurrent.MoreExecutors
...
init {
viewModelScope.launch {
...
}
val sessionToken = SessionToken(
application,
ComponentName(application, PlaybackService::class.java)
)
val mediaControllerFuture =
MediaController.Builder(getApplication(), sessionToken).buildAsync()
mediaControllerFuture.addListener(
{ _player.update { mediaControllerFuture.get() } },
MoreExecutors.directExecutor()
)
}
アプリをもう一度テストすると、アプリがシステムによってブロックされたときに別の UI が表示されます。これで、ユーザーは移動中でも再生をコントロールできるようになりました。再度停車したときに、終了ボタンをクリックして完全なアプリ エクスペリエンスに戻ることができます。
ライフサイクルの再生に関する変更を削除する
バックグラウンド再生がサポートされたため、PlayerScreen.kt
の 2 つの LifecycleEventEffect
ブロックを削除して、ユーザーがアプリから離れたときに再生を続行できるようにします。これには、前の画面に表示された再生コントロールが表示されている場合も含まれます。
プレーヤー画面を離れたときに再生を一時停止する
実際のプレーヤー(現在は PlaybackService
に含まれています)はプレーヤー画面から離れたときに解放されないため、以前の動作を維持するには、プレーヤー画面から離れたときに再生を一時停止する呼び出しを追加する必要があります。これを行うには、PlayerViewModel
の onCleared
メソッドの実装を更新します。
PlayerViewModel.kt
override fun onCleared() {
super.onCleared()
_player.value?.apply {
pause()
release()
}
}
7. マニフェストを更新する
最後に、アプリが運転中のオーディオ再生をサポートしていることを示すには、アプリのマニフェストに次の <uses-feature>
要素を追加する必要があります。
AndroidManifest.xml
<application>
<uses-feature android:name="com.android.car.background_audio_while_driving" android:required="false">
</application>
8. 完了
運転中のオーディオ再生のサポートを動画アプリに追加できました。次は、学習した内容を自分のアプリに適用してみましょう。
参考資料
- 自動車用の駐車時向けアプリを作成する
- Android Automotive OS 用の動画アプリを作成する
- 駐車時向けアプリに Android Automotive OS のサポートを追加する
- 自動車向け Android アプリの品質に関するページでは、優れたユーザー エクスペリエンスを実現し、Google Play ストアの審査に合格するために、アプリが満たす必要がある基準について説明しています。必ずアプリのカテゴリでフィルタしてください。