[Media TTT] Don't hide the chip if we're in a transfer triggered state.
Also give those states a longer timeout. Fixes: 219766679 Test: manual (verify requesting a chip removal doesn't work if currently in the transfer triggered state) Test: media.taptotransfer tests Change-Id: Ifbbb4fd28ba092f1c989ed31c21bed5e891ac3be
This commit is contained in:
@@ -95,8 +95,8 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
// Cancel and re-set the chip timeout each time we get a new state.
|
||||
cancelChipViewTimeout?.run()
|
||||
cancelChipViewTimeout = mainExecutor.executeDelayed(
|
||||
{ removeChip(REASON_TIMEOUT) },
|
||||
TIMEOUT_MILLIS
|
||||
{ removeChip(MediaTttRemovalReason.REASON_TIMEOUT) },
|
||||
chipState.getTimeoutMs()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -106,10 +106,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
* @param removalReason a short string describing why the chip was removed (timeout, state
|
||||
* change, etc.)
|
||||
*/
|
||||
fun removeChip(removalReason: String) {
|
||||
// TODO(b/203800347): We may not want to hide the chip if we're currently in a
|
||||
// TransferTriggered state: Once the user has initiated the transfer, they should be able
|
||||
// to move away from the receiver device but still see the status of the transfer.
|
||||
open fun removeChip(removalReason: String) {
|
||||
if (chipView == null) { return }
|
||||
logger.logChipRemoval(removalReason)
|
||||
tapGestureDetector.removeOnGestureDetectedCallback(TAG)
|
||||
@@ -148,7 +145,7 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
// If the tap is within the chip bounds, we shouldn't hide the chip (in case users think the
|
||||
// chip is tappable).
|
||||
if (!viewUtil.touchIsWithinView(view, e.x, e.y)) {
|
||||
removeChip(REASON_SCREEN_TAP)
|
||||
removeChip(MediaTttRemovalReason.REASON_SCREEN_TAP)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,8 +154,9 @@ abstract class MediaTttChipControllerCommon<T : MediaTttChipState>(
|
||||
// UpdateMediaTapToTransferReceiverDisplayTest
|
||||
private const val WINDOW_TITLE = "Media Transfer Chip View"
|
||||
private val TAG = MediaTttChipControllerCommon::class.simpleName!!
|
||||
@VisibleForTesting
|
||||
const val TIMEOUT_MILLIS = 3000L
|
||||
|
||||
private const val REASON_TIMEOUT = "TIMEOUT"
|
||||
private const val REASON_SCREEN_TAP = "SCREEN_TAP"
|
||||
object MediaTttRemovalReason {
|
||||
const val REASON_TIMEOUT = "TIMEOUT"
|
||||
const val REASON_SCREEN_TAP = "SCREEN_TAP"
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,14 @@ open class MediaTttChipState(
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of time this chip should display on the screen before it times out and
|
||||
* disappears. [MediaTttChipControllerCommon] will ensure that the timeout resets each time we
|
||||
* receive a new state.
|
||||
*/
|
||||
open fun getTimeoutMs(): Long = DEFAULT_TIMEOUT_MILLIS
|
||||
}
|
||||
|
||||
private const val DEFAULT_TIMEOUT_MILLIS = 3000L
|
||||
private val TAG = MediaTttChipState::class.simpleName!!
|
||||
|
||||
@@ -97,6 +97,7 @@ class TransferToReceiverTriggered(
|
||||
}
|
||||
|
||||
override fun showLoading() = true
|
||||
override fun getTimeoutMs() = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -111,6 +112,7 @@ class TransferToThisDeviceTriggered(
|
||||
}
|
||||
|
||||
override fun showLoading() = true
|
||||
override fun getTimeoutMs() = TRANSFER_TRIGGERED_TIMEOUT_MILLIS
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -194,3 +196,8 @@ class TransferFailed(
|
||||
return context.getString(R.string.media_transfer_failed)
|
||||
}
|
||||
}
|
||||
|
||||
// Give the Transfer*Triggered states a longer timeout since those states represent an active
|
||||
// process and we should keep the user informed about it as long as possible (but don't allow it to
|
||||
// continue indefinitely).
|
||||
private const val TRANSFER_TRIGGERED_TIMEOUT_MILLIS = 15000L
|
||||
@@ -30,6 +30,7 @@ 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.media.taptotransfer.common.MediaTttLogger
|
||||
import com.android.systemui.media.taptotransfer.common.MediaTttRemovalReason
|
||||
import com.android.systemui.statusbar.CommandQueue
|
||||
import com.android.systemui.statusbar.gesture.TapGestureDetector
|
||||
import com.android.systemui.util.concurrency.DelayableExecutor
|
||||
@@ -58,6 +59,8 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
tapGestureDetector,
|
||||
R.layout.media_ttt_chip
|
||||
) {
|
||||
private var currentlyDisplayedChipState: ChipStateSender? = null
|
||||
|
||||
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
|
||||
override fun updateMediaTapToTransferSenderDisplay(
|
||||
@StatusBarManager.MediaTransferSenderState displayState: Int,
|
||||
@@ -115,6 +118,8 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
|
||||
/** Displays the chip view for the given state. */
|
||||
override fun updateChipView(chipState: ChipStateSender, currentChipView: ViewGroup) {
|
||||
currentlyDisplayedChipState = chipState
|
||||
|
||||
// App icon
|
||||
setIcon(chipState, currentChipView)
|
||||
|
||||
@@ -139,6 +144,18 @@ class MediaTttChipControllerSender @Inject constructor(
|
||||
if (showFailure) { View.VISIBLE } else { View.GONE }
|
||||
}
|
||||
|
||||
override fun removeChip(removalReason: String) {
|
||||
// Don't remove the chip if we're mid-transfer since the user should still be able to
|
||||
// see the status of the transfer. (But do remove it if it's finally timed out.)
|
||||
if ((currentlyDisplayedChipState is TransferToReceiverTriggered ||
|
||||
currentlyDisplayedChipState is TransferToThisDeviceTriggered)
|
||||
&& removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
|
||||
return
|
||||
}
|
||||
super.removeChip(removalReason)
|
||||
currentlyDisplayedChipState = null
|
||||
}
|
||||
|
||||
private fun stateIntToString(@StatusBarManager.MediaTransferSenderState state: Int): String {
|
||||
return when(state) {
|
||||
StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST ->
|
||||
|
||||
@@ -96,20 +96,22 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun displayChip_chipDoesNotDisappearsBeforeTimeout() {
|
||||
controllerCommon.displayChip(getState())
|
||||
val state = getState()
|
||||
controllerCommon.displayChip(state)
|
||||
reset(windowManager)
|
||||
|
||||
fakeClock.advanceTime(TIMEOUT_MILLIS - 1)
|
||||
fakeClock.advanceTime(state.getTimeoutMs() - 1)
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun displayChip_chipDisappearsAfterTimeout() {
|
||||
controllerCommon.displayChip(getState())
|
||||
val state = getState()
|
||||
controllerCommon.displayChip(state)
|
||||
reset(windowManager)
|
||||
|
||||
fakeClock.advanceTime(TIMEOUT_MILLIS + 1)
|
||||
fakeClock.advanceTime(state.getTimeoutMs() + 1)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
@@ -117,7 +119,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun displayChip_calledAgainBeforeTimeout_timeoutReset() {
|
||||
// First, display the chip
|
||||
controllerCommon.displayChip(getState())
|
||||
val state = getState()
|
||||
controllerCommon.displayChip(state)
|
||||
|
||||
// After some time, re-display the chip
|
||||
val waitTime = 1000L
|
||||
@@ -125,7 +128,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
controllerCommon.displayChip(getState())
|
||||
|
||||
// Wait until the timeout for the first display would've happened
|
||||
fakeClock.advanceTime(TIMEOUT_MILLIS - waitTime + 1)
|
||||
fakeClock.advanceTime(state.getTimeoutMs() - waitTime + 1)
|
||||
|
||||
// Verify we didn't hide the chip
|
||||
verify(windowManager, never()).removeView(any())
|
||||
@@ -134,14 +137,15 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun displayChip_calledAgainBeforeTimeout_eventuallyTimesOut() {
|
||||
// First, display the chip
|
||||
controllerCommon.displayChip(getState())
|
||||
val state = getState()
|
||||
controllerCommon.displayChip(state)
|
||||
|
||||
// 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)
|
||||
fakeClock.advanceTime(state.getTimeoutMs() + 1)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
@@ -72,6 +72,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
private lateinit var commandQueue: CommandQueue
|
||||
private lateinit var commandQueueCallback: CommandQueue.Callbacks
|
||||
private lateinit var fakeAppIconDrawable: Drawable
|
||||
private lateinit var fakeClock: FakeSystemClock
|
||||
private lateinit var fakeExecutor: FakeExecutor
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -85,13 +87,16 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
)).thenReturn(applicationInfo)
|
||||
context.setMockPackageManager(packageManager)
|
||||
|
||||
fakeClock = FakeSystemClock()
|
||||
fakeExecutor = FakeExecutor(fakeClock)
|
||||
|
||||
controllerSender = MediaTttChipControllerSender(
|
||||
commandQueue,
|
||||
context,
|
||||
logger,
|
||||
windowManager,
|
||||
viewUtil,
|
||||
FakeExecutor(FakeSystemClock()),
|
||||
fakeExecutor,
|
||||
TapGestureDetector(context)
|
||||
)
|
||||
|
||||
@@ -475,6 +480,52 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
|
||||
assertThat(getChipView().getFailureIcon().visibility).isEqualTo(View.VISIBLE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverTriggeredThenRemoveChip_chipStillDisplayed() {
|
||||
controllerSender.displayChip(transferToReceiverTriggered())
|
||||
fakeClock.advanceTime(1000L)
|
||||
|
||||
controllerSender.removeChip("fakeRemovalReason")
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToReceiverTriggeredThenFarFromReceiver_eventuallyTimesOut() {
|
||||
val state = transferToReceiverTriggered()
|
||||
controllerSender.displayChip(state)
|
||||
fakeClock.advanceTime(1000L)
|
||||
controllerSender.removeChip("fakeRemovalReason")
|
||||
|
||||
fakeClock.advanceTime(state.getTimeoutMs() + 1)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToThisDeviceTriggeredThenRemoveChip_chipStillDisplayed() {
|
||||
controllerSender.displayChip(transferToThisDeviceTriggered())
|
||||
fakeClock.advanceTime(1000L)
|
||||
|
||||
controllerSender.removeChip("fakeRemovalReason")
|
||||
fakeExecutor.runAllReady()
|
||||
|
||||
verify(windowManager, never()).removeView(any())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun transferToThisDeviceTriggeredThenFarFromReceiver_eventuallyTimesOut() {
|
||||
val state = transferToThisDeviceTriggered()
|
||||
controllerSender.displayChip(state)
|
||||
fakeClock.advanceTime(1000L)
|
||||
controllerSender.removeChip("fakeRemovalReason")
|
||||
|
||||
fakeClock.advanceTime(state.getTimeoutMs() + 1)
|
||||
|
||||
verify(windowManager).removeView(any())
|
||||
}
|
||||
|
||||
private fun LinearLayout.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
|
||||
|
||||
private fun LinearLayout.getChipText(): String =
|
||||
|
||||
Reference in New Issue
Block a user