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

Commit a126e5fc authored by Nicolò Mazzucato's avatar Nicolò Mazzucato Committed by Android (Google) Code Review
Browse files

Merge "Listen for insets changes from MirroringDialog" into main

parents 99745f1d 10064bc6
Loading
Loading
Loading
Loading
+16 −8
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.systemui.display.ui.view
import android.content.Context
import android.content.Context
import android.os.Bundle
import android.os.Bundle
import android.view.View
import android.view.View
import android.view.WindowInsets
import android.widget.TextView
import android.widget.TextView
import androidx.core.view.updatePadding
import androidx.core.view.updatePadding
import com.android.systemui.res.R
import com.android.systemui.res.R
@@ -44,7 +45,10 @@ class MirroringConfirmationDialog(
    private lateinit var mirrorButton: TextView
    private lateinit var mirrorButton: TextView
    private lateinit var dismissButton: TextView
    private lateinit var dismissButton: TextView
    private lateinit var dualDisplayWarning: TextView
    private lateinit var dualDisplayWarning: TextView
    private lateinit var bottomSheet: View
    private var enabledPressed = false
    private var enabledPressed = false
    private val defaultDialogBottomInset =
        context.resources.getDimensionPixelSize(R.dimen.dialog_bottom_padding)


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


        bottomSheet = requireViewById(R.id.cd_bottom_sheet)

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


    private fun setupInsets() {
    private fun setupInsets(navbarInsets: Int = navbarBottomInsetsProvider()) {
        // This avoids overlap between dialog content and navigation bars.
        // 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
        // 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.
        // 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 Original line Diff line number Diff line
@@ -32,9 +32,12 @@ import dagger.Module
import dagger.multibindings.ClassKey
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
import dagger.multibindings.IntoMap
import javax.inject.Inject
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.launch
@@ -57,6 +60,7 @@ constructor(
    private var dialog: Dialog? = null
    private var dialog: Dialog? = null


    /** Starts listening for pending displays. */
    /** Starts listening for pending displays. */
    @OptIn(FlowPreview::class)
    override fun start() {
    override fun start() {
        val pendingDisplayFlow = connectedDisplayInteractor.pendingDisplay
        val pendingDisplayFlow = connectedDisplayInteractor.pendingDisplay
        val concurrentDisplaysInProgessFlow =
        val concurrentDisplaysInProgessFlow =
@@ -66,6 +70,13 @@ constructor(
                flow { emit(false) }
                flow { emit(false) }
            }
            }
        pendingDisplayFlow
        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
            .combine(concurrentDisplaysInProgessFlow) { pendingDisplay, concurrentDisplaysInProgress
                ->
                ->
                if (pendingDisplay == null) {
                if (pendingDisplay == null) {
+30 −0
Original line number Original line Diff line number Diff line
@@ -23,6 +23,9 @@ import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.os.Bundle
import android.view.Gravity
import android.view.Gravity
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
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.MATCH_PARENT
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
import android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS
@@ -70,16 +73,43 @@ open class SystemUIBottomSheetDialog(
    override fun onStart() {
    override fun onStart() {
        super.onStart()
        super.onStart()
        configurationController?.addCallback(onConfigChanged)
        configurationController?.addCallback(onConfigChanged)
        window?.decorView?.setWindowInsetsAnimationCallback(insetsAnimationCallback)
    }
    }


    override fun onStop() {
    override fun onStop() {
        super.onStop()
        super.onStop()
        configurationController?.removeCallback(onConfigChanged)
        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. */
    /** Can be overridden by subclasses to receive config changed events. */
    open fun onConfigurationChanged() {}
    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 =
    private val onConfigChanged =
        object : ConfigurationListener {
        object : ConfigurationListener {
            override fun onConfigChanged(newConfig: Configuration?) {
            override fun onConfigChanged(newConfig: Configuration?) {
+34 −0
Original line number Original line Diff line number Diff line
@@ -16,14 +16,17 @@


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


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


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

    @Before
    @Before
    fun setUp() {
    fun setUp() {
        MockitoAnnotations.initMocks(this)
        MockitoAnnotations.initMocks(this)
@@ -96,10 +100,40 @@ class MirroringConfirmationDialogTest : SysuiTestCase() {
        verify(onStartMirroringCallback).onClick(any())
        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
    @After
    fun teardown() {
    fun teardown() {
        if (::dialog.isInitialized) {
        if (::dialog.isInitialized) {
            dialog.dismiss()
            dialog.dismiss()
        }
        }
    }
    }

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