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

Commit 10064bc6 authored by Nicolo' Mazzucato's avatar Nicolo' Mazzucato
Browse files

Listen for insets changes from MirroringDialog

The dialog was being shown only after lockscreen dismissal when an external display was connected.

The dialog got bottom insets only when created. However, when using 3 buttons nav the bottom insets was changing with some delay.

The base class (SystemUIBottomSheetDialog) now listens at insets changes and propagates them to child classes.

+ Adding a 200ms debounce to show the dialog: this avoids a visible jump when unlocking with 3 buttons nav.

Flag: None
Fixes: 327656078
Test: MirroringConfirmationDialogTest
Change-Id: I14ce1c2111707758f31ba9256a705edfd711b31e
parent ecce5eb5
Loading
Loading
Loading
Loading
+16 −8
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.display.ui.view
import android.content.Context
import android.os.Bundle
import android.view.View
import android.view.WindowInsets
import android.widget.TextView
import androidx.core.view.updatePadding
import com.android.systemui.res.R
@@ -44,7 +45,10 @@ class MirroringConfirmationDialog(
    private lateinit var mirrorButton: TextView
    private lateinit var dismissButton: TextView
    private lateinit var dualDisplayWarning: TextView
    private lateinit var bottomSheet: View
    private var enabledPressed = false
    private val defaultDialogBottomInset =
        context.resources.getDimensionPixelSize(R.dimen.dialog_bottom_padding)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
@@ -63,6 +67,8 @@ class MirroringConfirmationDialog(
                visibility = if (showConcurrentDisplayInfo) View.VISIBLE else View.GONE
            }

        bottomSheet = requireViewById(R.id.cd_bottom_sheet)

        setOnDismissListener {
            if (!enabledPressed) {
                onCancelMirroring.onClick(null)
@@ -71,15 +77,17 @@ class MirroringConfirmationDialog(
        setupInsets()
    }

    private fun setupInsets() {
    private fun setupInsets(navbarInsets: Int = navbarBottomInsetsProvider()) {
        // This avoids overlap between dialog content and navigation bars.
        requireViewById<View>(R.id.cd_bottom_sheet).apply {
            val navbarInsets = navbarBottomInsetsProvider()
            val defaultDialogBottomInset =
                context.resources.getDimensionPixelSize(R.dimen.dialog_bottom_padding)
        // we only care about the bottom inset as in all other configuration where navigations
        // are in other display sides there is no overlap with the dialog.
            updatePadding(bottom = max(navbarInsets, defaultDialogBottomInset))
        bottomSheet.updatePadding(bottom = max(navbarInsets, defaultDialogBottomInset))
    }

    override fun onInsetsChanged(changedTypes: Int, insets: WindowInsets) {
        val navbarType = WindowInsets.Type.navigationBars()
        if (changedTypes and navbarType != 0) {
            setupInsets(insets.getInsets(navbarType).bottom)
        }
    }

+11 −0
Original line number Diff line number Diff line
@@ -32,9 +32,12 @@ import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.launch
@@ -57,6 +60,7 @@ constructor(
    private var dialog: Dialog? = null

    /** Starts listening for pending displays. */
    @OptIn(FlowPreview::class)
    override fun start() {
        val pendingDisplayFlow = connectedDisplayInteractor.pendingDisplay
        val concurrentDisplaysInProgessFlow =
@@ -66,6 +70,13 @@ constructor(
                flow { emit(false) }
            }
        pendingDisplayFlow
            // Let's debounce for 2 reasons:
            // - prevent fast dialog flashes in case pending displays are available for just a few
            // millis
            // - Prevent jumps related to inset changes: when in 3 buttons navigation, device
            // unlock triggers a change in insets that might result in a jump of the dialog (if a
            // display was connected while on the lockscreen).
            .debounce(200.milliseconds)
            .combine(concurrentDisplaysInProgessFlow) { pendingDisplay, concurrentDisplaysInProgress
                ->
                if (pendingDisplay == null) {
+30 −0
Original line number Diff line number Diff line
@@ -23,6 +23,9 @@ import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.view.WindowInsets
import android.view.WindowInsets.Type.InsetsType
import android.view.WindowInsetsAnimation
import android.view.WindowManager.LayoutParams.MATCH_PARENT
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
@@ -70,16 +73,43 @@ open class SystemUIBottomSheetDialog(
    override fun onStart() {
        super.onStart()
        configurationController?.addCallback(onConfigChanged)
        window?.decorView?.setWindowInsetsAnimationCallback(insetsAnimationCallback)
    }

    override fun onStop() {
        super.onStop()
        configurationController?.removeCallback(onConfigChanged)
        window?.decorView?.setWindowInsetsAnimationCallback(null)
    }

    /** Called after any insets change. */
    open fun onInsetsChanged(@InsetsType changedTypes: Int, insets: WindowInsets) {}

    /** Can be overridden by subclasses to receive config changed events. */
    open fun onConfigurationChanged() {}

    private val insetsAnimationCallback =
        object : WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) {

            private var lastInsets: WindowInsets? = null

            override fun onEnd(animation: WindowInsetsAnimation) {
                lastInsets?.let { onInsetsChanged(animation.typeMask, it) }
            }

            override fun onProgress(
                insets: WindowInsets,
                animations: MutableList<WindowInsetsAnimation>,
            ): WindowInsets {
                lastInsets = insets
                onInsetsChanged(changedTypes = allAnimationMasks(animations), insets)
                return insets
            }

            private fun allAnimationMasks(animations: List<WindowInsetsAnimation>): Int =
                animations.fold(0) { acc: Int, it -> acc or it.typeMask }
        }

    private val onConfigChanged =
        object : ConfigurationListener {
            override fun onConfigChanged(newConfig: Configuration?) {
+34 −0
Original line number Diff line number Diff line
@@ -16,14 +16,17 @@

package com.android.systemui.display.ui.view

import android.graphics.Insets
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.view.WindowInsets
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.res.R
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -41,6 +44,7 @@ class MirroringConfirmationDialogTest : SysuiTestCase() {

    private val onStartMirroringCallback = mock<View.OnClickListener>()
    private val onCancelCallback = mock<View.OnClickListener>()

    @Before
    fun setUp() {
        MockitoAnnotations.initMocks(this)
@@ -96,10 +100,40 @@ class MirroringConfirmationDialogTest : SysuiTestCase() {
        verify(onStartMirroringCallback).onClick(any())
    }

    @Test
    fun onInsetsChanged_navBarInsets_updatesBottomPadding() {
        dialog.show()

        val insets = buildInsets(WindowInsets.Type.navigationBars(), TEST_BOTTOM_INSETS)
        dialog.onInsetsChanged(WindowInsets.Type.navigationBars(), insets)

        assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom)
            .isEqualTo(TEST_BOTTOM_INSETS)
    }

    @Test
    fun onInsetsChanged_otherType_doesNotUpdateBottomPadding() {
        dialog.show()

        val insets = buildInsets(WindowInsets.Type.ime(), TEST_BOTTOM_INSETS)
        dialog.onInsetsChanged(WindowInsets.Type.ime(), insets)

        assertThat(dialog.requireViewById<View>(R.id.cd_bottom_sheet).paddingBottom)
            .isNotEqualTo(TEST_BOTTOM_INSETS)
    }

    private fun buildInsets(@WindowInsets.Type.InsetsType type: Int, bottom: Int): WindowInsets {
        return WindowInsets.Builder().setInsets(type, Insets.of(0, 0, 0, bottom)).build()
    }

    @After
    fun teardown() {
        if (::dialog.isInitialized) {
            dialog.dismiss()
        }
    }

    private companion object {
        const val TEST_BOTTOM_INSETS = 1000 // arbitrarily high number
    }
}