Skip to content

Migrate Media enhancement, HDR, projection, and spatial audio snippets - #1114

Draft
barbaralaw wants to merge 5 commits into
android:mainfrom
StellarElements:media2-snippet-migration
Draft

barbaralaw wants to merge 5 commits into
android:mainfrom
StellarElements:media2-snippet-migration

Conversation

@barbaralaw

@barbaralaw barbaralaw commented Sep 17, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Extracts the Kotlin samples from eight Media guides (AI enhancement, HDR, media projection, spatial audio and Ultra HDR) into :media as region-tagged source, so the guides can import them instead of hardcoding them.

18 snippets across 8 pages. 4 publish exactly what the page shows. The rest differ for a reason listed below. One Surface-mode block stays hardcoded because it can't be made to compile without contradicting the page's text (see page 3).

Region tags all begin android_media_; the lists and tables drop that prefix.

Code snippets are for:

One non-Kotlin file is added because a tagged Kotlin snippet won't build without it: media/src/main/res/layout/fragment_ultra_hdr_display.xml. The Display page's code calls binding.imageContainer, which needs a generated view binding (FragmentUltraHdrDisplayBinding) with an ImageView whose id is image_container. The page's code forces only that id; the file name is our choice. The layout has no region tag and doesn't appear on the page.

List of modifications

  • Bitmap-mode-lifecycle page: ai_enhancement_bitmap_initialize_engine: installModule() becomes installModule(installStatusCallback), because the published library requires a callback. 2 comments end with a full stop.
  • Bitmap-mode-lifecycle page: ai_enhancement_bitmap_wrappers: this.createSession(...) becomes createSession(...), because inside withContext { } this is the CoroutineScope and the published code doesn't compile.
  • Bitmap-mode-lifecycle page: ai_enhancement_bitmap_viewmodel: EnhancementOptions uses the library's parameter names and gains one argument line. The library splits deblur/denoise and upscale into photo and video flags and has no denoise-only option, so enableDenoiseOnly is dropped and both video flags are false. val options moves above the if so it can be passed to processBitmapAsync(bitmap, options), because EnhancementSession has no defaultOptions. Comments that sat at column 0 are indented. 4 comments end with a full stop.
  • Get-started page: ai_enhancement_get_started_checks: 2 comments end with a full stop.
  • Surface-mode-lifecycle page: ai_enhancement_surface_initialize_engine: the same installModule(installStatusCallback) change as on the Bitmap-mode-lifecycle page. 2 comments end with a full stop.
  • Hdr-playback page: hdr_playback_check_support: trailing ; dropped and indentation goes from 2 to 4 spaces (formatter). 1 comment ends with a full stop.
  • Hdr-playback page: hdr_playback_mediacodec: two declarations jammed on one line are split into two statements, because the published line doesn't compile. Trailing ; and the space before ( are dropped, the callback is re-wrapped so ( and ) sit on their own lines (its body gains one indent level), and 3-space indents, including the while body, become 4 (formatter). 4 comments end with a full stop.
  • Media-projection page: projection_start: var mediaProjection : MediaProjection becomes var mediaProjection: MediaProjection?, because getMediaProjection() can return null and the published assignment doesn't compile.
  • Media-projection page: projection_start: <br> line breaks become blank lines (formatter).
  • Media-projection page: projection_virtual_display and projection_window_context_metrics: aligned arguments get a 4-space indent and the call is re-wrapped so the closing ) sits on its own line (formatter).
  • Spatial-audio page: spatial_audio_disable_channel_constraints_player, spatial_audio_disable_channel_constraints_selector and spatial_audio_set_max_channels: builder chains are indented 4 instead of 2 spaces (formatter). The page shows .setConstrainAudioChannelCountToDeviceCapabilities(false) in bold in the first two blocks; the imported snippets show it as plain code. The selector block's bare ... becomes // ....
  • Display page: ultra_hdr_display_window_color_mode: one continuation line is indented 4 instead of 3 spaces (formatter).
  • Display page: adds untagged media/src/main/res/layout/fragment_ultra_hdr_display.xml (one ImageView, id image_container). Required for the Kotlin snippet to compile.

The per-page tables below give the same changes with the page section for each snippet.

How to read the "why" column

