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

Commit 16509cc3 authored by Michael Mikhail's avatar Michael Mikhail
Browse files

[Flexiglass] remove unused helper classes

As we use media composable now, we do not need the helper classes used to make media view classes work with the AndroidView.

Flag: com.android.systemui.scene_container
Bug: 436384980
Test: build
Change-Id: I95d8f586ec9f1465913a4947f73c555a90f8d185
parent 6a42d662
Loading
Loading
Loading
Loading
+0 −118
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.media.controls.ui.util

import androidx.recyclerview.widget.DiffUtil
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.internal.logging.InstanceId
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import com.android.systemui.media.controls.ui.viewmodel.mediaControlViewModel
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.fail
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class MediaDiffUtilTest : SysuiTestCase() {

    private val kosmos = testKosmos()

    @Test
    fun newMediaControlAdded() {
        val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123))
        val oldList = listOf<MediaControlViewModel>()
        val newList = listOf(mediaControl)
        val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
        val mediaLoadedListUpdateCallback =
            MediaViewModelListUpdateCallback(
                oldList,
                newList,
                { commonViewModel, _ -> assertThat(commonViewModel).isEqualTo(mediaControl) },
                { commonViewModel, _ -> fail("Unexpected to update $commonViewModel") },
                { fail("Unexpected to remove $it") },
                { commonViewModel, _, _ -> fail("Unexpected to move $commonViewModel ") },
            )

        DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
    }

    @Test
    fun updateMediaControl_contentChanged() {
        val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123))
        val oldList = listOf(mediaControl)
        val newList = listOf(mediaControl.copy())
        val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
        val mediaLoadedListUpdateCallback =
            MediaViewModelListUpdateCallback(
                oldList,
                newList,
                { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") },
                { controlViewModel, _ -> assertThat(controlViewModel).isNotEqualTo(mediaControl) },
                { fail("Unexpected to remove $it") },
                { controlViewModel, _, _ -> fail("Unexpected to move $controlViewModel ") },
            )

        DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
    }

    @Test
    fun mediaControlMoved() {
        val mediaControl1 = createMediaControl(InstanceId.fakeInstanceId(123))
        val mediaControl2 = createMediaControl(InstanceId.fakeInstanceId(456))
        val oldList = listOf(mediaControl1, mediaControl2)
        val newList = listOf(mediaControl2, mediaControl1)
        val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
        val mediaLoadedListUpdateCallback =
            MediaViewModelListUpdateCallback(
                oldList,
                newList,
                { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") },
                { controlViewModel, _ -> fail("Unexpected to update $controlViewModel") },
                { fail("Unexpected to remove $it") },
                { controlViewModel, _, _ -> assertThat(controlViewModel).isEqualTo(mediaControl1) },
            )

        DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
    }

    @Test
    fun mediaControlRemoved() {
        val mediaControl = createMediaControl(InstanceId.fakeInstanceId(123))
        val oldList = listOf(mediaControl)
        val newList = listOf<MediaControlViewModel>()
        val mediaLoadedCallback = MediaViewModelCallback(oldList, newList)
        val mediaLoadedListUpdateCallback =
            MediaViewModelListUpdateCallback(
                oldList,
                newList,
                { controlViewModel, _ -> fail("Unexpected to add $controlViewModel") },
                { controlViewModel, _ -> fail("Unexpected to update $controlViewModel") },
                { controlViewModel -> assertThat(controlViewModel).isEqualTo(mediaControl) },
                { controlViewModel, _, _ -> fail("Unexpected to move $controlViewModel ") },
            )

        DiffUtil.calculateDiff(mediaLoadedCallback).dispatchUpdatesTo(mediaLoadedListUpdateCallback)
    }

    private fun createMediaControl(instanceId: InstanceId): MediaControlViewModel {
        return kosmos.mediaControlViewModel.copy(instanceId = instanceId)
    }
}
+0 −106
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.media.controls.ui.util

import android.app.WallpaperColors
import android.content.Context
import android.content.pm.PackageManager
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.util.Log
import com.android.systemui.media.controls.ui.animation.backgroundFromScheme
import com.android.systemui.monet.ColorScheme
import com.android.systemui.monet.Style
import com.android.systemui.util.getColorWithAlpha
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.withContext

object MediaArtworkHelper {

