Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8a78dcb authored by Cecilia Hong's avatar Cecilia Hong Committed by Android (Google) Code Review
Browse files

Merge "Add Smartspace media card in media carousel when the Ss data is available." into sc-dev

parents 34440439 629f3cb0
Loading
Loading
Loading
Loading
+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
+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
+6 −0
Original line number Diff line number Diff line
@@ -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>
@@ -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>
+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>
+65 −15
Original line number Diff line number Diff line
@@ -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) {
@@ -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 { _, _, _, _, _, _, _, _, _ ->
@@ -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()
@@ -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()
@@ -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()
@@ -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()
            }
        }
    }

@@ -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 }
@@ -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