Loading packages/SystemUI/res/drawable/bg_smartspace_media_item.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2020 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. ~ You may obtain a copy of the License at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable law or agreed to in writing, software ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and ~ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@android:color/white" /> <corners android:radius="@dimen/qs_media_album_radius" /> </shape> No newline at end of file packages/SystemUI/res/layout/smartspace_card_media.xml 0 → 100644 +86 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2019 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. ~ You may obtain a copy of the License at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable law or agreed to in writing, software ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and ~ limitations under the License --> <com.android.systemui.util.animation.TransitionLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/media_recommendations" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="8dp" android:paddingBottom="8dp" android:clipChildren="false" android:clipToPadding="false" android:forceHasOverlappingRendering="false" android:background="@drawable/qs_media_background"> <ImageView android:id="@+id/media_cover1" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" android:adjustViewBounds="true" android:background="@drawable/bg_smartspace_media_item" android:clipToOutline="true" android:scaleType="centerCrop"/> <ImageView android:id="@+id/media_logo1" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" /> <ImageView android:id="@+id/media_cover2" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" android:adjustViewBounds="true" android:background="@drawable/bg_smartspace_media_item" android:clipToOutline="true" android:scaleType="centerCrop"/> <ImageView android:id="@+id/media_logo2" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" /> <ImageView android:id="@+id/media_cover3" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" android:adjustViewBounds="true" android:background="@drawable/bg_smartspace_media_item" android:clipToOutline="true" android:scaleType="centerCrop"/> <ImageView android:id="@+id/media_logo3" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" /> <ImageView android:id="@+id/media_cover4" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" android:adjustViewBounds="true" android:background="@drawable/bg_smartspace_media_item" android:clipToOutline="true" android:scaleType="centerCrop"/> <ImageView android:id="@+id/media_logo4" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" /> </com.android.systemui.util.animation.TransitionLayout> No newline at end of file packages/SystemUI/res/values/dimens.xml +6 −0 Original line number Diff line number Diff line Loading @@ -1232,6 +1232,7 @@ <dimen name="qs_media_padding">16dp</dimen> <dimen name="qs_media_panel_outer_padding">16dp</dimen> <dimen name="qs_media_album_size">120dp</dimen> <dimen name="qs_media_album_radius">14dp</dimen> <dimen name="qs_media_icon_size">16dp</dimen> <dimen name="qs_center_guideline_padding">10dp</dimen> <dimen name="qs_seamless_icon_size">@dimen/qs_media_icon_size</dimen> Loading @@ -1244,6 +1245,11 @@ <dimen name="qs_media_enabled_seekbar_vertical_padding">15dp</dimen> <dimen name="qs_media_disabled_seekbar_vertical_padding">16dp</dimen> <!-- Size of Smartspace media recommendations cards in the QSPanel carousel --> <dimen name="qs_aa_media_rec_album_size">80dp</dimen> <dimen name="qs_aa_media_rec_icon_size">20dp</dimen> <!-- Window magnification --> <dimen name="magnification_border_drag_size">35dp</dimen> <dimen name="magnification_outer_border_margin">15dp</dimen> Loading packages/SystemUI/res/xml/media_recommendation.xml 0 → 100644 +97 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2020 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. ~ You may obtain a copy of the License at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable law or agreed to in writing, software ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and ~ limitations under the License --> <ConstraintSet xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <Constraint android:id="@+id/media_cover1" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toStartOf="@id/media_cover2" app:layout_constraintHorizontal_weight="1" android:visibility="gone"/> <Constraint android:id="@+id/media_logo1" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" app:layout_constraintEnd_toEndOf="@+id/media_cover1" app:layout_constraintBottom_toBottomOf="@+id/media_cover1" android:visibility="gone" /> <Constraint android:id="@+id/media_cover2" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/media_cover1" app:layout_constraintEnd_toStartOf="@id/media_cover3" app:layout_constraintHorizontal_weight="1" android:visibility="gone"/> <Constraint android:id="@+id/media_logo2" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" app:layout_constraintEnd_toEndOf="@+id/media_cover2" app:layout_constraintBottom_toBottomOf="@+id/media_cover2" android:visibility="gone" /> <Constraint android:id="@+id/media_cover3" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/media_cover2" app:layout_constraintEnd_toStartOf="@id/media_cover4" app:layout_constraintHorizontal_weight="1" android:visibility="gone"/> <Constraint android:id="@+id/media_logo3" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" app:layout_constraintEnd_toEndOf="@+id/media_cover3" app:layout_constraintBottom_toBottomOf="@+id/media_cover3" android:visibility="gone" /> <Constraint android:id="@+id/media_cover4" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/media_cover3" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_weight="1" android:visibility="gone"/> <Constraint android:id="@+id/media_logo4" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" app:layout_constraintEnd_toEndOf="@+id/media_cover4" app:layout_constraintBottom_toBottomOf="@+id/media_cover4" android:visibility="gone" /> </ConstraintSet> packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +65 −15 Original line number Diff line number Diff line Loading @@ -204,7 +204,7 @@ class MediaCarouselController @Inject constructor( override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) { Log.d(TAG, "My Smartspace media update is here") addOrUpdateSmartspaceMediaRecommendations(key, data) addSmartspaceMediaRecommendations(key, data) } override fun onMediaDataRemoved(key: String) { Loading @@ -213,6 +213,7 @@ class MediaCarouselController @Inject constructor( override fun onSmartspaceMediaDataRemoved(key: String) { Log.d(TAG, "My Smartspace media removal request is received") removePlayer(key) } }) mediaFrame.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> Loading Loading @@ -256,8 +257,10 @@ class MediaCarouselController @Inject constructor( private fun reorderAllPlayers() { mediaContent.removeAllViews() for (mediaPlayer in MediaPlayerData.players()) { mediaPlayer.view?.let { mediaPlayer.playerViewHolder?.let { mediaContent.addView(it.player) } ?: mediaPlayer.recommendationViewHolder?.let { mediaContent.addView(it.recommendations) } } mediaCarouselScrollHandler.onPlayersChanged() Loading @@ -272,18 +275,19 @@ class MediaCarouselController @Inject constructor( val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey) if (existingPlayer == null) { var newPlayer = mediaControlPanelFactory.get() newPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) newPlayer.attachPlayer( PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) newPlayer.view?.player?.setLayoutParams(lp) newPlayer.bind(dataCopy, key) newPlayer.playerViewHolder?.player?.setLayoutParams(lp) newPlayer.bindPlayer(dataCopy, key) newPlayer.setListening(currentlyExpanded) MediaPlayerData.addMediaPlayer(key, dataCopy, newPlayer) updatePlayerToState(newPlayer, noAnimation = true) reorderAllPlayers() } else { existingPlayer.bind(dataCopy, key) existingPlayer.bindPlayer(dataCopy, key) MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer) if (visualStabilityManager.isReorderingAllowed) { reorderAllPlayers() Loading @@ -301,15 +305,43 @@ class MediaCarouselController @Inject constructor( } } private fun addOrUpdateSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) { // TODO(b/182813345): Add Smartspace media recommendation view. private fun addSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) { Log.d(TAG, "Updating smartspace target in carousel") if (MediaPlayerData.getMediaPlayer(key, null) != null) { Log.w(TAG, "Skip adding smartspace target in carousel") return } var newRecs = mediaControlPanelFactory.get() newRecs.attachRecommendation( RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent)) newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp) newRecs.bindRecommendation(data, bgColor, { v -> removePlayer(key) }) MediaPlayerData.addMediaPlayer(key, newRecs) updatePlayerToState(newRecs, noAnimation = true) reorderAllPlayers() updatePageIndicator() mediaCarousel.requiresRemeasuring = true // Check postcondition: mediaContent should have the same number of children as there are // elements in mediaPlayers. if (MediaPlayerData.players().size != mediaContent.childCount) { Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync") } } private fun removePlayer(key: String, dismissMediaData: Boolean = true) { private fun removePlayer( key: String, dismissMediaData: Boolean = true, dismissRecommendation: Boolean = true ) { val removed = MediaPlayerData.removeMediaPlayer(key) removed?.apply { mediaCarouselScrollHandler.onPrePlayerRemoved(removed) mediaContent.removeView(removed.view?.player) mediaContent.removeView(removed.playerViewHolder?.player) mediaContent.removeView(removed.recommendationViewHolder?.recommendations) removed.onDestroy() mediaCarouselScrollHandler.onPlayersChanged() updatePageIndicator() Loading @@ -318,6 +350,10 @@ class MediaCarouselController @Inject constructor( // Inform the media manager of a potentially late dismissal mediaManager.dismissMediaData(key, 0L) } if (dismissRecommendation) { // Inform the media manager of a potentially late dismissal mediaManager.dismissSmartspaceRecommendation() } } } Loading Loading @@ -539,13 +575,20 @@ 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) private data class MediaSortKey( // Is Smartspace media recommendation. When the Smartspace media is present, it should // always be the first card in carousel. val isSsMediaRec: Boolean, val data: MediaData, val updateTime: Long = 0 ) private val comparator = compareByDescending<MediaSortKey> { it.data.isPlaying } compareByDescending<MediaSortKey> { it.isSsMediaRec } .thenByDescending { it.data.isPlaying } .thenByDescending { it.data.isLocalSession } .thenByDescending { !it.data.resumption } .thenByDescending { it.updateTime } Loading @@ -555,7 +598,14 @@ internal object MediaPlayerData { fun addMediaPlayer(key: String, data: MediaData, player: MediaControlPanel) { removeMediaPlayer(key) val sortKey = MediaSortKey(data, System.currentTimeMillis()) val sortKey = MediaSortKey(isSsMediaRec = false, data, System.currentTimeMillis()) mediaData.put(key, sortKey) mediaPlayers.put(sortKey, player) } fun addMediaPlayer(key: String, player: MediaControlPanel) { removeMediaPlayer(key) val sortKey = MediaSortKey(isSsMediaRec = true, EMPTY, System.currentTimeMillis()) mediaData.put(key, sortKey) mediaPlayers.put(sortKey, player) } Loading Loading
packages/SystemUI/res/drawable/bg_smartspace_media_item.xml 0 → 100644 +21 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2020 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. ~ You may obtain a copy of the License at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable law or agreed to in writing, software ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and ~ limitations under the License. --> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <solid android:color="@android:color/white" /> <corners android:radius="@dimen/qs_media_album_radius" /> </shape> No newline at end of file
packages/SystemUI/res/layout/smartspace_card_media.xml 0 → 100644 +86 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2019 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. ~ You may obtain a copy of the License at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable law or agreed to in writing, software ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and ~ limitations under the License --> <com.android.systemui.util.animation.TransitionLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/media_recommendations" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="8dp" android:paddingBottom="8dp" android:clipChildren="false" android:clipToPadding="false" android:forceHasOverlappingRendering="false" android:background="@drawable/qs_media_background"> <ImageView android:id="@+id/media_cover1" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" android:adjustViewBounds="true" android:background="@drawable/bg_smartspace_media_item" android:clipToOutline="true" android:scaleType="centerCrop"/> <ImageView android:id="@+id/media_logo1" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" /> <ImageView android:id="@+id/media_cover2" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" android:adjustViewBounds="true" android:background="@drawable/bg_smartspace_media_item" android:clipToOutline="true" android:scaleType="centerCrop"/> <ImageView android:id="@+id/media_logo2" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" /> <ImageView android:id="@+id/media_cover3" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" android:adjustViewBounds="true" android:background="@drawable/bg_smartspace_media_item" android:clipToOutline="true" android:scaleType="centerCrop"/> <ImageView android:id="@+id/media_logo3" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" /> <ImageView android:id="@+id/media_cover4" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" android:adjustViewBounds="true" android:background="@drawable/bg_smartspace_media_item" android:clipToOutline="true" android:scaleType="centerCrop"/> <ImageView android:id="@+id/media_logo4" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" /> </com.android.systemui.util.animation.TransitionLayout> No newline at end of file
packages/SystemUI/res/values/dimens.xml +6 −0 Original line number Diff line number Diff line Loading @@ -1232,6 +1232,7 @@ <dimen name="qs_media_padding">16dp</dimen> <dimen name="qs_media_panel_outer_padding">16dp</dimen> <dimen name="qs_media_album_size">120dp</dimen> <dimen name="qs_media_album_radius">14dp</dimen> <dimen name="qs_media_icon_size">16dp</dimen> <dimen name="qs_center_guideline_padding">10dp</dimen> <dimen name="qs_seamless_icon_size">@dimen/qs_media_icon_size</dimen> Loading @@ -1244,6 +1245,11 @@ <dimen name="qs_media_enabled_seekbar_vertical_padding">15dp</dimen> <dimen name="qs_media_disabled_seekbar_vertical_padding">16dp</dimen> <!-- Size of Smartspace media recommendations cards in the QSPanel carousel --> <dimen name="qs_aa_media_rec_album_size">80dp</dimen> <dimen name="qs_aa_media_rec_icon_size">20dp</dimen> <!-- Window magnification --> <dimen name="magnification_border_drag_size">35dp</dimen> <dimen name="magnification_outer_border_margin">15dp</dimen> Loading
packages/SystemUI/res/xml/media_recommendation.xml 0 → 100644 +97 −0 Original line number Diff line number Diff line <?xml version="1.0" encoding="utf-8"?> <!-- ~ Copyright (C) 2020 The Android Open Source Project ~ ~ Licensed under the Apache License, Version 2.0 (the "License"); ~ you may not use this file except in compliance with the License. ~ You may obtain a copy of the License at ~ ~ http://www.apache.org/licenses/LICENSE-2.0 ~ ~ Unless required by applicable law or agreed to in writing, software ~ distributed under the License is distributed on an "AS IS" BASIS, ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ~ See the License for the specific language governing permissions and ~ limitations under the License --> <ConstraintSet xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <Constraint android:id="@+id/media_cover1" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toStartOf="@id/media_cover2" app:layout_constraintHorizontal_weight="1" android:visibility="gone"/> <Constraint android:id="@+id/media_logo1" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" app:layout_constraintEnd_toEndOf="@+id/media_cover1" app:layout_constraintBottom_toBottomOf="@+id/media_cover1" android:visibility="gone" /> <Constraint android:id="@+id/media_cover2" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/media_cover1" app:layout_constraintEnd_toStartOf="@id/media_cover3" app:layout_constraintHorizontal_weight="1" android:visibility="gone"/> <Constraint android:id="@+id/media_logo2" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" app:layout_constraintEnd_toEndOf="@+id/media_cover2" app:layout_constraintBottom_toBottomOf="@+id/media_cover2" android:visibility="gone" /> <Constraint android:id="@+id/media_cover3" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/media_cover2" app:layout_constraintEnd_toStartOf="@id/media_cover4" app:layout_constraintHorizontal_weight="1" android:visibility="gone"/> <Constraint android:id="@+id/media_logo3" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" app:layout_constraintEnd_toEndOf="@+id/media_cover3" app:layout_constraintBottom_toBottomOf="@+id/media_cover3" android:visibility="gone" /> <Constraint android:id="@+id/media_cover4" android:layout_width="@dimen/qs_aa_media_rec_album_size" android:layout_height="@dimen/qs_aa_media_rec_album_size" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toEndOf="@id/media_cover3" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_weight="1" android:visibility="gone"/> <Constraint android:id="@+id/media_logo4" android:layout_width="@dimen/qs_aa_media_rec_icon_size" android:layout_height="@dimen/qs_aa_media_rec_icon_size" app:layout_constraintEnd_toEndOf="@+id/media_cover4" app:layout_constraintBottom_toBottomOf="@+id/media_cover4" android:visibility="gone" /> </ConstraintSet>
packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt +65 −15 Original line number Diff line number Diff line Loading @@ -204,7 +204,7 @@ class MediaCarouselController @Inject constructor( override fun onSmartspaceMediaDataLoaded(key: String, data: SmartspaceTarget) { Log.d(TAG, "My Smartspace media update is here") addOrUpdateSmartspaceMediaRecommendations(key, data) addSmartspaceMediaRecommendations(key, data) } override fun onMediaDataRemoved(key: String) { Loading @@ -213,6 +213,7 @@ class MediaCarouselController @Inject constructor( override fun onSmartspaceMediaDataRemoved(key: String) { Log.d(TAG, "My Smartspace media removal request is received") removePlayer(key) } }) mediaFrame.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> Loading Loading @@ -256,8 +257,10 @@ class MediaCarouselController @Inject constructor( private fun reorderAllPlayers() { mediaContent.removeAllViews() for (mediaPlayer in MediaPlayerData.players()) { mediaPlayer.view?.let { mediaPlayer.playerViewHolder?.let { mediaContent.addView(it.player) } ?: mediaPlayer.recommendationViewHolder?.let { mediaContent.addView(it.recommendations) } } mediaCarouselScrollHandler.onPlayersChanged() Loading @@ -272,18 +275,19 @@ class MediaCarouselController @Inject constructor( val existingPlayer = MediaPlayerData.getMediaPlayer(key, oldKey) if (existingPlayer == null) { var newPlayer = mediaControlPanelFactory.get() newPlayer.attach(PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) newPlayer.attachPlayer( PlayerViewHolder.create(LayoutInflater.from(context), mediaContent)) newPlayer.mediaViewController.sizeChangedListener = this::updateCarouselDimensions val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) newPlayer.view?.player?.setLayoutParams(lp) newPlayer.bind(dataCopy, key) newPlayer.playerViewHolder?.player?.setLayoutParams(lp) newPlayer.bindPlayer(dataCopy, key) newPlayer.setListening(currentlyExpanded) MediaPlayerData.addMediaPlayer(key, dataCopy, newPlayer) updatePlayerToState(newPlayer, noAnimation = true) reorderAllPlayers() } else { existingPlayer.bind(dataCopy, key) existingPlayer.bindPlayer(dataCopy, key) MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer) if (visualStabilityManager.isReorderingAllowed) { reorderAllPlayers() Loading @@ -301,15 +305,43 @@ class MediaCarouselController @Inject constructor( } } private fun addOrUpdateSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) { // TODO(b/182813345): Add Smartspace media recommendation view. private fun addSmartspaceMediaRecommendations(key: String, data: SmartspaceTarget) { Log.d(TAG, "Updating smartspace target in carousel") if (MediaPlayerData.getMediaPlayer(key, null) != null) { Log.w(TAG, "Skip adding smartspace target in carousel") return } var newRecs = mediaControlPanelFactory.get() newRecs.attachRecommendation( RecommendationViewHolder.create(LayoutInflater.from(context), mediaContent)) newRecs.mediaViewController.sizeChangedListener = this::updateCarouselDimensions val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) newRecs.recommendationViewHolder?.recommendations?.setLayoutParams(lp) newRecs.bindRecommendation(data, bgColor, { v -> removePlayer(key) }) MediaPlayerData.addMediaPlayer(key, newRecs) updatePlayerToState(newRecs, noAnimation = true) reorderAllPlayers() updatePageIndicator() mediaCarousel.requiresRemeasuring = true // Check postcondition: mediaContent should have the same number of children as there are // elements in mediaPlayers. if (MediaPlayerData.players().size != mediaContent.childCount) { Log.wtf(TAG, "Size of players list and number of views in carousel are out of sync") } } private fun removePlayer(key: String, dismissMediaData: Boolean = true) { private fun removePlayer( key: String, dismissMediaData: Boolean = true, dismissRecommendation: Boolean = true ) { val removed = MediaPlayerData.removeMediaPlayer(key) removed?.apply { mediaCarouselScrollHandler.onPrePlayerRemoved(removed) mediaContent.removeView(removed.view?.player) mediaContent.removeView(removed.playerViewHolder?.player) mediaContent.removeView(removed.recommendationViewHolder?.recommendations) removed.onDestroy() mediaCarouselScrollHandler.onPlayersChanged() updatePageIndicator() Loading @@ -318,6 +350,10 @@ class MediaCarouselController @Inject constructor( // Inform the media manager of a potentially late dismissal mediaManager.dismissMediaData(key, 0L) } if (dismissRecommendation) { // Inform the media manager of a potentially late dismissal mediaManager.dismissSmartspaceRecommendation() } } } Loading Loading @@ -539,13 +575,20 @@ 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) private data class MediaSortKey( // Is Smartspace media recommendation. When the Smartspace media is present, it should // always be the first card in carousel. val isSsMediaRec: Boolean, val data: MediaData, val updateTime: Long = 0 ) private val comparator = compareByDescending<MediaSortKey> { it.data.isPlaying } compareByDescending<MediaSortKey> { it.isSsMediaRec } .thenByDescending { it.data.isPlaying } .thenByDescending { it.data.isLocalSession } .thenByDescending { !it.data.resumption } .thenByDescending { it.updateTime } Loading @@ -555,7 +598,14 @@ internal object MediaPlayerData { fun addMediaPlayer(key: String, data: MediaData, player: MediaControlPanel) { removeMediaPlayer(key) val sortKey = MediaSortKey(data, System.currentTimeMillis()) val sortKey = MediaSortKey(isSsMediaRec = false, data, System.currentTimeMillis()) mediaData.put(key, sortKey) mediaPlayers.put(sortKey, player) } fun addMediaPlayer(key: String, player: MediaControlPanel) { removeMediaPlayer(key) val sortKey = MediaSortKey(isSsMediaRec = true, EMPTY, System.currentTimeMillis()) mediaData.put(key, sortKey) mediaPlayers.put(sortKey, player) } Loading