Merge "Merge "Do not dismiss notification, flip activity flag" into rvc-dev am: fe16654f21 am: 8d4b42a069 am: 73cf407d05" into rvc-qpr-dev-plus-aosp

This commit is contained in:
Automerger Merge Worker
2020-06-18 21:18:48 +00:00
committed by Android (Google) Code Review
2 changed files with 176 additions and 19 deletions

View File

@@ -38,18 +38,20 @@ import android.service.notification.StatusBarNotification
import android.text.TextUtils
import android.util.Log
import com.android.internal.graphics.ColorUtils
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.NotificationMediaManager
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.util.Assert
import com.android.systemui.util.Utils
import java.io.FileDescriptor
import java.io.IOException
import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
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.
*/
@Singleton
class MediaDataManager @Inject constructor(
class MediaDataManager(
private val context: Context,
private val mediaControllerFactory: MediaControllerFactory,
private val notificationEntryManager: NotificationEntryManager,
@Background private val backgroundExecutor: Executor,
@Main private val foregroundExecutor: Executor,
broadcastDispatcher: BroadcastDispatcher,
private val mediaControllerFactory: MediaControllerFactory,
private val broadcastDispatcher: BroadcastDispatcher,
dumpManager: DumpManager,
mediaTimeoutListener: MediaTimeoutListener,
mediaResumeListener: MediaResumeListener
) {
mediaResumeListener: MediaResumeListener,
private val useMediaResumption: Boolean,
private val useQsMediaPlayer: Boolean
) : Dumpable {
private val listeners: MutableSet<Listener> = mutableSetOf()
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() {
override fun onReceive(context: Context, intent: Intent) {
@@ -128,6 +145,7 @@ class MediaDataManager @Inject constructor(
}
init {
dumpManager.registerDumpable(TAG, this)
mediaTimeoutListener.timeoutCallback = { token: String, timedOut: Boolean ->
setTimedOut(token, timedOut) }
addListener(mediaTimeoutListener)
@@ -159,8 +177,13 @@ class MediaDataManager @Inject constructor(
context.registerReceiver(appChangeReceiver, uninstallFilter)
}
fun destroy() {
context.unregisterReceiver(appChangeReceiver)
broadcastDispatcher.unregisterReceiver(userChangeReceiver)
}
fun onNotificationAdded(key: String, sbn: StatusBarNotification) {
if (Utils.useQsMediaPlayer(context) && isMediaNotification(sbn)) {
if (useQsMediaPlayer && isMediaNotification(sbn)) {
Assert.isMainThread()
val oldKey = findExistingEntry(key, sbn.packageName)
if (oldKey == null) {
@@ -253,18 +276,18 @@ class MediaDataManager @Inject constructor(
*/
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) {
mediaEntries[token]?.let {
if (Utils.useMediaResumption(context)) {
if (it.active == !timedOut) {
return
}
it.active = !timedOut
onMediaDataLoaded(token, token, it)
} else if (timedOut) {
notificationEntryManager.removeNotification(it.notificationKey, null /* ranking */,
UNDEFINED_DISMISS_REASON)
if (it.active == !timedOut) {
return
}
it.active = !timedOut
onMediaDataLoaded(token, token, it)
}
}
@@ -570,4 +593,12 @@ class MediaDataManager @Inject constructor(
*/
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")
}
}
}

View File

@@ -0,0 +1,126 @@
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))
}
}