[Chipbar] Have MediaTttSenderCoordinator be responsible for not hiding

in certain cases.

Bug: 245610654
Test: manual: Send a TRANSFER_TRIGGERED event then FAR_FROM_RECEIVER
event and verify the chip is still displayed (i.e. the removal request
is ignored)
Test: manual: Send a ALMOST_CLOSE event then FAR_FROM_RECEIVER event and
verify the chip is hidden
Test: atest MediaTttSenderCoordinatorTest
Test: atest ChipbarCoordinatorTest
Change-Id: I8cd33a65ec48f37aa83179b348f60916df3990a8

Change-Id: Ic1f1d2fcb09727ebc24d649a26dcea40ca8287b2
This commit is contained in:
Caitlin Shkuratov
2022-10-12 17:19:39 +00:00
parent 9a35f7ec01
commit 9c0e888732
6 changed files with 46 additions and 151 deletions

View File

@@ -47,6 +47,8 @@ constructor(
private val uiEventLogger: MediaTttSenderUiEventLogger,
) : CoreStartable {
private var displayedState: ChipStateSender? = null
private val commandQueueCallbacks =
object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferSenderDisplay(
@@ -84,8 +86,27 @@ constructor(
uiEventLogger.logSenderStateChange(chipState)
if (chipState == ChipStateSender.FAR_FROM_RECEIVER) {
chipbarCoordinator.removeView(removalReason = ChipStateSender.FAR_FROM_RECEIVER.name)
// Return early if we're not displaying a chip anyway
val currentDisplayedState = displayedState ?: return
val removalReason = ChipStateSender.FAR_FROM_RECEIVER.name
if (
currentDisplayedState.transferStatus == TransferStatus.IN_PROGRESS ||
currentDisplayedState.transferStatus == TransferStatus.SUCCEEDED
) {
// Don't remove the chip if we're in progress or succeeded, since the user should
// still be able to see the status of the transfer.
logger.logRemovalBypass(
removalReason,
bypassReason = "transferStatus=${currentDisplayedState.transferStatus.name}"
)
return
}
displayedState = null
chipbarCoordinator.removeView(removalReason)
} else {
displayedState = chipState
chipbarCoordinator.displayView(ChipSenderInfo(chipState, routeInfo, undoCallback))
}
}

View File

@@ -131,7 +131,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
)
cancelViewTimeout?.run()
cancelViewTimeout = mainExecutor.executeDelayed(
{ removeView(TemporaryDisplayRemovalReason.REASON_TIMEOUT) },
{ removeView(REMOVAL_REASON_TIMEOUT) },
timeout.toLong()
)
}
@@ -175,9 +175,6 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
*/
fun removeView(removalReason: String) {
val currentDisplayInfo = displayInfo ?: return
if (shouldIgnoreViewRemoval(currentDisplayInfo.info, removalReason)) {
return
}
val currentView = currentDisplayInfo.view
animateViewOut(currentView) { windowManager.removeView(currentView) }
@@ -192,13 +189,6 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
cancelViewTimeout?.run()
}
/**
* Returns true if a view removal request should be ignored and false otherwise.
*
* Allows subclasses to keep the view visible for longer in certain circumstances.
*/
open fun shouldIgnoreViewRemoval(info: T, removalReason: String): Boolean = false
/**
* A method implemented by subclasses to update [currentView] based on [newInfo].
*/
@@ -236,10 +226,7 @@ abstract class TemporaryViewDisplayController<T : TemporaryViewInfo, U : Tempora
)
}
object TemporaryDisplayRemovalReason {
const val REASON_TIMEOUT = "TIMEOUT"
const val REASON_SCREEN_TAP = "SCREEN_TAP"
}
private const val REMOVAL_REASON_TIMEOUT = "TIMEOUT"
private data class IconInfo(
val iconName: String,

View File

@@ -44,7 +44,6 @@ import com.android.systemui.media.taptotransfer.sender.MediaTttSenderUiEventLogg
import com.android.systemui.media.taptotransfer.sender.TransferStatus
import com.android.systemui.plugins.FalsingManager
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.temporarydisplay.TemporaryDisplayRemovalReason
import com.android.systemui.temporarydisplay.TemporaryViewDisplayController
import com.android.systemui.temporarydisplay.TemporaryViewInfo
import com.android.systemui.util.concurrency.DelayableExecutor
@@ -185,23 +184,6 @@ open class ChipbarCoordinator @Inject constructor(
)
}
override fun shouldIgnoreViewRemoval(info: ChipSenderInfo, removalReason: String): Boolean {
// Don't remove the chip if we're in progress or succeeded, since the user should still be
// able to see the status of the transfer. (But do remove it if it's finally timed out.)
val transferStatus = info.state.transferStatus
if (
(transferStatus == TransferStatus.IN_PROGRESS ||
transferStatus == TransferStatus.SUCCEEDED) &&
removalReason != TemporaryDisplayRemovalReason.REASON_TIMEOUT
) {
logger.logRemovalBypass(
removalReason, bypassReason = "transferStatus=${transferStatus.name}"
)
return true
}
return false
}
override fun getTouchableRegion(view: View, outRect: Rect) {
viewUtil.setRectToViewWindowLocation(view, outRect)
}