Why What a reader of the page would see
real library API The page code doesn't compile against the published enhancement library. The region uses the real API with the smallest visible change.
hardcoded snippet defect fixed The published Kotlin doesn't compile, and it isn't a sample meant to show an error. The region publishes the corrected line.
// ... The page prints a bare ..., which Kotlin won't compile. The region publishes // ....
comment indent Comments that sat at column 0 are indented with the surrounding class or method body.
Spotless Same tokens, different line breaks or indentation. spotlessApply normalizes indentation to 4 spaces, drops trailing ; and wraps long calls.
full stop Natural-language comments inside a region end with a full stop, for consistency. 15 comments in this PR, listed per snippet.

The enhancement library

The AI-enhancement snippets now compile against the published library, com.google.android.gms:play-services-media-effect-enhancement, instead of local compile-only shims. It's pinned to 16.0.0-beta04, the version the get-started page documents. 16.0.0-beta05 and later add an abstract EnhancementCallback.onCancelled(int) that the page's callback doesn't implement.

Per page

1. Understand the media enhancement lifecycle in Bitmap mode

https://developer.android.com/media/ai-enhancement/bitmap-mode-lifecycle

3 snippets, all 3 differ.

Snippet Page section Difference Why
ai_enhancement_bitmap_initialize_engine Initialize the enhancement engine installModule() → installModule(installStatusCallback); the callback is a private declaration outside the snippet. 2 comments get a full stop real library API, full stop
ai_enhancement_bitmap_wrappers Create session and bitmap process wrappers this.createSession(...) inside withContext { } becomes createSession(...) hardcoded snippet defect fixed
ai_enhancement_bitmap_viewmodel Execute the bitmap pipeline in a ViewModel EnhancementOptions uses the library's parameters (isTonemappingEnabled, isDeblurAndDenoisePhotoEnabled, isDeblurAndDenoiseVideoEnabled, isUpscalePhotoEnabled, isUpscaleVideoEnabled), which adds one line. The library splits deblur/denoise and upscale into photo and video flags and has no denoise-only option, so enableDenoiseOnly is dropped and both video flags are false. val options moves above the if so it can be passed to processBitmapAsync(bitmap, options), because EnhancementSession has no defaultOptions. Column-0 comments are indented. 4 comments get a full stop real library API, comment indent, full stop

2. Get started with Media Enhancement APIs

https://developer.android.com/media/ai-enhancement/get-started

1 snippet, differs.

Snippet Page section Difference Why
ai_enhancement_get_started_checks Device compatibility and module setup 2 comments get a full stop full stop

3. Understand the media enhancement lifecycle in Surface mode

https://developer.android.com/media/ai-enhancement/surface-mode-lifecycle

1 snippet migrated, differs; 1 block stays hardcoded.

Snippet Page section Difference Why
ai_enhancement_surface_initialize_engine Initialize the enhancement engine Same installModule(installStatusCallback) change as Bitmap mode; 2 comments get a full stop real library API, full stop

Kept hardcoded: the snapshot block under Process a single frame. The page's code and prose describe the app supplying the input Surface (options.setInputSurface / setOutputSurface). In the real library, the session provides the input surface (EnhancementSession.getInputSurface()) and the output is set with EnhancementSession.setOutputSurface(Surface, EnhancementOptions, EnhancementCallback). Fixing the code would contradict the page text, so the block stays hardcoded (with a linter suppression) and is being reported to the page owner.

4. Color correct with look-up tables (LUTs)

https://developer.android.com/media/grow/hdr-lut

1 snippet, matches as published: hdr_lut_apply.

5. HDR video playback

https://developer.android.com/media/grow/hdr-playback

2 snippets, both differ.

Snippet Page section Difference Why
hdr_playback_check_support Check for HDR playback support Trailing ; dropped; indentation 2 → 4 spaces; 1 comment gets a full stop Spotless, full stop
hdr_playback_mediacodec Set up MediaCodec using SurfaceView The jammed val list = MediaCodecList(...) var format = MediaFormat() …; is split into two statements. Trailing ; dropped; the space before ( in findDecoderForFormat (format) and RuntimeException (...) removed; the callback re-wrapped so ( and ) sit on their own lines (its body gains one indent level); 3-space indents, including the while body, become 4; onOutputFormatChanged parameters on separate lines. 4 comments get a full stop hardcoded snippet defect fixed, Spotless, full stop

6. Media projection

https://developer.android.com/media/grow/media-projection

3 snippets, all 3 differ.

Snippet Page section Difference Why
projection_start Real display var mediaProjection : MediaProjection → var mediaProjection: MediaProjection?. The page's <br> line breaks become blank lines hardcoded snippet defect fixed, Spotless
projection_virtual_display Virtual display Arguments aligned under the call (21 spaces) become a 4-space indent; closing ) on its own line Spotless
projection_window_context_metrics Resizable apps createWindowContext(...) split so context.display!! and the closing ) sit on their own lines; .maximumWindowMetrics indented 4 instead of 6 Spotless

