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

Commit c1986f81 authored by Anton Potapov's avatar Anton Potapov
Browse files

Retain old logic for prohibiting battery estimate on the devices with

center cutout.

Fixes: 266846431
Test: manual + added autotests
Change-Id: I03a5618baeaa6aa558b9b6c280f6a41ec688d8f4
parent ae1b2e33
Loading
Loading
Loading
Loading
+18 −20
Original line number Diff line number Diff line
@@ -152,9 +152,7 @@ public class QuickStatusBarHeader extends FrameLayout {
        Configuration config = mContext.getResources().getConfiguration();
        setDatePrivacyContainersWidth(config.orientation == Configuration.ORIENTATION_LANDSCAPE);

        // QS will always show the estimate, and BatteryMeterView handles the case where
        // it's unavailable or charging
        mBatteryRemainingIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE);
        updateBatteryMode();

        mIconsAlphaAnimatorFixed = new TouchAnimator.Builder()
                .addFloat(mIconContainer, "alpha", 0, 1)
@@ -460,9 +458,9 @@ public class QuickStatusBarHeader extends FrameLayout {
                (LinearLayout.LayoutParams) mDatePrivacySeparator.getLayoutParams();
        LinearLayout.LayoutParams mClockIconsSeparatorLayoutParams =
                (LinearLayout.LayoutParams) mClockIconsSeparator.getLayoutParams();
        if (cutout != null) {
            Rect topCutout = cutout.getBoundingRectTop();
            if (topCutout.isEmpty() || hasCornerCutout) {

        Rect topCutout = cutout == null ? null : cutout.getBoundingRectTop();
        if (topCutout == null || topCutout.isEmpty() || hasCornerCutout) {
            datePrivacySeparatorLayoutParams.width = 0;
            mDatePrivacySeparator.setVisibility(View.GONE);
            mClockIconsSeparatorLayoutParams.width = 0;
@@ -477,7 +475,7 @@ public class QuickStatusBarHeader extends FrameLayout {
            setSeparatorVisibility(mKeyguardExpansionFraction == 0f);
            mHasCenterCutout = true;
        }
        }

        mDatePrivacySeparator.setLayoutParams(datePrivacySeparatorLayoutParams);
        mClockIconsSeparator.setLayoutParams(mClockIconsSeparatorLayoutParams);
        mCutOutPaddingLeft = sbInsets.first;
+19 −7
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.os.Bundle
import android.os.Trace
import android.os.Trace.TRACE_TAG_APP
import android.util.Pair
import android.view.DisplayCutout
import android.view.View
import android.view.WindowInsets
import android.widget.TextView
@@ -91,7 +92,8 @@ class LargeScreenShadeHeaderController @Inject constructor(
    private val featureFlags: FeatureFlags,
    private val qsCarrierGroupControllerBuilder: QSCarrierGroupController.Builder,
    private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager,
    private val demoModeController: DemoModeController
    private val demoModeController: DemoModeController,
    private val qsBatteryModeController: QsBatteryModeController,
) : ViewController<View>(header), Dumpable {

    companion object {
@@ -129,9 +131,8 @@ class LargeScreenShadeHeaderController @Inject constructor(
    private val iconContainer: StatusIconContainer = header.findViewById(R.id.statusIcons)
    private val qsCarrierGroup: QSCarrierGroup = header.findViewById(R.id.carrier_group)

    private var cutoutLeft = 0
    private var cutoutRight = 0
    private var roundedCorners = 0
    private var cutout: DisplayCutout? = null
    private var lastInsets: WindowInsets? = null

    private var qsDisabled = false
@@ -273,7 +274,6 @@ class LargeScreenShadeHeaderController @Inject constructor(

        // battery settings same as in QS icons
        batteryMeterViewController.ignoreTunerUpdates()
        batteryIcon.setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)

        iconManager = tintedIconManagerFactory.create(iconContainer, StatusBarLocation.QS)
        iconManager.setTint(
@@ -305,6 +305,7 @@ class LargeScreenShadeHeaderController @Inject constructor(

        if (header is MotionLayout) {
            header.setOnApplyWindowInsetsListener(insetListener)

            clock.addOnLayoutChangeListener { v, _, _, _, _, _, _, _, _ ->
                val newPivot = if (v.isLayoutRtl) v.width.toFloat() else 0f
                v.pivotX = newPivot
@@ -376,11 +377,13 @@ class LargeScreenShadeHeaderController @Inject constructor(
    }

    private fun updateConstraintsForInsets(view: MotionLayout, insets: WindowInsets) {
        val cutout = insets.displayCutout
        val cutout = insets.displayCutout.also {
            this.cutout = it
        }

        val sbInsets: Pair<Int, Int> = insetsProvider.getStatusBarContentInsetsForCurrentRotation()
        cutoutLeft = sbInsets.first
        cutoutRight = sbInsets.second
        val cutoutLeft = sbInsets.first
        val cutoutRight = sbInsets.second
        val hasCornerCutout: Boolean = insetsProvider.currentRotationHasCornerCutout()
        updateQQSPaddings()
        // Set these guides as the left/right limits for content that lives in the top row, using
@@ -408,6 +411,13 @@ class LargeScreenShadeHeaderController @Inject constructor(
        }

        view.updateAllConstraints(changes)
        updateBatteryMode()
    }

    private fun updateBatteryMode() {
        qsBatteryModeController.getBatteryMode(cutout, qsExpandedFraction)?.let {
            batteryIcon.setPercentShowMode(it)
        }
    }

    private fun updateScrollY() {
@@ -475,6 +485,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
        if (header is MotionLayout && !largeScreenActive && visible) {
            logInstantEvent("updatePosition: $qsExpandedFraction")
            header.progress = qsExpandedFraction
            updateBatteryMode()
        }
    }

@@ -511,6 +522,7 @@ class LargeScreenShadeHeaderController @Inject constructor(
        val padding = resources.getDimensionPixelSize(R.dimen.qs_panel_padding)
        header.setPadding(padding, header.paddingTop, padding, header.paddingBottom)
        updateQQSPaddings()
        qsBatteryModeController.updateResources()
    }

    private fun updateQQSPaddings() {
+70 −0
Original line number Diff line number Diff line
package com.android.systemui.shade

import android.content.Context
import android.view.DisplayCutout
import com.android.systemui.R
import com.android.systemui.battery.BatteryMeterView
import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider
import javax.inject.Inject

/**
 * Controls [BatteryMeterView.BatteryPercentMode]. It takes into account cutout and qs-qqs
 * transition fraction when determining the mode.
 */
class QsBatteryModeController
@Inject
constructor(
    private val context: Context,
    private val insetsProvider: StatusBarContentInsetsProvider,
) {

    private companion object {
        // MotionLayout frames are in [0, 100]. Where 0 and 100 are reserved for start and end
        // frames.
        const val MOTION_LAYOUT_MAX_FRAME = 100
        // We add a single buffer frame to ensure that battery view faded out completely when we are
        // about to change it's state
        const val BUFFER_FRAME_COUNT = 1
    }

    private var fadeInStartFraction: Float = 0f
    private var fadeOutCompleteFraction: Float = 0f

    init {
        updateResources()
    }

    /**
     * Returns an appropriate [BatteryMeterView.BatteryPercentMode] for the [qsExpandedFraction] and
     * [cutout]. We don't show battery estimation in qqs header on the devices with center cutout.
     * The result might be null when the battery icon is invisible during the qs-qqs transition
     * animation.
     */
    @BatteryMeterView.BatteryPercentMode
    fun getBatteryMode(cutout: DisplayCutout?, qsExpandedFraction: Float): Int? =
        when {
            qsExpandedFraction > fadeInStartFraction -> BatteryMeterView.MODE_ESTIMATE
            qsExpandedFraction < fadeOutCompleteFraction ->
                if (hasCenterCutout(cutout)) {
                    BatteryMeterView.MODE_ON
                } else {
                    BatteryMeterView.MODE_ESTIMATE
                }
            else -> null
        }

    fun updateResources() {
        fadeInStartFraction =
            (context.resources.getInteger(R.integer.fade_in_start_frame) - BUFFER_FRAME_COUNT) /
                MOTION_LAYOUT_MAX_FRAME.toFloat()
        fadeOutCompleteFraction =
            (context.resources.getInteger(R.integer.fade_out_complete_frame) + BUFFER_FRAME_COUNT) /
                MOTION_LAYOUT_MAX_FRAME.toFloat()
    }

    private fun hasCenterCutout(cutout: DisplayCutout?): Boolean =
        cutout?.let {
            !insetsProvider.currentRotationHasCornerCutout() && !it.boundingRectTop.isEmpty
        }
            ?: false
}
+49 −19
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import android.content.res.Resources
import android.content.res.XmlResourceParser
import android.graphics.Rect
import android.testing.AndroidTestingRunner
import android.view.Display
import android.view.DisplayCutout
import android.view.View
import android.view.ViewPropertyAnimator
@@ -77,9 +78,11 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.inOrder
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.same
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when` as whenever
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever

private val EMPTY_CHANGES = ConstraintsChanges()

@@ -133,6 +136,7 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {

    @Mock
    private lateinit var mockedContext: Context
    private lateinit var viewContext: Context
    @Mock(answer = Answers.RETURNS_MOCKS)
    private lateinit var view: MotionLayout

@@ -143,6 +147,7 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
    @Mock
    private lateinit var largeScreenConstraints: ConstraintSet
    @Mock private lateinit var demoModeController: DemoModeController
    @Mock private lateinit var qsBatteryModeController: QsBatteryModeController

    @JvmField @Rule
    val mockitoRule = MockitoJUnit.rule()
@@ -175,7 +180,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
            .thenReturn(qsCarrierGroupControllerBuilder)
        whenever(qsCarrierGroupControllerBuilder.build()).thenReturn(qsCarrierGroupController)

        whenever(view.context).thenReturn(context)
        viewContext = spy(context)
        whenever(view.context).thenReturn(viewContext)
        whenever(view.resources).thenReturn(context.resources)
        whenever(view.setVisibility(ArgumentMatchers.anyInt())).then {
            viewVisibility = it.arguments[0] as Int
@@ -204,7 +210,8 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
                featureFlags,
                qsCarrierGroupControllerBuilder,
                combinedShadeHeadersConstraintManager,
            demoModeController
                demoModeController,
                qsBatteryModeController,
        )
        whenever(view.isAttachedToWindow).thenReturn(true)
        controller.init()
@@ -218,13 +225,29 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {

        verify(batteryMeterViewController).init()
        verify(batteryMeterViewController).ignoreTunerUpdates()
        verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)

        val inOrder = inOrder(qsCarrierGroupControllerBuilder)
        inOrder.verify(qsCarrierGroupControllerBuilder).setQSCarrierGroup(carrierGroup)
        inOrder.verify(qsCarrierGroupControllerBuilder).build()
    }

    @Test
    fun `battery mode controller called when qsExpandedFraction changes`() {
        whenever(qsBatteryModeController.getBatteryMode(same(null), eq(0f)))
                .thenReturn(BatteryMeterView.MODE_ON)
        whenever(qsBatteryModeController.getBatteryMode(same(null), eq(1f)))
                .thenReturn(BatteryMeterView.MODE_ESTIMATE)
        controller.qsVisible = true

        val times = 10
        repeat(times) {
            controller.qsExpandedFraction = it / (times - 1).toFloat()
        }

        verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ON)
        verify(batteryMeterView).setPercentShowMode(BatteryMeterView.MODE_ESTIMATE)
    }

    @Test
    fun testClockPivotLtr() {
        val width = 200
@@ -684,11 +707,11 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
        configurationController.notifyDensityOrFontScaleChanged()

        val captor = ArgumentCaptor.forClass(XmlResourceParser::class.java)
        verify(qqsConstraints).load(eq(context), capture(captor))
        verify(qqsConstraints).load(eq(viewContext), capture(captor))
        assertThat(captor.value.getResId()).isEqualTo(R.xml.qqs_header)
        verify(qsConstraints).load(eq(context), capture(captor))
        verify(qsConstraints).load(eq(viewContext), capture(captor))
        assertThat(captor.value.getResId()).isEqualTo(R.xml.qs_header)
        verify(largeScreenConstraints).load(eq(context), capture(captor))
        verify(largeScreenConstraints).load(eq(viewContext), capture(captor))
        assertThat(captor.value.getResId()).isEqualTo(R.xml.large_screen_shade_header)
    }

@@ -786,6 +809,13 @@ class LargeScreenShadeHeaderControllerCombinedTest : SysuiTestCase() {
        whenever(insetsProvider.getStatusBarContentInsetsForCurrentRotation())
            .thenReturn(Pair(0, 0).toAndroidPair())
        whenever(insetsProvider.currentRotationHasCornerCutout()).thenReturn(false)
        setupCurrentInsets(null)
    }

    private fun setupCurrentInsets(cutout: DisplayCutout?) {
        val mockedDisplay =
                mock<Display>().also { display -> whenever(display.cutout).thenReturn(cutout) }
        whenever(viewContext.display).thenReturn(mockedDisplay)
    }

    private fun<T, U> Pair<T, U>.toAndroidPair(): android.util.Pair<T, U> {
+4 −2
Original line number Diff line number Diff line
@@ -76,6 +76,7 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {

    @Mock private lateinit var mockedContext: Context
    @Mock private lateinit var demoModeController: DemoModeController
    @Mock private lateinit var qsBatteryModeController: QsBatteryModeController

    @JvmField @Rule val mockitoRule = MockitoJUnit.rule()
    var viewVisibility = View.GONE
@@ -130,7 +131,8 @@ class LargeScreenShadeHeaderControllerTest : SysuiTestCase() {
                featureFlags,
                qsCarrierGroupControllerBuilder,
                combinedShadeHeadersConstraintManager,
                demoModeController
                demoModeController,
                qsBatteryModeController,
        )
        whenever(view.isAttachedToWindow).thenReturn(true)
        mLargeScreenShadeHeaderController.init()
Loading