View File

@@ -83,7 +83,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
fun setUp() {
MockitoAnnotations.initMocks(this)
whenever(mediaTttFlags.isMediaTttEnabled()).thenReturn(true)
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(1000)
whenever(accessibilityManager.getRecommendedTimeoutMillis(any(), any())).thenReturn(TIMEOUT)
fakeClock = FakeSystemClock()
fakeExecutor = FakeExecutor(fakeClock)
@@ -316,7 +316,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
}
@Test
fun transferToReceiverTriggeredThenFarFromReceiver_viewStillDisplayed() {
fun transferToReceiverTriggeredThenFarFromReceiver_viewStillDisplayedButStillTimesOut() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
routeInfo,
@@ -332,10 +332,14 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
@Test
fun transferToThisDeviceTriggeredThenFarFromReceiver_viewStillDisplayed() {
fun transferToThisDeviceTriggeredThenFarFromReceiver_viewStillDisplayedButDoesTimeOut() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
routeInfo,
@@ -351,10 +355,14 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
@Test
fun transferToReceiverSucceededThenFarFromReceiver_viewStillDisplayed() {
fun transferToReceiverSucceededThenFarFromReceiver_viewStillDisplayedButDoesTimeOut() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
routeInfo,
@@ -370,10 +378,14 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
@Test
fun transferToThisDeviceSucceededThenFarFromReceiver_viewStillDisplayed() {
fun transferToThisDeviceSucceededThenFarFromReceiver_viewStillDisplayedButDoesTimeOut() {
commandQueueCallback.updateMediaTapToTransferSenderDisplay(
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
routeInfo,
@@ -389,6 +401,10 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
private fun getChipView(): ViewGroup {
@@ -434,6 +450,7 @@ class MediaTttSenderCoordinatorTest : SysuiTestCase() {
}
private const val OTHER_DEVICE_NAME = "My Tablet"
private const val TIMEOUT = 10000
private val routeInfo =
MediaRoute2Info.Builder("id", OTHER_DEVICE_NAME)

View File

@@ -63,8 +63,6 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
@Mock
private lateinit var powerManager: PowerManager
private var shouldIgnoreViewRemoval: Boolean = false
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
@@ -209,26 +207,6 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any())
}
@Test
fun removeView_shouldIgnoreRemovalFalse_viewRemoved() {
shouldIgnoreViewRemoval = false
underTest.displayView(getState())
underTest.removeView("reason")
verify(windowManager).removeView(any())
}
@Test
fun removeView_shouldIgnoreRemovalTrue_viewNotRemoved() {
shouldIgnoreViewRemoval = true
underTest.displayView(getState())
underTest.removeView("reason")
verify(windowManager, never()).removeView(any())
}
private fun getState(name: String = "name") = ViewInfo(name)
private fun getConfigurationListener(): ConfigurationListener {
@@ -267,10 +245,6 @@ class TemporaryViewDisplayControllerTest : SysuiTestCase() {
mostRecentViewInfo = newInfo
}
override fun shouldIgnoreViewRemoval(info: ViewInfo, removalReason: String): Boolean {
return shouldIgnoreViewRemoval
}
override fun getTouchableRegion(view: View, outRect: Rect) {
outRect.setEmpty()
}

View File

@@ -434,92 +434,6 @@ class ChipbarCoordinatorTest : SysuiTestCase() {
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
}
@Test
fun transferToReceiverTriggeredThenRemoveView_viewStillDisplayed() {
underTest.displayView(transferToReceiverTriggered())
fakeClock.advanceTime(1000L)
underTest.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
}
@Test
fun transferToReceiverTriggeredThenRemoveView_eventuallyTimesOut() {
underTest.displayView(transferToReceiverTriggered())
underTest.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
@Test
fun transferToThisDeviceTriggeredThenRemoveView_viewStillDisplayed() {
underTest.displayView(transferToThisDeviceTriggered())
fakeClock.advanceTime(1000L)
underTest.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
}
@Test
fun transferToThisDeviceTriggeredThenRemoveView_eventuallyTimesOut() {
underTest.displayView(transferToThisDeviceTriggered())
underTest.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
@Test
fun transferToReceiverSucceededThenRemoveView_viewStillDisplayed() {
underTest.displayView(transferToReceiverSucceeded())
underTest.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
}
@Test
fun transferToReceiverSucceededThenRemoveView_eventuallyTimesOut() {
underTest.displayView(transferToReceiverSucceeded())
underTest.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
@Test
fun transferToThisDeviceSucceededThenRemoveView_viewStillDisplayed() {
underTest.displayView(transferToThisDeviceSucceeded())
underTest.removeView("fakeRemovalReason")
fakeExecutor.runAllReady()
verify(windowManager, never()).removeView(any())
verify(logger).logRemovalBypass(any(), any())
}
@Test
fun transferToThisDeviceSucceededThenRemoveView_eventuallyTimesOut() {
underTest.displayView(transferToThisDeviceSucceeded())
underTest.removeView("fakeRemovalReason")
fakeClock.advanceTime(TIMEOUT + 1L)
verify(windowManager).removeView(any())
}
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
private fun ViewGroup.getChipText(): String =