Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2b940543 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

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

parents 62c47f6c 655f9880
Loading
Loading
Loading
Loading
+59 −28
Original line number Diff line number Diff line
@@ -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

@@ -58,11 +60,11 @@ abstract class MediaTttChipControllerCommon<T : ChipInfoCommon>(
        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
        @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
+28 −24
Original line number Diff line number Diff line
@@ -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
@@ -61,6 +62,7 @@ class MediaTttChipControllerReceiver @Inject constructor(
        viewUtil: ViewUtil,
        mainExecutor: DelayableExecutor,
        accessibilityManager: AccessibilityManager,
        configurationController: ConfigurationController,
        tapGestureDetector: TapGestureDetector,
        powerManager: PowerManager,
        @Main private val mainHandler: Handler,
@@ -72,9 +74,10 @@ class MediaTttChipControllerReceiver @Inject constructor(
        viewUtil,
        mainExecutor,
        accessibilityManager,
        configurationController,
        tapGestureDetector,
        powerManager,
    R.layout.media_ttt_chip_receiver
        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
        )
    }

+33 −31
Original line number Diff line number Diff line
@@ -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
@@ -56,6 +57,7 @@ class MediaTttChipControllerSender @Inject constructor(
        viewUtil: ViewUtil,
        @Main mainExecutor: DelayableExecutor,
        accessibilityManager: AccessibilityManager,
        configurationController: ConfigurationController,
        tapGestureDetector: TapGestureDetector,
        powerManager: PowerManager,
        private val uiEventLogger: MediaTttSenderUiEventLogger
@@ -66,16 +68,15 @@ class MediaTttChipControllerSender @Inject constructor(
        viewUtil,
        mainExecutor,
        accessibilityManager,
        configurationController,
        tapGestureDetector,
        powerManager,
    R.layout.media_ttt_chip
        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 {
+45 −14
Original line number Diff line number Diff line
@@ -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
@@ -104,8 +108,9 @@ class MediaTttChipControllerCommonTest : SysuiTestCase() {
                viewUtil,
                fakeExecutor,
                accessibilityManager,
                configurationController,
                tapGestureDetector,
            powerManager
                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
    }
}
+4 −0
Original line number Diff line number Diff line
@@ -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(),
Loading