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

Commit 629f3cb0 authored by cecilia's avatar cecilia
Browse files

Add Smartspace media card in media carousel when the Ss data is available.

Test: Tested manually on local builds
Bug: 182813345, 181365922
Change-Id: I143b8df12f9ca65936f4871a7a8985ef9495b61e
parent b5e420af
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