Merge "[Media TTT] Re-display the chip when the density/font changes." into tm-qpr-dev

This commit is contained in:
TreeHugger Robot
2022-08-01 22:09:58 +00:00
committed by Android (Google) Code Review
6 changed files with 174 additions and 99 deletions

View File

@@ -33,11 +33,13 @@ import android.view.accessibility.AccessibilityManager
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_ICONS
import android.view.accessibility.AccessibilityManager.FLAG_CONTENT_TEXT
import androidx.annotation.CallSuper
import com.android.internal.widget.CachingIconView
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
@@ -52,17 +54,17 @@ import com.android.systemui.util.view.ViewUtil
* display the chip in a certain state, since they receive <T> in [updateChipView].
*/
abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
internal val context: Context,
internal val logger: MediaTttLogger,
internal val windowManager: WindowManager,
private val viewUtil: ViewUtil,
@Main private val mainExecutor: DelayableExecutor,
private val accessibilityManager: AccessibilityManager,
private val tapGestureDetector: TapGestureDetector,
private val powerManager: PowerManager,
@LayoutRes private val chipLayoutRes: Int
internal val context: Context,
internal val logger: MediaTttLogger,
internal val windowManager: WindowManager,
private val viewUtil: ViewUtil,
@Main private val mainExecutor: DelayableExecutor,
private val accessibilityManager: AccessibilityManager,
private val configurationController: ConfigurationController,
private val tapGestureDetector: TapGestureDetector,
private val powerManager: PowerManager,
@LayoutRes private val chipLayoutRes: Int,
) {
/**
* Window layout params that will be used as a starting point for the [windowLayoutParams] of
* all subclasses.
@@ -89,42 +91,40 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
/** The chip view currently being displayed. Null if the chip is not being displayed. */
private var chipView: ViewGroup? = null
/** The chip info currently being displayed. Null if the chip is not being displayed. */
internal var chipInfo: T? = null
/** A [Runnable] that, when run, will cancel the pending timeout of the chip. */
private var cancelChipViewTimeout: Runnable? = null
/**
* Displays the chip with the current state.
* Displays the chip with the provided [newChipInfo].
*
* This method handles inflating and attaching the view, then delegates to [updateChipView] to
* display the correct information in the chip.
*/
fun displayChip(chipInfo: T) {
val oldChipView = chipView
if (chipView == null) {
chipView = LayoutInflater
.from(context)
.inflate(chipLayoutRes, null) as ViewGroup
}
val currentChipView = chipView!!
fun displayChip(newChipInfo: T) {
val currentChipView = chipView
updateChipView(chipInfo, currentChipView)
// Add view if necessary
if (oldChipView == null) {
if (currentChipView != null) {
updateChipView(newChipInfo, currentChipView)
} else {
// The chip is new, so set up all our callbacks and inflate the view
configurationController.addCallback(displayScaleListener)
tapGestureDetector.addOnGestureDetectedCallback(TAG, this::onScreenTapped)
windowManager.addView(chipView, windowLayoutParams)
// Wake the screen so the user will see the chip
powerManager.wakeUp(
SystemClock.uptimeMillis(),
PowerManager.WAKE_REASON_APPLICATION,
"com.android.systemui:media_tap_to_transfer_activated"
)
animateChipIn(currentChipView)
inflateAndUpdateChip(newChipInfo)
}
// Cancel and re-set the chip timeout each time we get a new state.
val timeout = accessibilityManager.getRecommendedTimeoutMillis(
chipInfo.getTimeoutMs().toInt(),
newChipInfo.getTimeoutMs().toInt(),
// Not all chips have controls so FLAG_CONTENT_CONTROLS might be superfluous, but
// include it just to be safe.
FLAG_CONTENT_ICONS or FLAG_CONTENT_TEXT or FLAG_CONTENT_CONTROLS
@@ -136,6 +136,32 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
)
}
/** Inflates a new chip view, updates it with [newChipInfo], and adds the view to the window. */
private fun inflateAndUpdateChip(newChipInfo: T) {
val newChipView = LayoutInflater
.from(context)
.inflate(chipLayoutRes, null) as ViewGroup
chipView = newChipView
updateChipView(newChipInfo, newChipView)
windowManager.addView(newChipView, windowLayoutParams)
animateChipIn(newChipView)
}
/** Removes then re-inflates the chip. */
private fun reinflateChip() {
val currentChipInfo = chipInfo
if (chipView == null || currentChipInfo == null) { return }
windowManager.removeView(chipView)
inflateAndUpdateChip(currentChipInfo)
}
private val displayScaleListener = object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
reinflateChip()
}
}
/**
* Hides the chip.
*
@@ -145,17 +171,22 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
open fun removeChip(removalReason: String) {
if (chipView == null) { return }
logger.logChipRemoval(removalReason)
configurationController.removeCallback(displayScaleListener)
tapGestureDetector.removeOnGestureDetectedCallback(TAG)
windowManager.removeView(chipView)
chipView = null
chipInfo = null
// No need to time the chip out since it's already gone
cancelChipViewTimeout?.run()
}
/**
* A method implemented by subclasses to update [currentChipView] based on [chipInfo].
* A method implemented by subclasses to update [currentChipView] based on [newChipInfo].
*/
abstract fun updateChipView(chipInfo: T, currentChipView: ViewGroup)
@CallSuper
open fun updateChipView(newChipInfo: T, currentChipView: ViewGroup) {
chipInfo = newChipInfo
}
/**
* A method that can be implemented by subclcasses to do custom animations for when the chip

View File

@@ -42,6 +42,7 @@ import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCom
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.animation.AnimationUtil.Companion.frames
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
@@ -54,27 +55,29 @@ import javax.inject.Inject
*/
@SysUISingleton
class MediaTttChipControllerReceiver @Inject constructor(
commandQueue: CommandQueue,
context: Context,
@MediaTttReceiverLogger logger: MediaTttLogger,
windowManager: WindowManager,
viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
tapGestureDetector: TapGestureDetector,
powerManager: PowerManager,
@Main private val mainHandler: Handler,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
commandQueue: CommandQueue,
context: Context,
@MediaTttReceiverLogger logger: MediaTttLogger,
windowManager: WindowManager,
viewUtil: ViewUtil,
mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
tapGestureDetector: TapGestureDetector,
powerManager: PowerManager,
@Main private val mainHandler: Handler,
private val uiEventLogger: MediaTttReceiverUiEventLogger,
) : MediaTttChipControllerCommon<ChipReceiverInfo>(
context,
logger,
windowManager,
viewUtil,
mainExecutor,
accessibilityManager,
tapGestureDetector,
powerManager,
R.layout.media_ttt_chip_receiver
context,
logger,
windowManager,
viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
tapGestureDetector,
powerManager,
R.layout.media_ttt_chip_receiver,
) {
@SuppressLint("WrongConstant") // We're allowed to use LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
override val windowLayoutParams = commonWindowLayoutParams.apply {
@@ -140,12 +143,13 @@ class MediaTttChipControllerReceiver @Inject constructor(
)
}
override fun updateChipView(chipInfo: ChipReceiverInfo, currentChipView: ViewGroup) {
override fun updateChipView(newChipInfo: ChipReceiverInfo, currentChipView: ViewGroup) {
super.updateChipView(newChipInfo, currentChipView)
setIcon(
currentChipView,
chipInfo.routeInfo.packageName,
chipInfo.appIconDrawableOverride,
chipInfo.appNameOverride
newChipInfo.routeInfo.packageName,
newChipInfo.appIconDrawableOverride,
newChipInfo.appNameOverride
)
}

View File

@@ -39,6 +39,7 @@ 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.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.view.ViewUtil
import javax.inject.Inject
@@ -49,33 +50,33 @@ import javax.inject.Inject
*/
@SysUISingleton
class MediaTttChipControllerSender @Inject constructor(
commandQueue: CommandQueue,
context: Context,
@MediaTttSenderLogger logger: MediaTttLogger,
windowManager: WindowManager,
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
tapGestureDetector: TapGestureDetector,
powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger
commandQueue: CommandQueue,
context: Context,
@MediaTttSenderLogger logger: MediaTttLogger,
windowManager: WindowManager,
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
tapGestureDetector: TapGestureDetector,
powerManager: PowerManager,
private val uiEventLogger: MediaTttSenderUiEventLogger
) : MediaTttChipControllerCommon<ChipSenderInfo>(
context,
logger,
windowManager,
viewUtil,
mainExecutor,
accessibilityManager,
tapGestureDetector,
powerManager,
R.layout.media_ttt_chip
context,
logger,
windowManager,
viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
tapGestureDetector,
powerManager,
R.layout.media_ttt_chip,
) {
override val windowLayoutParams = commonWindowLayoutParams.apply {
gravity = Gravity.TOP.or(Gravity.CENTER_HORIZONTAL)
}
private var currentlyDisplayedChipState: ChipStateSender? = null
private val commandQueueCallbacks = object : CommandQueue.Callbacks {
override fun updateMediaTapToTransferSenderDisplay(
@StatusBarManager.MediaTransferSenderState displayState: Int,
@@ -116,16 +117,18 @@ class MediaTttChipControllerSender @Inject constructor(
/** Displays the chip view for the given state. */
override fun updateChipView(
chipInfo: ChipSenderInfo,
currentChipView: ViewGroup) {
val chipState = chipInfo.state
currentlyDisplayedChipState = chipState
newChipInfo: ChipSenderInfo,
currentChipView: ViewGroup
) {
super.updateChipView(newChipInfo, currentChipView)
val chipState = newChipInfo.state
// App icon
setIcon(currentChipView, chipInfo.routeInfo.packageName)
setIcon(currentChipView, newChipInfo.routeInfo.packageName)
// Text
val otherDeviceName = chipInfo.routeInfo.name.toString()
val otherDeviceName = newChipInfo.routeInfo.name.toString()
currentChipView.requireViewById<TextView>(R.id.text).apply {
text = chipState.getChipTextString(context, otherDeviceName)
}
@@ -137,7 +140,7 @@ class MediaTttChipControllerSender @Inject constructor(
// Undo
val undoView = currentChipView.requireViewById<View>(R.id.undo)
val undoClickListener = chipState.undoClickListener(
this, chipInfo.routeInfo, chipInfo.undoCallback, uiEventLogger
this, newChipInfo.routeInfo, newChipInfo.undoCallback, uiEventLogger
)
undoView.setOnClickListener(undoClickListener)
undoView.visibility = (undoClickListener != null).visibleIfTrue()
@@ -161,12 +164,11 @@ class MediaTttChipControllerSender @Inject constructor(
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?.isMidTransfer == true
&& removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
if (chipInfo?.state?.isMidTransfer == true &&
removalReason != MediaTttRemovalReason.REASON_TIMEOUT) {
return
}
super.removeChip(removalReason)
currentlyDisplayedChipState = null
}
private fun Boolean.visibleIfTrue(): Int {

View File

@@ -32,6 +32,8 @@ import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
@@ -53,7 +55,7 @@ import org.mockito.MockitoAnnotations
@SmallTest
class MediaTttChipControllerCommonTest : SysuiTestCase() {
private lateinit var controllerCommon: MediaTttChipControllerCommon<ChipInfo>
private lateinit var controllerCommon: TestControllerCommon
private lateinit var fakeClock: FakeSystemClock
private lateinit var fakeExecutor: FakeExecutor
@@ -68,6 +70,8 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
@Mock
private lateinit var accessibilityManager: AccessibilityManager
@Mock
private lateinit var configurationController: ConfigurationController
@Mock
private lateinit var windowManager: WindowManager
@Mock
private lateinit var viewUtil: ViewUtil
@@ -98,14 +102,15 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
fakeExecutor = FakeExecutor(fakeClock)
controllerCommon = TestControllerCommon(
context,
logger,
windowManager,
viewUtil,
fakeExecutor,
accessibilityManager,
tapGestureDetector,
powerManager
context,
logger,
windowManager,
viewUtil,
fakeExecutor,
accessibilityManager,
configurationController,
tapGestureDetector,
powerManager,
)
}
@@ -185,6 +190,19 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
verify(windowManager).removeView(any())
}
@Test
fun displayScaleChange_chipReinflatedWithMostRecentState() {
controllerCommon.displayChip(getState(name = "First name"))
controllerCommon.displayChip(getState(name = "Second name"))
reset(windowManager)
getConfigurationListener().onDensityOrFontScaleChanged()
verify(windowManager).removeView(any())
verify(windowManager).addView(any(), any())
assertThat(controllerCommon.mostRecentChipInfo?.name).isEqualTo("Second name")
}
@Test
fun removeChip_chipRemovedAndGestureDetectionStoppedAndRemovalLogged() {
// First, add the chip
@@ -341,7 +359,7 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
verify(windowManager, never()).removeView(any())
}
private fun getState() = ChipInfo()
private fun getState(name: String = "name") = ChipInfo(name)
private fun getChipView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
@@ -351,6 +369,12 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
private fun getConfigurationListener(): ConfigurationListener {
val callbackCaptor = argumentCaptor<ConfigurationListener>()
verify(configurationController).addCallback(capture(callbackCaptor))
return callbackCaptor.value
}
inner class TestControllerCommon(
context: Context,
logger: MediaTttLogger,
@@ -358,8 +382,9 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
viewUtil: ViewUtil,
@Main mainExecutor: DelayableExecutor,
accessibilityManager: AccessibilityManager,
configurationController: ConfigurationController,
tapGestureDetector: TapGestureDetector,
powerManager: PowerManager
powerManager: PowerManager,
) : MediaTttChipControllerCommon<ChipInfo>(
context,
logger,
@@ -367,16 +392,22 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
viewUtil,
mainExecutor,
accessibilityManager,
configurationController,
tapGestureDetector,
powerManager,
R.layout.media_ttt_chip
R.layout.media_ttt_chip,
) {
var mostRecentChipInfo: ChipInfo? = null
override val windowLayoutParams = commonWindowLayoutParams
override fun updateChipView(chipInfo: ChipInfo, currentChipView: ViewGroup) {}
override fun updateChipView(newChipInfo: ChipInfo, currentChipView: ViewGroup) {
super.updateChipView(newChipInfo, currentChipView)
mostRecentChipInfo = newChipInfo
}
override fun getIconSize(isAppIcon: Boolean): Int = ICON_SIZE
}
inner class ChipInfo : ChipInfoCommon {
inner class ChipInfo(val name: String) : ChipInfoCommon {
override fun getTimeoutMs() = 1L
}
}

View File

@@ -37,6 +37,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.eq
@@ -68,6 +69,8 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
@Mock
private lateinit var accessibilityManager: AccessibilityManager
@Mock
private lateinit var configurationController: ConfigurationController
@Mock
private lateinit var powerManager: PowerManager
@Mock
private lateinit var windowManager: WindowManager
@@ -103,6 +106,7 @@ class MediaTttChipControllerReceiverTest : SysuiTestCase() {
viewUtil,
FakeExecutor(FakeSystemClock()),
accessibilityManager,
configurationController,
TapGestureDetector(context),
powerManager,
Handler.getMain(),

View File

@@ -38,12 +38,12 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.media.taptotransfer.common.MediaTttLogger
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.gesture.TapGestureDetector
import com.android.systemui.statusbar.policy.ConfigurationController
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.android.systemui.util.view.ViewUtil
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
@@ -52,8 +52,8 @@ import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -70,6 +70,8 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
@Mock
private lateinit var accessibilityManager: AccessibilityManager
@Mock
private lateinit var configurationController: ConfigurationController
@Mock
private lateinit var powerManager: PowerManager
@Mock
private lateinit var windowManager: WindowManager
@@ -112,6 +114,7 @@ class MediaTttChipControllerSenderTest : SysuiTestCase() {
viewUtil,
fakeExecutor,
accessibilityManager,
configurationController,
TapGestureDetector(context),
powerManager,
senderUiEventLogger