Migrate Media I guide snippets - #1115
barbaralaw wants to merge 9 commits into
Conversation
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.example.media |
There was a problem hiding this comment.
can we have android in the package name for these files? com.example.android.media?
| androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle-compose" } | ||
| androidx-lifecycle-viewmodel-navigation3 = { module = "androidx.lifecycle:lifecycle-viewmodel-navigation3", version.ref = "androidx-lifecycle-viewmodel-navigation3" } | ||
| androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core" } | ||
| androidx-media = { module = "androidx.media:media", version.ref = "androidx-media" } |
There was a problem hiding this comment.
I'm wondering if we should add this dependency or not (if this is the old version of the media library). Posted a question on our group chat, curious for your thoughts!
|
|
||
| @OptIn(UnstableApi::class) | ||
| // [START android_media_surfaces_mobile_custom_command_buttons] | ||
| class CustomControlsPlaybackService : MediaSessionService() { |
There was a problem hiding this comment.
Could we keep the rendered class name as class PlaybackService : MediaSessionService() inside the region tag and avoid the collision by wrapping the snippet in a private object outside the region tag?
```kotlin
private object CustomControlsSnippet {
// [START android_media_surfaces_mobile_custom_command_buttons]
class PlaybackService : MediaSessionService() {
...
}
// [END android_media_surfaces_mobile_custom_command_buttons]
}
|
Moving this back to draft while I apply the outcome of the Media legacy assessment and get an internal review. I'll address your comments and re-request your review once it's ready. Thanks for your patience! |
- Remove the Assistant, pre-Android 13 and legacy mobile surfaces snippets. Those pages and sections stay hardcoded on DAC as legacy. - Remove the androidx-media dependency. AudioFocus.kt now uses the framework MediaController in non-rendered code. - Keep the rendered class name PlaybackService by wrapping the custom command buttons snippet in a private object (review feedback). - Reuse playback-app's create_media_session region for the Android TV page instead of a duplicate region. - Open audio_focus_request and playback_service once each, using silent excludes. Rendered output is unchanged. - End natural-language comments inside regions with a full stop.
e770b58 to
6591435
Compare
Show super.onStart() in connect_ui, move the saveToFavorites stub out of the region, drop the unused core-ktx dependency, and register PlaybackService in the module manifest.
Summary
Extracts the Kotlin samples from five Media I guides (editing, playback, Android TV, media controls and audio focus) into
:mediaas region-tagged source, so the guides can import them instead of hardcoding them.18 new region tags across 5 pages. The Android TV page reuses playback-app's
create_media_sessionregion because it prints the same block. Of the 20 snippets the pages import, 9 publish exactly what the page shows. The rest differ for a reason listed below.Region tags all begin
android_media_; the lists and tables drop that prefix.Code snippets are for:
List of modifications
editing_set_hdr_mode: the closing)ofImmutableList.of(videoSequence)moves to its own line (formatter).editing_trim_videoandediting_custom_effects: 3 comments end with a full stop.editing_preview_audio_effects: drops theenableOffloadparameter and.setOffloadMode(...), which current Media3 no longer has, so the published code doesn't compile. The builder is re-wrapped (formatter).playback_app_playback_serviceandplayback_app_on_get_session: 3 comments end with a full stop.playback_app_connect_ui: addssuper.onStart()as the first line ofonStart(), because anActivitythat overridesonStart()must call it or the app crashes. Indentation goes from 2 to 4 spaces (formatter).playback_app_create_media_session.surfaces_mobile_custom_command_buttons: indentation goes from 2 to 4 spaces, and the under-indentedonConnectbody is aligned with its function (formatter). 1 comment ends with a full stop.audio_focus_request: thesetAudioAttributes(...)argument moves onto its own lines (formatter). 5 comments end with a full stop.audio_focus_request_pre_o: adds the missing:inlateinit var afChangeListener: AudioManager.OnAudioFocusChangeListener, so the code compiles. The bare...becomes// ..., and therequestAudioFocusarguments are indented 4 instead of 8 (formatter). 2 comments end with a full stop.audio_focus_change_listener: 7 comments end with a full stop.The per-page tables below give the same changes with the page section for each snippet.
Scope after the Media legacy assessment
This PR was paused while the team decided which Media pages are legacy. Following that assessment:
onGetRootand the pre-Android 13 notification) stays hardcoded.androidx-mediadependency is removed, since only the legacy snippets needed it.How to read the "why" column
spotlessApplynormalizes indentation to 4 spaces and wraps long calls.// ......, which Kotlin will not compile. The region publishes// ....Per page
1. Create a basic video editing app using Media3 Transformer
https://developer.android.com/media/implement/editing-app
8 snippets, 4 match, 4 differ.
editing_set_hdr_mode)ofImmutableList.of(videoSequence)on its own lineediting_trim_videoediting_custom_effectsediting_preview_audio_effectsenableOffloadparameter frombuildAudioSinkand.setOffloadMode(...)fromDefaultAudioSink.Builder; the builder is rewrappedMatching as published:
editing_transcode,editing_built_in_effects,editing_preview_effects,editing_start_transformation.2. Create a basic media player app using Media3 ExoPlayer
https://developer.android.com/media/implement/playback-app
5 snippets (4 new, plus the existing
playback_app_create_exoplayer), 2 match, 3 differ.playback_app_playback_serviceMediaSessionServiceplayback_app_on_get_sessionMediaSessionServiceplayback_app_connect_uisuper.onStart()as the first line ofonStart(). Indentation 2 → 4 spacesMatching as published:
playback_app_create_exoplayer,playback_app_create_media_session.playback_app_on_get_sessionsits insideplayback_app_playback_servicein source. The outer region hides it with a silent exclude so each tag is opened only once, and both render as the page shows.3. Extend your media app to Android TV
https://developer.android.com/media/implement/surfaces/android-tv
1 snippet, matches as published. The page's MediaSession block is identical to playback-app's, so the page imports
playback_app_create_media_sessioninstead of a duplicate region.4. Media controls
https://developer.android.com/media/implement/surfaces/mobile
1 snippet migrated; the legacy section stays hardcoded (see Scope above).
surfaces_mobile_custom_command_buttonsonConnectbody is indented to match its function; 1 comment gets a full stopThe rendered class name stays
PlaybackService, as on the page. The region sits inside aprivate object CustomControlsSnippetwrapper (outside the tags) to avoid clashing with playback-app'sPlaybackService.5. Manage audio focus
https://developer.android.com/media/optimize/audio-focus
5 snippets, 2 match, 3 differ.
audio_focus_requestsetAudioAttributes(...)argument moves onto its own lines; 5 comments get a full stopaudio_focus_request_pre_olateinit var afChangeListener AudioManager.OnAudioFocusChangeListenergets its missing:;...→// ...;requestAudioFocusarguments indented 4 instead of 8; 2 comments get a full stop// ..., ktlint wrap, full stopaudio_focus_change_listenerMatching as published:
audio_focus_abandon,audio_focus_delayed_stop_runnable.The page shows the request setup and
onAudioFocusChangeas one continuous block, but in source the override has to live on a type that implementsAudioManager.OnAudioFocusChangeListener(private inner class AudioFocusHandler).audio_focus_requestis one region, and a silent exclude hides the end of the setup function and the inner-class header between the two parts. The hidden}and{balance each other, so the rendered block is exactly the page's block (plus the changes listed above).Snippets not migrated
MediaSessionServiceandMediaLibraryServiceblocks, because they are in the Android manifest file.The Java versions of the migrated Kotlin samples aren't migrated. The paired guide-page change removes them, following Kotlin-first guidance.
Other Media I pages not in this PR:
Dependencies
One entry added to
gradle/libs.versions.toml:androidx-media3-transformer, which reuses the existingmedia3version throughversion.ref.:medianow also depends onmedia3-common,media3-effect,media3-session,media3-transformer,media3-uiandguava-android, each used by a snippet in this PR.Live snippet defects this surfaced
Fixed in the migrated regions:
lateinit var afChangeListener AudioManager.OnAudioFocusChangeListeneris missing:.buildAudioSinkstill takesenableOffloadand callsDefaultAudioSink.Builder.setOffloadMode. Neither exists in this module's Media3. Offload is now configured withTrackSelectionParameters.AudioOffloadPreferences, which doesn't belong in this processor-preview sample, so the region publishes the current three-argument override.onStart()never callssuper.onStart(), so the activity throwsSuperNotCalledException. The region adds the call.On pages or sections that stay hardcoded (not fixed here; listed for the page owner):
stateActionschain starts withor, so Kotlin ends the statement afterACTION_PLAYand the next lines don't compile.object: MediaSession.Callback()sample is the platformandroid.media.sessioncallback, butsessionin that section is aMediaSessionCompat, whosesetCallbacktakes aMediaSessionCompat.Callback.onGetRootstarts with...and never closes the function}.Transformer.Builder.setTransformationRequest()andTransformationRequest.HDR_MODE_TONE_MAP_HDR_TO_SDRno longer exist in this module's Media3. The successor isComposition.Builder.setHdrMode(HDR_MODE_TONE_MAP_HDR_TO_SDR_USING_OPEN_GL), but porting the block would change what the page teaches while the prose still describes the old flow.MediaFormatblocks callformat.setInt32(...), which isn't a Java API; it should beformat.setInteger(...).Notes for reviewers
class PlaybackServiceinPlaybackApp.ktis public because its declaration line is inside theplayback_app_playback_serviceregion; making it private would change the rendered snippet.:media's ownAndroidManifest.xmlnow registers thatPlaybackService, the way an app would: exported, with theandroidx.media3.session.MediaSessionServiceintent filter,foregroundServiceType="mediaPlayback", and theFOREGROUND_SERVICEandFOREGROUND_SERVICE_MEDIA_PLAYBACKpermissions (required from target SDK 31 and 34). It's module configuration, not a snippet; the page's own manifest block stays hardcoded.custom_command_buttons, the requiredonGetSessionoverride sits inside the class, hidden with a silent exclude. The compile-onlysaveToFavorites(...)stub is a private top-level function outside the region.Verification
Rebased on
main(media3 1.11.1)../gradlew :media:spotlessCheckpasses./gradlew :media:compileDebugKotlinpasses./gradlew :media:lintDebugpassesincludecoderegion parser and compared with the live page block; the differences are exactly the ones listed above.