diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt index 24503675ae369..7a7198fc92339 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt @@ -115,6 +115,7 @@ class MediaCarouselController @Inject constructor( private var needsReordering: Boolean = false private var keysNeedRemoval = mutableSetOf() private var bgColor = getBackgroundColor() + private var shouldScrollToActivePlayer: Boolean = false private var isRtl: Boolean = false set(value) { if (value != field) { @@ -271,6 +272,15 @@ class MediaCarouselController @Inject constructor( } } mediaCarouselScrollHandler.onPlayersChanged() + + // Automatically scroll to the active player if needed + if (shouldScrollToActivePlayer) { + shouldScrollToActivePlayer = false + val activeMediaIndex = MediaPlayerData.getActiveMediaIndex() + if (activeMediaIndex != -1) { + mediaCarouselScrollHandler.scrollToActivePlayer(activeMediaIndex) + } + } } private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData) { @@ -322,8 +332,8 @@ class MediaCarouselController @Inject constructor( val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp) - newRecs.bindRecommendation(data, bgColor) - MediaPlayerData.addMediaPlayer(key, newRecs) + newRecs.bindRecommendation(data, bgColor, { v -> shouldScrollToActivePlayer = true }) + MediaPlayerData.addMediaRecommendation(key, newRecs) updatePlayerToState(newRecs, noAnimation = true) reorderAllPlayers() updatePageIndicator() @@ -593,7 +603,7 @@ class MediaCarouselController @Inject constructor( @VisibleForTesting internal object MediaPlayerData { private val EMPTY = MediaData(-1, false, 0, null, null, null, null, null, - emptyList(), emptyList(), "INVALID", null, null, null, true, null) + emptyList(), emptyList(), "INVALID", null, null, null, false, null) private data class MediaSortKey( // Is Smartspace media recommendation. When the Smartspace media is present, it should @@ -620,7 +630,7 @@ internal object MediaPlayerData { mediaPlayers.put(sortKey, player) } - fun addMediaPlayer(key: String, player: MediaControlPanel) { + fun addMediaRecommendation(key: String, player: MediaControlPanel) { removeMediaPlayer(key) val sortKey = MediaSortKey(isSsMediaRec = true, EMPTY, System.currentTimeMillis()) mediaData.put(key, sortKey) @@ -643,6 +653,16 @@ internal object MediaPlayerData { fun players() = mediaPlayers.values + /** Returns the index of the first non-timeout media. */ + fun getActiveMediaIndex(): Int { + mediaPlayers.entries.forEachIndexed { index, e -> + if (e.key.data.active) { + return index + } + } + return -1 + } + @VisibleForTesting fun clear() { mediaData.clear() diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt index f0b78dde0ebd3..63580041c899b 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt +++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselScrollHandler.kt @@ -38,6 +38,7 @@ import com.android.systemui.util.concurrency.DelayableExecutor private const val FLING_SLOP = 1000000 private const val DISMISS_DELAY = 100L +private const val SCROLL_DELAY = 100L private const val RUBBERBAND_FACTOR = 0.2f private const val SETTINGS_BUTTON_TRANSLATION_FRACTION = 0.3f @@ -100,10 +101,11 @@ class MediaCarouselScrollHandler( private lateinit var settingsButton: View /** - * What's the currently active player index? + * What's the currently visible player index? */ - var activeMediaIndex: Int = 0 + var visibleMediaIndex: Int = 0 private set + /** * How much are we scrolled into the current media? */ @@ -129,7 +131,7 @@ class MediaCarouselScrollHandler( field = value // The player width has changed, let's update the scroll position to make sure // it's still at the same place - var newRelativeScroll = activeMediaIndex * playerWidthPlusPadding + var newRelativeScroll = visibleMediaIndex * playerWidthPlusPadding if (scrollIntoCurrentMedia > playerWidthPlusPadding) { newRelativeScroll += playerWidthPlusPadding - (scrollIntoCurrentMedia - playerWidthPlusPadding) @@ -457,12 +459,12 @@ class MediaCarouselScrollHandler( val wasScrolledIn = scrollIntoCurrentMedia != 0 scrollIntoCurrentMedia = scrollInAmount val nowScrolledIn = scrollIntoCurrentMedia != 0 - if (newIndex != activeMediaIndex || wasScrolledIn != nowScrolledIn) { - activeMediaIndex = newIndex + if (newIndex != visibleMediaIndex || wasScrolledIn != nowScrolledIn) { + visibleMediaIndex = newIndex closeGuts() updatePlayerVisibilities() } - val relativeLocation = activeMediaIndex.toFloat() + if (playerWidthPlusPadding > 0) + val relativeLocation = visibleMediaIndex.toFloat() + if (playerWidthPlusPadding > 0) scrollInAmount.toFloat() / playerWidthPlusPadding else 0f // Fix the location, because PageIndicator does not handle RTL internally val location = if (isRtl) { @@ -500,7 +502,7 @@ class MediaCarouselScrollHandler( val scrolledIn = scrollIntoCurrentMedia != 0 for (i in 0 until mediaContent.childCount) { val view = mediaContent.getChildAt(i) - val visible = (i == activeMediaIndex) || ((i == (activeMediaIndex + 1)) && scrolledIn) + val visible = (i == visibleMediaIndex) || ((i == (visibleMediaIndex + 1)) && scrolledIn) view.visibility = if (visible) View.VISIBLE else View.INVISIBLE } } @@ -511,12 +513,12 @@ class MediaCarouselScrollHandler( */ fun onPrePlayerRemoved(removed: MediaControlPanel) { val removedIndex = mediaContent.indexOfChild(removed.playerViewHolder?.player) - // If the removed index is less than the activeMediaIndex, then we need to decrement it. + // If the removed index is less than the visibleMediaIndex, then we need to decrement it. // RTL has no effect on this, because indices are always relative (start-to-end). // Update the index 'manually' since we won't always get a call to onMediaScrollingChanged - val beforeActive = removedIndex <= activeMediaIndex + val beforeActive = removedIndex <= visibleMediaIndex if (beforeActive) { - activeMediaIndex = Math.max(0, activeMediaIndex - 1) + visibleMediaIndex = Math.max(0, visibleMediaIndex - 1) } // If the removed media item is "left of" the active one (in an absolute sense), we need to // scroll the view to keep that player in view. This is because scroll position is always @@ -545,6 +547,17 @@ class MediaCarouselScrollHandler( scrollView.relativeScrollX = 0 } + fun scrollToActivePlayer(activePlayerIndex: Int) { + var destIndex = activePlayerIndex + destIndex = Math.min(mediaContent.getChildCount() - 1, destIndex) + val view = mediaContent.getChildAt(destIndex) + // We need to post this to wait for the active player becomes visible. + mainExecutor.executeDelayed({ + visibleMediaIndex = activePlayerIndex + scrollView.smoothScrollTo(view.left, scrollView.scrollY) + }, SCROLL_DELAY) + } + companion object { private val CONTENT_TRANSLATION = object : FloatPropertyCompat( "contentTranslation") { diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java index 1e9cc8cb08ccf..647e8bfa704a3 100644 --- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java @@ -462,7 +462,8 @@ public class MediaControlPanel { /** Bind this recommendation view based on the data given. */ public void bindRecommendation( @NonNull SmartspaceTarget target, - @NonNull int backgroundColor) { + @NonNull int backgroundColor, + @Nullable View.OnClickListener callback) { if (mRecommendationViewHolder == null) { return; } @@ -526,7 +527,7 @@ public class MediaControlPanel { setSmartspaceRecItemOnClickListener( mediaCoverImageView, recommendation, - null); + callback); if (uiComponentIndex < MEDIA_RECOMMENDATION_ITEMS_PER_ROW) { setVisibleAndAlpha(collapsedSet,