Skip to content

Add legacy service preservation and feature flag toggle infrastructure#5145

Open
sztomek wants to merge 1 commit intomedia3/04-session-callbacksfrom
media3/05-legacy-service
Open

Add legacy service preservation and feature flag toggle infrastructure#5145
sztomek wants to merge 1 commit intomedia3/04-session-callbacksfrom
media3/05-legacy-service

Conversation

@sztomek
Copy link
Contributor

@sztomek sztomek commented Mar 18, 2026

Description

This is the 5th step of the media3 migration. This PR preserves the existing PlaybackService logic by extracting it into LegacyPlaybackService (a MediaBrowserServiceCompat) and its thin LegacyAutoPlaybackService subclass, so the old service can continue to run alongside the new Media3 implementation. A PlaybackServiceToggle utility was added to resolve the correct service class based on the MEDIA3_SESSION feature flag, enabling a safe runtime switch between legacy and Media3 paths.
A ProGuard keep rule was also added to ensure the legacy service class isn't stripped during minification.

Testing Instructions

Just review the code since the new components are not wired up yet

Checklist

  • If this is a user-facing change, I have added an entry in CHANGELOG.md
  • Ensure the linter passes (./gradlew spotlessApply to automatically apply formatting/linting)
  • I have considered whether it makes sense to add tests for my changes
  • All strings that need to be localized are in modules/services/localization/src/main/res/values/strings.xml
  • Any jetpack compose components I added or changed are covered by compose previews
  • I have updated (or requested that someone edit) the spreadsheet to reflect any new or changed analytics.

@sztomek sztomek added this to the 8.10 milestone Mar 18, 2026
@sztomek sztomek requested a review from a team as a code owner March 18, 2026 10:35
@sztomek sztomek removed the request for review from a team March 18, 2026 10:35
@sztomek sztomek requested a review from MiSikora March 18, 2026 10:35
@sztomek sztomek added the [Type] Enhancement Improve an existing feature. label Mar 18, 2026
Copilot AI review requested due to automatic review settings March 18, 2026 10:35
@sztomek sztomek added the [Area] Playback Episode playback issue label Mar 18, 2026
@dangermattic
Copy link
Collaborator

3 Errors
🚫 Please add tests for class LegacyPlaybackService (or add unit-tests-exemption label to ignore this).
🚫 Please add tests for class MediaControllerCallback (or add unit-tests-exemption label to ignore this).
🚫 This PR is tagged with do not merge label(s).

Generated by 🚫 Danger

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR is part of the Media3 migration, preserving the existing MediaBrowserServiceCompat playback service implementation under new “legacy” service classes and adding a feature-flag-based toggle to switch between legacy and Media3 service components at runtime.

Changes:

  • Extracted legacy playback service logic into LegacyPlaybackService and added an automotive-specific LegacyAutoPlaybackService subclass.
  • Added PlaybackServiceToggle to enable/disable the appropriate service components based on the MEDIA3_SESSION feature flag.
  • Added a ProGuard keep rule intended to retain the legacy service during minification.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
proguard-rules.pro Adds a keep rule for the new legacy playback service class.
modules/services/repositories/.../PlaybackServiceToggle.kt Introduces runtime component enable/disable logic based on a feature flag.
modules/services/repositories/.../LegacyPlaybackService.kt New legacy MediaBrowserServiceCompat implementation extracted from existing playback service logic.
automotive/.../LegacyAutoPlaybackService.kt Automotive-specific legacy service subclass with refresh/pause behavior.

You can also share your feedback on Copilot code review. Take the survey.

Comment on lines +9 to +19
object PlaybackServiceToggle {

fun ensureCorrectServiceEnabled(
context: Context,
media3Services: List<String> = listOf(PlaybackService::class.java.name),
legacyServices: List<String> = listOf(LegacyPlaybackService::class.java.name),
) {
val useMedia3 = FeatureFlag.isEnabled(Feature.MEDIA3_SESSION)
media3Services.forEach { setComponentEnabled(context, it, useMedia3) }
legacyServices.forEach { setComponentEnabled(context, it, !useMedia3) }
}
Comment on lines +277 to +282
val removeNotification = state != PlaybackStateCompat.STATE_PAUSED || settings.hideNotificationOnPause.value
if (removeNotification || isForegroundService) {
val isTransientLoss = playbackManager.playbackStateRelay.blockingFirst().transientLoss
if (isTransientLoss) {
return
}
Comment on lines +321 to +330
private fun observeSleepTimer() {
sleepTimer.stateFlow
.onEach { state ->
onSleepTimerStateChange(state)
}
.catch { throwable ->
Timber.e(throwable, "Error observing SleepTimer state")
}
.launchIn(this)
}
# https://github.com/shiftyjelly/pocketcasts-android/issues/1656
# https://github.com/shiftyjelly/pocketcasts-android/pulls/2921
-keep,allowobfuscation,allowshrinking class au.com.shiftyjelly.pocketcasts.repositories.playback.PlaybackService { *; }
-keep,allowobfuscation,allowshrinking class au.com.shiftyjelly.pocketcasts.repositories.playback.LegacyPlaybackService { *; }
Comment on lines +21 to +36
private fun setComponentEnabled(context: Context, className: String, enabled: Boolean) {
val componentName = ComponentName(context.packageName, className)
val desiredState = if (enabled) {
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
} else {
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
}

val currentState = context.packageManager.getComponentEnabledSetting(componentName)
if (currentState != desiredState) {
context.packageManager.setComponentEnabledSetting(
componentName,
desiredState,
PackageManager.DONT_KILL_APP,
)
}
import timber.log.Timber

@AndroidEntryPoint
class LegacyAutoPlaybackService : LegacyPlaybackService() {

Check failure

Code scanning / Android Lint

Class is not registered in the manifest Error

The <service> au.com.shiftyjelly.pocketcasts.LegacyAutoPlaybackService is not registered in the manifest
}

Timber.d("Playback Notification State Change $state")
when (state) {

Check warning

Code scanning / Android Lint

Missing @IntDef in Switch Warning

Switch statement on an int with known associated constant missing case PlaybackStateCompat.STATE_CONNECTING, PlaybackStateCompat.STATE_FAST_FORWARDING, PlaybackStateCompat.STATE_REWINDING, PlaybackStateCompat.STATE_SKIPPING_TO_NEXT, PlaybackStateCompat.STATE_SKIPPING_TO_PREVIOUS, PlaybackStateCompat.STATE_SKIPPING_TO_QUEUE_ITEM
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Area] Playback Episode playback issue do not merge [Type] Enhancement Improve an existing feature.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants