Loading...
Loading...
Adapt existing Android video streaming apps, games, and web browsers to run as parked apps on Android Auto and Android Automotive OS. Covers manifest configuration, Google Play hardware feature declarations, compatibility mode, back navigation affordances, orientation and display continuity, controllable system bar insets, irregular display cutouts, driver distraction lifecycle handling, MediaSession playback restrictions with CarUxRestrictionsManager, background audio while driving, and browser sensitive data protection. Use this skill when migrating, building, or auditing parked apps for cars to meet Car App Quality guidelines. Don't use for media audio apps, templated Car App Library navigation, point of interest, internet of things, weather, or communication apps designed for use while driving.
npx skill4agent add android/skills android-for-cars-parked-appsandroid.software.car.display_compatibilityAndroidManifest.xmlAssets/Plugins/Android/AndroidManifest.xmlres/xml/automotive_app_desc.xmlbuild.gradlebuild.gradle.ktsgradle/libs.versions.tomlMediaSessionrequestedOrientationWindowInsetsAndroidManifest.xmlandroid:appCategory="video"<application>app/src/main/res/xml/automotive_app_desc.xmluses name="video"android:appCategory="game"<application>android.hardware.gamepadandroid:required="false"<category android:name="android.intent.category.APP_BROWSER"/>MAIN<intent-filter><activity>android:exported="true"<category android:name="android.intent.category.CAR_LAUNCHER"/><intent-filter>AndroidManifest.xmlMAIN<intent-filter><uses-feature android:name="android.hardware.type.automotive" android:required="false"/>"false""true"AndroidManifest.xmlDO-1DO-1android.hardware.wifiandroid.hardware.screen.portraitandroid.hardware.screen.landscapeandroid.hardware.cameraandroid:required="false"android:required="true"LS-C1android:configChangesscreenSizesmallestScreenSizeorientationscreenLayoutdensity<activity>android.hardware.type.automotive<meta-data android:name="android.software.car.display_compatibility" android:value="true"/><application>android.software.car.display_compatibilityAndroidManifest.xmlPackageManager.hasSystemFeature("android.software.car.display_compatibility")true<meta-data android:name="distractionOptimized" android:value="true"/><activity>DD-2DD-3AN-1AndroidManifest.xmlandroid.software.car.display_compatibilityAndroidManifest.xmlandroid.software.car.display_compatibilityandroid:value="true"PackageManager.hasSystemFeature("android.software.car.display_compatibility")falseDO-1android:screenOrientationPackageManager.FEATURE_SCREEN_LANDSCAPEPackageManager.FEATURE_SCREEN_PORTRAITrequestedOrientationAR-1OnControllableInsetsChangedListenerwindowInsetsPaddingstatusBarsnavigationBarsdisplayCutoutAR-2LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT0LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES1LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS3LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER2carres/values-car-v30/integers.xml<integer name="windowLayoutInDisplayCutoutMode">3</integer>WindowInsets.safeDrawingonPauseonStopDD-2DD-3MC-1ON_PAUSEON_STOPLifecycleEventEffectandroidx.lifecycle:lifecycle-runtime-composeMediaSessionandroidx.media3:media3-sessionMC-1onCleareduseLibrary("android.car")build.gradle.ktsExoPlayerForwardingSimpleBasePlayerCarUxRestrictionsManagerisRequiresDistractionOptimizationtrueCOMMAND_PLAY_PAUSEgetStateMediaSession@UnstableApi
class CarAwarePlayer(context: Context) :
ForwardingSimpleBasePlayer(ExoPlayer.Builder(context).build()) {
private var shouldPreventPlay = false
private var pausedByUxRestrictions = false
private lateinit var carUxRestrictionsManager: CarUxRestrictionsManager
init {
if (context.packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE)) {
val car = Car.createCar(context)
carUxRestrictionsManager =
car.getCarManager(Car.CAR_UX_RESTRICTION_SERVICE) as CarUxRestrictionsManager
shouldPreventPlay =
carUxRestrictionsManager.currentCarUxRestrictions.isRequiresDistractionOptimization
invalidateState()
carUxRestrictionsManager.registerListener { restrictions: CarUxRestrictions ->
shouldPreventPlay = restrictions.isRequiresDistractionOptimization
if (!shouldPreventPlay && pausedByUxRestrictions) {
handleSetPlayWhenReady(true)
invalidateState()
} else if (shouldPreventPlay && isPlaying) {
pausedByUxRestrictions = true
handleSetPlayWhenReady(false)
invalidateState()
}
}
}
}
override fun getState(): State {
val state = super.getState()
return state.buildUpon()
.setAvailableCommands(
state.availableCommands.buildUpon()
.removeIf(COMMAND_PLAY_PAUSE, shouldPreventPlay)
.build()
)
.build()
}
override fun handleRelease(): ListenableFuture<*> {
if (::carUxRestrictionsManager.isInitialized) {
carUxRestrictionsManager.unregisterListener()
}
return super.handleRelease()
}
}<uses-feature android:name="com.android.car.background_audio_while_driving" android:required="false" />AndroidManifest.xmlFOREGROUND_SERVICEFOREGROUND_SERVICE_MEDIA_PLAYBACKCarFeatures.isFeatureEnabled(context, CarFeatures.FEATURE_BACKGROUND_AUDIO_WHILE_DRIVING)androidx.car.app:appshouldPreventPlay!isBackgroundAudioWhileDrivingSupported && restrictions.isRequiresDistractionOptimizationMediaSessionMediaSessionServiceandroid:foregroundServiceType="mediaPlayback"MediaController.BuilderbuildAsyncSD-1SD-2KeyguardManagerSettings.ACTION_SECURITY_SETTINGSBiometricPromptBiometricManager.Authenticators.DEVICE_CREDENTIALval keyguardManager = context.getSystemService<KeyguardManager>()
val isDeviceSecure = keyguardManager?.isDeviceSecure == true
lateinit var biometricPrompt: BiometricPrompt
if (!isDeviceSecure) {
context.startActivity(
Intent(Settings.ACTION_SECURITY_SETTINGS).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
)
} else {
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle(context.getString(R.string.auth_title))
.setSubtitle(context.getString(R.string.sync_data_to_car_notice))
.setAllowedAuthenticators(BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.build()
biometricPrompt.authenticate(promptInfo)
}./gradlew assembleDebug<meta-data android:name="distractionOptimized" android:value="true"/><activity>res/xml/automotive_app_desc.xmluses name="video"LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULTLAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGESisRequiresDistractionOptimizationCarUxRestrictionsManagerBIOMETRIC_STRONGBIOMETRIC_WEAKBiometricPromptDEVICE_CREDENTIALCAR_LAUNCHERandroid.hardware.type.automotiveandroid:required="false"wifiscreen.portraitscreen.landscapecamerarequestedOrientationFEATURE_SCREEN_LANDSCAPEFEATURE_SCREEN_PORTRAITDO-1AN-1WindowInsets.safeDrawingOnControllableInsetsChangedListenerAR-1AR-2COMMAND_PLAY_PAUSEMediaSessionDD-2DD-4MC-1SD-1SD-2