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

Commit fe16654f authored by Lucas Dupin's avatar Lucas Dupin Committed by Android (Google) Code Review
Browse files

Merge "Do not dismiss notification, flip activity flag" into rvc-dev

parents e93d99c6 ae437a10
Loading
Loading
Loading
Loading
+50 −19
Original line number Original line Diff line number Diff line
@@ -38,18 +38,20 @@ import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.text.TextUtils
import android.util.Log
import android.util.Log
import com.android.internal.graphics.ColorUtils
import com.android.internal.graphics.ColorUtils
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.NotificationMediaManager
import com.android.systemui.statusbar.notification.MediaNotificationProcessor
import com.android.systemui.statusbar.notification.MediaNotificationProcessor
import com.android.systemui.statusbar.notification.NotificationEntryManager
import com.android.systemui.statusbar.notification.NotificationEntryManager.UNDEFINED_DISMISS_REASON
import com.android.systemui.statusbar.notification.row.HybridGroupManager
import com.android.systemui.statusbar.notification.row.HybridGroupManager
import com.android.systemui.util.Assert
import com.android.systemui.util.Assert
import com.android.systemui.util.Utils
import com.android.systemui.util.Utils
import java.io.FileDescriptor
import java.io.IOException
import java.io.IOException
import java.io.PrintWriter
import java.util.concurrent.Executor
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Inject
import javax.inject.Singleton
import javax.inject.Singleton
@@ -85,20 +87,35 @@ fun isMediaNotification(sbn: StatusBarNotification): Boolean {
 * A class that facilitates management and loading of Media Data, ready for binding.
 * A class that facilitates management and loading of Media Data, ready for binding.
 */
 */
@Singleton
@Singleton
class MediaDataManager @Inject constructor(
class MediaDataManager(
    private val context: Context,
    private val context: Context,
    private val mediaControllerFactory: MediaControllerFactory,
    private val notificationEntryManager: NotificationEntryManager,
    @Background private val backgroundExecutor: Executor,
    @Background private val backgroundExecutor: Executor,
    @Main private val foregroundExecutor: Executor,
    @Main private val foregroundExecutor: Executor,
    broadcastDispatcher: BroadcastDispatcher,
    private val mediaControllerFactory: MediaControllerFactory,
    private val broadcastDispatcher: BroadcastDispatcher,
    dumpManager: DumpManager,
    mediaTimeoutListener: MediaTimeoutListener,
    mediaTimeoutListener: MediaTimeoutListener,
    mediaResumeListener: MediaResumeListener
    mediaResumeListener: MediaResumeListener,
) {
    private val useMediaResumption: Boolean,
    private val useQsMediaPlayer: Boolean
) : Dumpable {


    private val listeners: MutableSet<Listener> = mutableSetOf()
    private val listeners: MutableSet<Listener> = mutableSetOf()
    private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
    private val mediaEntries: LinkedHashMap<String, MediaData> = LinkedHashMap()
    private val useMediaResumption: Boolean = Utils.useMediaResumption(context)

    @Inject
    constructor(
        context: Context,
        @Background backgroundExecutor: Executor,
        @Main foregroundExecutor: Executor,
        mediaControllerFactory: MediaControllerFactory,
        dumpManager: DumpManager,
        broadcastDispatcher: BroadcastDispatcher,
        mediaTimeoutListener: MediaTimeoutListener,
        mediaResumeListener: MediaResumeListener
    ) : this(context, backgroundExecutor, foregroundExecutor, mediaControllerFactory,
            broadcastDispatcher, dumpManager, mediaTimeoutListener, mediaResumeListener,
            Utils.useMediaResumption(context), Utils.useQsMediaPlayer(context))


    private val userChangeReceiver = object : BroadcastReceiver() {
    private val userChangeReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
        override fun onReceive(context: Context, intent: Intent) {
@@ -128,6 +145,7 @@ class MediaDataManager @Inject constructor(
    }
    }


    init {
    init {
        dumpManager.registerDumpable(TAG, this)
        mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean ->
        mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean ->
            setTimedOut(token, timedOut) }
            setTimedOut(token, timedOut) }
        addListener(mediaTimeoutListener)
        addListener(mediaTimeoutListener)
@@ -159,8 +177,13 @@ class MediaDataManager @Inject constructor(
        context.registerReceiver(appChangeReceiver, uninstallFilter)
        context.registerReceiver(appChangeReceiver, uninstallFilter)
    }
    }


    fun destroy() {
        context.unregisterReceiver(appChangeReceiver)
        broadcastDispatcher.unregisterReceiver(userChangeReceiver)
    }

    fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
    fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
        if (Utils.useQsMediaPlayer(context) && isMediaNotification(sbn)) {
        if (useQsMediaPlayer && isMediaNotification(sbn)) {
            Assert.isMainThread()
            Assert.isMainThread()
            val oldKey = findExistingEntry(key, sbn.packageName)
            val oldKey = findExistingEntry(key, sbn.packageName)
            if (oldKey == null) {
            if (oldKey == null) {
@@ -253,18 +276,18 @@ class MediaDataManager @Inject constructor(
     */
     */
    fun removeListener(listener: Listener) = listeners.remove(listener)
    fun removeListener(listener: Listener) = listeners.remove(listener)


    /**
     * Called whenever the player has been paused or stopped for a while.
     * This will make the player not active anymore, hiding it from QQS and Keyguard.
     * @see MediaData.active
     */
    private fun setTimedOut(token: String, timedOut: Boolean) {
    private fun setTimedOut(token: String, timedOut: Boolean) {
        mediaEntries[token]?.let {
        mediaEntries[token]?.let {
            if (Utils.useMediaResumption(context)) {
            if (it.active == !timedOut) {
            if (it.active == !timedOut) {
                return
                return
            }
            }
            it.active = !timedOut
            it.active = !timedOut
            onMediaDataLoaded(token, token, it)
            onMediaDataLoaded(token, token, it)
            } else if (timedOut) {
                notificationEntryManager.removeNotification(it.notificationKey, null /* ranking */,
                        UNDEFINED_DISMISS_REASON)
            }
        }
        }
    }
    }


@@ -570,4 +593,12 @@ class MediaDataManager @Inject constructor(
         */
         */
        fun onMediaDataRemoved(key: String) {}
        fun onMediaDataRemoved(key: String) {}
    }
    }

    override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<out String>) {
        pw.apply {
            println("listeners: $listeners")
            println("mediaEntries: $mediaEntries")
            println("useMediaResumption: $useMediaResumption")
        }
    }
}
}
+126 −0
Original line number Original line Diff line number Diff line
package com.android.systemui.media

import android.app.Notification
import android.service.notification.StatusBarNotification
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dump.DumpManager
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import java.util.concurrent.Executor
import org.mockito.Mockito.`when` as whenever

private const val KEY = "KEY"
private const val PACKAGE_NAME = "com.android.systemui"

private fun <T> eq(value: T): T = Mockito.eq(value) ?: value
private fun <T> anyObject(): T {
    return Mockito.anyObject<T>()
}

@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
class MediaDataManagerTest : SysuiTestCase() {

    @Mock lateinit var mediaControllerFactory: MediaControllerFactory
    @Mock lateinit var backgroundExecutor: Executor
    @Mock lateinit var foregroundExecutor: Executor
    @Mock lateinit var dumpManager: DumpManager
    @Mock lateinit var broadcastDispatcher: BroadcastDispatcher
    @Mock lateinit var mediaTimeoutListener: MediaTimeoutListener
    @Mock lateinit var mediaResumeListener: MediaResumeListener
    @JvmField @Rule val mockito = MockitoJUnit.rule()
    lateinit var mediaDataManager: MediaDataManager
    lateinit var mediaNotification: StatusBarNotification

    @Before
    fun setup() {
        mediaDataManager = MediaDataManager(context, backgroundExecutor, foregroundExecutor,
                mediaControllerFactory, broadcastDispatcher, dumpManager,
                mediaTimeoutListener, mediaResumeListener, useMediaResumption = true,
                useQsMediaPlayer = true)
        val sbn = mock(StatusBarNotification::class.java)
        val notification = mock(Notification::class.java)
        whenever(notification.hasMediaSession()).thenReturn(true)
        whenever(notification.notificationStyle).thenReturn(Notification.MediaStyle::class.java)
        whenever(sbn.notification).thenReturn(notification)
        whenever(sbn.packageName).thenReturn(PACKAGE_NAME)
        mediaNotification = sbn
    }

    @After
    fun tearDown() {
        mediaDataManager.destroy()
    }

    @Test
    fun testHasActiveMedia() {
        assertThat(mediaDataManager.hasActiveMedia()).isFalse()
        val data = mock(MediaData::class.java)

        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
        mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = data)
        assertThat(mediaDataManager.hasActiveMedia()).isFalse()

        whenever(data.active).thenReturn(true)
        assertThat(mediaDataManager.hasActiveMedia()).isTrue()
    }

    @Test
    fun testLoadsMetadataOnBackground() {
        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
        verify(backgroundExecutor).execute(anyObject())
    }

    @Test
    fun testOnMetaDataLoaded_callsListener() {
        val listener = mock(MediaDataManager.Listener::class.java)
        mediaDataManager.addListener(listener)
        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
        mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
        verify(listener).onMediaDataLoaded(eq(KEY), eq(null), anyObject())
    }

    @Test
    fun testHasAnyMedia_whenAddingMedia() {
        assertThat(mediaDataManager.hasAnyMedia()).isFalse()
        val data = mock(MediaData::class.java)

        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
        mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = data)
        assertThat(mediaDataManager.hasAnyMedia()).isTrue()
    }

    @Test
    fun testOnNotificationRemoved_doesntHaveMedia() {
        val data = mock(MediaData::class.java)
        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
        mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = data)
        mediaDataManager.onNotificationRemoved(KEY)
        assertThat(mediaDataManager.hasAnyMedia()).isFalse()
    }

    @Test
    fun testOnNotificationRemoved_callsListener() {
        val listener = mock(MediaDataManager.Listener::class.java)
        mediaDataManager.addListener(listener)
        mediaDataManager.onNotificationAdded(KEY, mediaNotification)
        mediaDataManager.onMediaDataLoaded(KEY, oldKey = null, data = mock(MediaData::class.java))
        mediaDataManager.onNotificationRemoved(KEY)

        verify(listener).onMediaDataRemoved(eq(KEY))
    }
}
 No newline at end of file