[Media TTT] Timeout the chip after 3 seconds.

(Also updates the media dagger module to use Lazys so we don't have to
update the module each time a constructor changes.)

Bug: 214274529
Bug: 211487971
Test: manual: verified chip disappears after 3 seconds
Test: atest MediaTttChipControllerCommonTest
Change-Id: Ib9425d93e28aa5f6b9071ac10e864114363283f4
This commit is contained in:
Caitlin Cassidy
2022-02-10 21:25:42 +00:00
parent e0394fb27b
commit 634d0eee2f
7 changed files with 108 additions and 38 deletions

View File

@@ -16,12 +16,7 @@
package com.android.systemui.media.dagger;
import android.content.Context;
import android.os.Handler;
import android.view.WindowManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaFlags;
import com.android.systemui.media.MediaHierarchyManager;
@@ -34,11 +29,8 @@ import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import java.util.Optional;
import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -101,13 +93,11 @@ public interface MediaModule {
@SysUISingleton
static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
MediaTttFlags mediaTttFlags,
CommandQueue commandQueue,
Context context,
WindowManager windowManager) {
Lazy<MediaTttChipControllerSender> controllerSenderLazy) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
return Optional.of(new MediaTttChipControllerSender(commandQueue, context, windowManager));
return Optional.of(controllerSenderLazy.get());
}
/** */
@@ -115,16 +105,11 @@ public interface MediaModule {
@SysUISingleton
static Optional<MediaTttChipControllerReceiver> providesMediaTttChipControllerReceiver(
MediaTttFlags mediaTttFlags,
CommandQueue commandQueue,
Context context,
WindowManager windowManager,
@Main Handler mainHandler) {
Lazy<MediaTttChipControllerReceiver> controllerReceiverLazy) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
return Optional.of(
new MediaTttChipControllerReceiver(
commandQueue, context, windowManager, mainHandler));
return Optional.of(controllerReceiverLazy.get());
}
/** */
@@ -132,14 +117,11 @@ public interface MediaModule {
@SysUISingleton
static Optional<MediaTttCommandLineHelper> providesMediaTttCommandLineHelper(
MediaTttFlags mediaTttFlags,
CommandRegistry commandRegistry,
Context context,
@Main Executor mainExecutor) {
Lazy<MediaTttCommandLineHelper> helperLazy) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
return Optional.of(
new MediaTttCommandLineHelper(commandRegistry, context, mainExecutor));
return Optional.of(helperLazy.get());
}
/** */
@@ -147,13 +129,12 @@ public interface MediaModule {
@SysUISingleton
static Optional<MediaMuteAwaitConnectionCli> providesMediaMuteAwaitConnectionCli(
MediaFlags mediaFlags,
CommandRegistry commandRegistry,
Context context
Lazy<MediaMuteAwaitConnectionCli> muteAwaitConnectionCliLazy
) {
if (!mediaFlags.areMuteAwaitConnectionsEnabled()) {
return Optional.empty();
}
return Optional.of(new MediaMuteAwaitConnectionCli(commandRegistry, context));
return Optional.of(muteAwaitConnectionCliLazy.get());
}
/** */

View File

@@ -25,8 +25,11 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import androidx.annotation.VisibleForTesting
import com.android.internal.widget.CachingIconView
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.concurrency.DelayableExecutor
/**
* A superclass controller that provides common functionality for showing chips on the sender device
@@ -38,6 +41,7 @@ import com.android.systemui.R
abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
internal val context: Context,
private val windowManager: WindowManager,
@Main private val mainExecutor: DelayableExecutor,
@LayoutRes private val chipLayoutRes: Int
) {
/** The window layout parameters we'll use when attaching the view to a window. */
@@ -56,6 +60,9 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
/** The chip view currently being displayed. Null if the chip is not being displayed. */
var chipView: ViewGroup? = null
/** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
var cancelChipViewTimeout: Runnable? = null
/**
* Displays the chip with the current state.
*
@@ -77,8 +84,11 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
if (oldChipView == null) {
windowManager.addView(chipView, windowLayoutParams)
}
}
// Cancel and re-set the chip timeout each time we get a new state.
cancelChipViewTimeout?.run()
cancelChipViewTimeout = mainExecutor.executeDelayed(this::removeChip, TIMEOUT_MILLIS)
}
/** Hides the chip. */
fun removeChip() {
@@ -118,3 +128,5 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
// Used in CTS tests UpdateMediaTapToTransferSenderDisplayTest and
// UpdateMediaTapToTransferReceiverDisplayTest
private const val WINDOW_TITLE = "Media Transfer Chip View"
@VisibleForTesting
const val TIMEOUT_MILLIS = 3000L

View File

@@ -18,7 +18,6 @@ package com.android.systemui.media.taptotransfer.receiver
import android.app.StatusBarManager
import android.content.Context
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
import android.os.Handler
@@ -27,10 +26,10 @@ import android.view.ViewGroup
import android.view.WindowManager
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
/**
@@ -43,9 +42,10 @@ class MediaTttChipControllerReceiver @Inject constructor(
commandQueue: CommandQueue,
context: Context,
windowManager: WindowManager,
mainExecutor: DelayableExecutor,
@Main private val mainHandler: Handler,
) : MediaTttChipControllerCommon<ChipStateReceiver>(
context, windowManager, R.layout.media_ttt_chip_receiver
context, windowManager, mainExecutor, R.layout.media_ttt_chip_receiver
) {
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferReceiverDisplay(

View File

@@ -27,8 +27,10 @@ import android.widget.TextView
import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.DelayableExecutor
import javax.inject.Inject
/**
@@ -40,8 +42,9 @@ class MediaTttChipControllerSender @Inject constructor(
commandQueue: CommandQueue,
context: Context,
windowManager: WindowManager,
@Main private val mainExecutor: DelayableExecutor,
) : MediaTttChipControllerCommon<ChipStateSender>(
context, windowManager, R.layout.media_ttt_chip
context, windowManager, mainExecutor, R.layout.media_ttt_chip
) {
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferSenderDisplay(

View File

@@ -25,7 +25,11 @@ import android.widget.ImageView
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -40,6 +44,9 @@ import org.mockito.MockitoAnnotations
class MediaTttChipControllerCommonTest : SysuiTestCase() {
private lateinit var controllerCommon: MediaTttChipControllerCommon<MediaTttChipState>
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
private lateinit var appIconDrawable: Drawable
@Mock
private lateinit var windowManager: WindowManager
@@ -48,7 +55,10 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
appIconDrawable = context.getDrawable(R.drawable.ic_cake)!!
controllerCommon = TestControllerCommon(context, windowManager)
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
controllerCommon = TestControllerCommon(context, windowManager, fakeExecutor)
}
@Test
@@ -67,6 +77,58 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
verify(windowManager, never()).addView(any(), any())
}
@Test
fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
controllerCommon.displayChip(getState())
reset(windowManager)
fakeClock.advanceTime(TIMEOUT_MILLIS - 1)
verify(windowManager, never()).removeView(any())
}
@Test
fun displayChip_chipDisappearsAfterTimeout() {
controllerCommon.displayChip(getState())
reset(windowManager)
fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
verify(windowManager).removeView(any())
}
@Test
fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
// First, display the chip
controllerCommon.displayChip(getState())
// After some time, re-display the chip
val waitTime = 1000L
fakeClock.advanceTime(waitTime)
controllerCommon.displayChip(getState())
// Wait until the timeout for the first display would've happened
fakeClock.advanceTime(TIMEOUT_MILLIS - waitTime + 1)
// Verify we didn't hide the chip
verify(windowManager, never()).removeView(any())
}
@Test
fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
// First, display the chip
controllerCommon.displayChip(getState())
// After some time, re-display the chip
fakeClock.advanceTime(1000L)
controllerCommon.displayChip(getState())
// Ensure we still hide the chip eventually
fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
verify(windowManager).removeView(any())
}
@Test
fun removeChip_chipRemoved() {
// First, add the chip
@@ -110,9 +172,10 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
inner class TestControllerCommon(
context: Context,
windowManager: WindowManager
) : MediaTttChipControllerCommon<MediaTttChipState>(
context, windowManager, R.layout.media_ttt_chip
windowManager: WindowManager,
@Main mainExecutor: DelayableExecutor,
) : MediaTttChipControllerCommon<MediaTttChipState>(
context, windowManager, mainExecutor, R.layout.media_ttt_chip
) {
override fun updateChipView(chipState: MediaTttChipState, currentChipView: ViewGroup) {
}

View File

@@ -30,8 +30,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -70,7 +72,12 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
context.setMockPackageManager(packageManager)
controllerReceiver = MediaTttChipControllerReceiver(
commandQueue, context, windowManager, Handler.getMain())
commandQueue,
context,
windowManager,
FakeExecutor(FakeSystemClock()),
Handler.getMain()
)
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
verify(commandQueue).addCallback(callbackCaptor.capture())

View File

@@ -31,8 +31,10 @@ import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -70,7 +72,9 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
)).thenReturn(applicationInfo)
context.setMockPackageManager(packageManager)
controllerSender = MediaTttChipControllerSender(commandQueue, context, windowManager)
controllerSender = MediaTttChipControllerSender(
commandQueue, context, windowManager, FakeExecutor(FakeSystemClock())
)
val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
verify(commandQueue).addCallback(callbackCaptor.capture())