    /**
     * This method should be called from a background thread. WallpaperColors.fromBitmap takes a
     * good amount of time. We do that work on the background executor to avoid stalling animations
     * on the UI Thread.
     */
    suspend fun getWallpaperColor(
        applicationContext: Context,
        backgroundDispatcher: CoroutineDispatcher,
        artworkIcon: Icon?,
        tag: String,
    ): WallpaperColors? =
        withContext(backgroundDispatcher) {
            return@withContext artworkIcon?.let {
                if (it.type == Icon.TYPE_BITMAP || it.type == Icon.TYPE_ADAPTIVE_BITMAP) {
                    // Avoids extra processing if this is already a valid bitmap
                    it.bitmap.let { artworkBitmap ->
                        if (artworkBitmap.isRecycled) {
                            Log.d(tag, "Cannot load wallpaper color from a recycled bitmap")
                            null
                        } else {
                            WallpaperColors.fromBitmap(artworkBitmap)
                        }
                    }
                } else {
                    it.loadDrawable(applicationContext)?.let { artworkDrawable ->
                        WallpaperColors.fromDrawable(artworkDrawable)
                    }
                }
            }
        }

    /**
     * Returns a scaled [Drawable] of a given [Icon] centered in [width]x[height] background size.
     */
    fun getScaledBackground(context: Context, icon: Icon, width: Int, height: Int): Drawable? {
        val drawable = icon.loadDrawable(context)
        val bounds = Rect(0, 0, width, height)
        if (bounds.width() > width || bounds.height() > height) {
            val offsetX = (bounds.width() - width) / 2.0f
            val offsetY = (bounds.height() - height) / 2.0f
            bounds.offset(-offsetX.toInt(), -offsetY.toInt())
        }
        drawable?.bounds = bounds
        return drawable
    }

    /** Adds [gradient] on a given [albumArt] drawable using [colorScheme]. */
    fun setUpGradientColorOnDrawable(
        albumArt: Drawable?,
        gradient: GradientDrawable,
        colorScheme: ColorScheme,
        startAlpha: Float,
        endAlpha: Float,
    ): LayerDrawable {
        val color = backgroundFromScheme(colorScheme)
        gradient.colors =
            intArrayOf(getColorWithAlpha(color, startAlpha), getColorWithAlpha(color, endAlpha))
        return LayerDrawable(arrayOf(albumArt, gradient))
    }

    /** Returns [ColorScheme] of media app given its [icon]. */
    fun getColorScheme(icon: Drawable, tag: String, darkTheme: Boolean): ColorScheme? {
        return try {
            ColorScheme(WallpaperColors.fromDrawable(icon), darkTheme, Style.CONTENT)
        } catch (e: PackageManager.NameNotFoundException) {
            Log.w(tag, "Fail to get media app info", e)
            null
        }
    }
}
+0 −47
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.media.controls.ui.util

import androidx.recyclerview.widget.DiffUtil
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel

/** A [DiffUtil.Callback] to calculate difference between old and new media view-model list. */
class MediaViewModelCallback(
    private val old: List<MediaControlViewModel>,
    private val new: List<MediaControlViewModel>,
) : DiffUtil.Callback() {

    override fun getOldListSize(): Int {
        return old.size
    }

    override fun getNewListSize(): Int {
        return new.size
    }

    override fun areItemsTheSame(oldIndex: Int, newIndex: Int): Boolean {
        val oldItem = old[oldIndex]
        val newItem = new[newIndex]
        return oldItem.instanceId == newItem.instanceId
    }

    override fun areContentsTheSame(oldIndex: Int, newIndex: Int): Boolean {
        val oldItem = old[oldIndex]
        val newItem = new[newIndex]
        return oldItem.updateTime == newItem.updateTime
    }
}
+0 −54
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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.
 */

package com.android.systemui.media.controls.ui.util

import androidx.recyclerview.widget.ListUpdateCallback
import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel
import kotlin.math.min

/** A [ListUpdateCallback] to apply media events needed to reach the new state. */
class MediaViewModelListUpdateCallback(
    private val old: List<MediaControlViewModel>,
    private val new: List<MediaControlViewModel>,
    private val onAdded: (MediaControlViewModel, Int) -> Unit,
    private val onUpdated: (MediaControlViewModel, Int) -> Unit,
    private val onRemoved: (MediaControlViewModel) -> Unit,
    private val onMoved: (MediaControlViewModel, Int, Int) -> Unit,
) : ListUpdateCallback {

    override fun onInserted(position: Int, count: Int) {
        for (i in position until position + count) {
            onAdded(new[i], i)
        }
    }

    override fun onRemoved(position: Int, count: Int) {
        for (i in position until position + count) {
            onRemoved(old[i])
        }
    }

    override fun onMoved(fromPosition: Int, toPosition: Int) {
        onMoved(old[fromPosition], fromPosition, toPosition)
    }

    override fun onChanged(position: Int, count: Int, payload: Any?) {
        for (i in position until min(position + count, new.size)) {
            onUpdated(new[i], position)
        }
    }
}