7. Spatial Audio

https://developer.android.com/media/grow/spatial-audio

6 snippets, 3 match, 3 differ.

Snippet Page section Difference Why
spatial_audio_disable_channel_constraints_player Audio channel count constraints Builder chain indented 4 instead of 2. The page bolds .setConstrainAudioChannelCountToDeviceCapabilities(false); the imported snippet can't, so it shows as plain code Spotless, no bold in imported code
spatial_audio_disable_channel_constraints_selector Audio channel count constraints Bare ... becomes // ...; builder chain indented 4 instead of 2. The same bold line shows as plain code // ..., Spotless, no bold in imported code
spatial_audio_set_max_channels Changing the track selection parameters Builder chain indented 4 instead of 2 Spotless

Matching as published: spatial_audio_get_spatializer, spatial_audio_max_output_channels, spatial_audio_audio_format.

8. Display Ultra HDR images

https://developer.android.com/media/grow/ultra-hdr/display

1 snippet, differs.

Snippet Page section Difference Why
ultra_hdr_display_window_color_mode Putting it all together One continuation line indented 4 instead of 3 Spotless

The published Kotlin is Views-based (binding.imageContainer, requireActivity().window.colorMode). It compiles inside a hidden private Fragment that inflates a real generated ViewBinding. That adds a small layout, media/src/main/res/layout/fragment_ultra_hdr_display.xml (one ImageView with id image_container, which the page's binding.imageContainer requires), and buildFeatures { viewBinding = true } in :media. The page's /* Get Bitmap from Image Resource */ placeholder is kept visible, and a hidden line supplies the real bitmap. A Compose version of this page is a content change for the page owner, not part of this migration.

Snippets not migrated

  • Get-started page: the two Gradle dependencies blocks, because they are dependency blocks. The native-library block, because it is in the Android manifest file.
  • Surface-mode-lifecycle page: the snapshot block under Process a single frame, because its code and text describe an API the published library doesn't have (see page 3). Reported to the page owner.
  • Media-projection page: the foreground-service block, because it is in the Android manifest file.
  • Spatial-audio page: the "Test spatial audio" dumpsys output, because it is command output, not code.

The Java versions of the migrated Kotlin samples (HDR LUT, HDR playback, media projection, spatial audio and Ultra HDR) aren't migrated. The paired guide-page change removes them, following Kotlin-first guidance.

Dependencies

  • gradle/libs.versions.toml: playServicesMediaEffectEnhancement = "16.0.0-beta04" and the play-services-media-effect-enhancement library entry.
  • :media now also depends on androidx-activity-ktx, androidx-fragment-ktx, androidx-lifecycle-viewmodel-ktx, media3-common, kotlinx-coroutines-android, kotlinx-coroutines-play-services and play-services-media-effect-enhancement, each used by a snippet in this PR, and enables viewBinding.

Live snippet defects this surfaced

Fixed in the migrated regions:

  1. HDR video playback: one Kotlin sample joins two declarations on a single line (val list = MediaCodecList(...) var format = MediaFormat() /* media format from the container */;), which isn't valid Kotlin. The region publishes them as separate statements.
  2. Bitmap-mode lifecycle wrappers: this.createSession(...) sits inside withContext(Dispatchers.Main) { }, so this is the CoroutineScope and the call doesn't compile. The region keeps the Main hop and publishes createSession(...).
  3. Spatial audio: a standalone ... between the DefaultTrackSelector setup and buildUponParameters isn't valid Kotlin. The region publishes // ....
  4. Media projection start: getMediaProjection() can return null, so assigning it to a non-null MediaProjection doesn't compile. The region declares var mediaProjection: MediaProjection?.
  5. AI enhancement (Bitmap and Surface modes): installModule() needs an InstallStatusCallback, EnhancementOptions has different parameters, and EnhancementSession has no defaultOptions. The regions use the real API (see page 1).

Not fixed (listed for the page owner):

  1. Surface-mode snapshot: the code and prose describe an API the library doesn't have (see page 3).

Notes for reviewers

  • isDeviceSupportedAsync() and isModuleInstalledAsync() are public extensions in GetStartedEnhancement.kt because they're declared inside the visible ai_enhancement_get_started_checks region; the Bitmap and Surface files reuse them.
  • Inline /* … */ placeholder labels on the pages (for example /* flags */, /* until EOS */) are kept exactly as published, without a full stop.
  • Three placeholders stay visible as the page prints them, with a hidden line that supplies a real value so the file compiles: while (/* until EOS */) { is backed by a hidden while (isStreaming) {, buffer?.put(/* write bitstream */) by a hidden buffer?.put(byteArrayOf()) (both in HDR playback), and /* Get Bitmap from Image Resource */ by a hidden bitmap load (Ultra HDR). The rendered blocks match the page.
  • The compile-only installStatusCallback and the notifyUi* / handleInitializationError stubs used by the two initialize_engine snippets are private top-level declarations outside the regions.

Verification

Rebased on main.

  • ./gradlew :media:spotlessCheck passes
  • ./gradlew :media:compileDebugKotlin passes
  • ./gradlew :media:lintDebug passes
  • Every region was rendered with DevSite's includecode region parser and compared with the live page block; the differences are exactly the ones listed above.

@barbaralaw
barbaralaw force-pushed the media2-snippet-migration branch from 60e9422 to 4359abc Compare September 17, 2026 22:31
@barbaralaw
barbaralaw marked this pull request as ready for review September 18, 2026 15:18
@snippet-bot

snippet-bot Bot commented Sep 18, 2026

Copy link
Copy Markdown

Here is the summary of changes.

You are about to add 19 region tags.

This comment is generated by snippet-bot.
If you find problems with this result, please file an issue at:
https://github.com/googleapis/repo-automation-bots/issues.
To update this comment, add snippet-bot:force-run label or use the checkbox below:

  • Refresh this comment

}
// [END android_media_ai_enhancement_bitmap_viewmodel]

// Shims for Media Enhancement API types if not provided by standalone SDK

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these classes provided by an existing google library? If so, could we use the library instead of defining shims?

import android.widget.ImageView
import androidx.fragment.app.Fragment

class UltraHdrDisplayFragment : Fragment() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is more of a Fragment snippet, I'm wondering if we need to migrate this? Is there a compose-version of this code? or is this the only way to do it?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, these are good questions. The published Kotlin is the Views path: ImageView via view binding and requireActivity().window.colorMode. The Fragment wrapper is only there because requireActivity() is a Fragment API.

Looking into it more though, on the Overview page for this section there is a link to github resource on Displaying an Ultra HDR image in Compose. It seems to me that this section of the page needs work beyond the scope of the migration and should be added to the list of other snippets that need further work, leaving this snippet hardcoded for now. Does that sound OK?

@barbaralaw
barbaralaw marked this pull request as draft September 30, 2026 20:57
@barbaralaw

Copy link
Copy Markdown
Contributor Author

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!

kkuan2011 and others added 4 commits September 30, 2026 21:05
Alphabetize :media dependencies, rename MediaProjection.kt, replace the spatial-audio ellipsis sandwich with // ..., and fix ViewModel comment / HDR buffer indent plus Surface imports.
- Replace the compile-only shims with the published
  com.google.android.gms:play-services-media-effect-enhancement
  library, pinned to 16.0.0-beta04 (the version the get-started page
  documents). Where page code doesn't compile against the real API,
  use the real API with the smallest visible change (review feedback).
- Remove the Surface-mode snapshot region. The page describes an API
  the library doesn't have, so that block stays hardcoded on DAC and
  is being reported to the page owner.
- Ultra HDR: make the host Fragment private, replace the stand-in
  binding class with a real generated ViewBinding (new layout,
  viewBinding enabled in :media) and use a hidden twin for the
  placeholder. Rendered output is unchanged.
- Make MediaProjectionActivity private.
- End natural-language comments inside regions with a full stop.
@barbaralaw
barbaralaw force-pushed the media2-snippet-migration branch from 4359abc to ac35f97 Compare September 30, 2026 21:11
Make the projection_start nullability fix visible, simplify createSession, move compile-only helpers out of the regions to the bottom of their files, and drop the unused media3 session and ui dependencies.
@barbaralaw barbaralaw changed the title Media2 snippet migration Migrate Media enhancement, HDR, projection, and spatial audio snippets Oct 2, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants