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

Commit 5a9624dc authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Fix unlocked split shade jumping back into place after over expanding"...

Merge "Fix unlocked split shade jumping back into place after over expanding" into tm-qpr-dev am: 2429c980

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/21510345



Change-Id: I22df5dd516991be186638a32d6bfe92f0e225858
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents f2c72fe0 2429c980
Loading
Loading
Loading
Loading
+10 −6
Original line number Diff line number Diff line
@@ -1915,7 +1915,6 @@ public final class NotificationPanelViewController implements Dumpable {
        // we want to perform an overshoot animation when flinging open
        final boolean addOverscroll =
                expand
                        && !mSplitShadeEnabled // Split shade has its own overscroll logic
                        && mStatusBarStateController.getState() != KEYGUARD
                        && mOverExpansion == 0.0f
                        && vel >= 0;
@@ -2591,9 +2590,14 @@ public final class NotificationPanelViewController implements Dumpable {
            return;
        }
        mOverExpansion = overExpansion;
        // Translating the quick settings by half the overexpansion to center it in the background
        // frame
        if (mSplitShadeEnabled) {
            mQsController.setOverScrollAmount((int) overExpansion);
            mScrimController.setNotificationsOverScrollAmount((int) overExpansion);
        } else {
            // Translating the quick settings by half the overexpansion to center it in the
            // background frame
            mQsController.updateQsFrameTranslation();
        }
        mNotificationStackScrollLayoutController.setOverExpansion(overExpansion);
    }

@@ -3595,7 +3599,7 @@ public final class NotificationPanelViewController implements Dumpable {

    private void fling(float vel, boolean expand, float collapseSpeedUpFactor,
            boolean expandBecauseOfFalsing) {
        float target = expand ? getMaxPanelHeight() : 0;
        float target = expand ? getMaxPanelTransitionDistance() : 0;
        if (!expand) {
            setClosing(true);
        }
@@ -3680,7 +3684,7 @@ public final class NotificationPanelViewController implements Dumpable {
            float maxPanelHeight = getMaxPanelTransitionDistance();
            if (mHeightAnimator == null) {
                // Split shade has its own overscroll logic
                if (mTracking && !mSplitShadeEnabled) {
                if (mTracking) {
                    float overExpansionPixels = Math.max(0, h - maxPanelHeight);
                    setOverExpansionInternal(overExpansionPixels, true /* isFromGesture */);
                }
+4 −0
Original line number Diff line number Diff line
@@ -803,6 +803,10 @@ public class QuickSettingsController {
        }
    }

    void setOverScrollAmount(int overExpansion) {
        mQs.setOverScrollAmount(overExpansion);
    }

    private void setOverScrolling(boolean overscrolling) {
        mStackScrollerOverscrolling = overscrolling;
        if (mQs != null) {
+0 −20
Original line number Diff line number Diff line
@@ -43,8 +43,6 @@ constructor(
    shadeExpansionStateManager: ShadeExpansionStateManager,
    dumpManager: DumpManager,
    private val context: Context,
    private val splitShadeOverScrollerFactory: SplitShadeOverScroller.Factory,
    private val noOpOverScroller: NoOpOverScroller,
    private val scrimShadeTransitionController: ScrimShadeTransitionController,
    private val statusBarStateController: SysuiStatusBarStateController,
) {
@@ -57,17 +55,6 @@ constructor(
    private var currentPanelState: Int? = null
    private var lastShadeExpansionChangeEvent: ShadeExpansionChangeEvent? = null

    private val splitShadeOverScroller by lazy {
        splitShadeOverScrollerFactory.create({ qs }, { notificationStackScrollLayoutController })
    }
    private val shadeOverScroller: ShadeOverScroller
        get() =
            if (inSplitShade && isScreenUnlocked() && propertiesInitialized()) {
                splitShadeOverScroller
            } else {
                noOpOverScroller
            }

    init {
        updateResources()
        configurationController.addCallback(
@@ -89,21 +76,14 @@ constructor(

    private fun onPanelStateChanged(@PanelState state: Int) {
        currentPanelState = state
        shadeOverScroller.onPanelStateChanged(state)
        scrimShadeTransitionController.onPanelStateChanged(state)
    }

    private fun onPanelExpansionChanged(event: ShadeExpansionChangeEvent) {
        lastShadeExpansionChangeEvent = event
        shadeOverScroller.onDragDownAmountChanged(event.dragDownPxAmount)
        scrimShadeTransitionController.onPanelExpansionChanged(event)
    }

    private fun propertiesInitialized() =
        this::qs.isInitialized &&
            this::notificationPanelViewController.isInitialized &&
            this::notificationStackScrollLayoutController.isInitialized

    private fun dump(pw: PrintWriter) {
        pw.println(
            """
+0 −167
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.systemui.shade.transition

import android.animation.Animator
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.Configuration
import android.util.MathUtils
import com.android.internal.annotations.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.shade.PanelState
import com.android.systemui.shade.STATE_CLOSED
import com.android.systemui.shade.STATE_OPENING
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.phone.ScrimController
import com.android.systemui.statusbar.policy.ConfigurationController
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import java.io.PrintWriter

class SplitShadeOverScroller
@AssistedInject
constructor(
    configurationController: ConfigurationController,
    dumpManager: DumpManager,
    private val context: Context,
    private val scrimController: ScrimController,
    @Assisted private val qSProvider: () -> QS,
    @Assisted private val nsslControllerProvider: () -> NotificationStackScrollLayoutController
) : ShadeOverScroller {

    private var releaseOverScrollDuration = 0L
    private var maxOverScrollAmount = 0
    private var previousOverscrollAmount = 0
    private var dragDownAmount: Float = 0f
    @PanelState private var panelState: Int = STATE_CLOSED

    private var releaseOverScrollAnimator: Animator? = null

    private val qS: QS
        get() = qSProvider()

    private val nsslController: NotificationStackScrollLayoutController
        get() = nsslControllerProvider()

    init {
        updateResources()
        configurationController.addCallback(
            object : ConfigurationController.ConfigurationListener {
                override fun onConfigChanged(newConfig: Configuration?) {
                    updateResources()
                }
            })
        dumpManager.registerCriticalDumpable("SplitShadeOverScroller") { printWriter, _ ->
            dump(printWriter)
        }
    }

    private fun updateResources() {
        val resources = context.resources
        maxOverScrollAmount = resources.getDimensionPixelSize(R.dimen.shade_max_over_scroll_amount)
        releaseOverScrollDuration =
            resources.getInteger(R.integer.lockscreen_shade_over_scroll_release_duration).toLong()
    }

    override fun onPanelStateChanged(@PanelState newPanelState: Int) {
        if (shouldReleaseOverscroll(previousState = panelState, newState = newPanelState)) {
            releaseOverScroll()
        }
        panelState = newPanelState
    }

    override fun onDragDownAmountChanged(newDragDownAmount: Float) {
        if (dragDownAmount == newDragDownAmount) {
            return
        }
        dragDownAmount = newDragDownAmount
        if (shouldOverscroll()) {
            overScroll(newDragDownAmount)
        }
    }

    private fun shouldOverscroll() = panelState == STATE_OPENING

    private fun shouldReleaseOverscroll(@PanelState previousState: Int, @PanelState newState: Int) =
        previousState == STATE_OPENING && newState != STATE_OPENING

    private fun overScroll(dragDownAmount: Float) {
        val overscrollAmount: Int = calculateOverscrollAmount(dragDownAmount)
        applyOverscroll(overscrollAmount)
        previousOverscrollAmount = overscrollAmount
    }

    private fun calculateOverscrollAmount(dragDownAmount: Float): Int {
        val fullHeight: Int = nsslController.height
        val fullHeightProgress: Float = MathUtils.saturate(dragDownAmount / fullHeight)
        return (fullHeightProgress * maxOverScrollAmount).toInt()
    }

    private fun applyOverscroll(overscrollAmount: Int) {
        qS.setOverScrollAmount(overscrollAmount)
        scrimController.setNotificationsOverScrollAmount(overscrollAmount)
        nsslController.setOverScrollAmount(overscrollAmount)
    }

    private fun releaseOverScroll() {
        val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0)
        animator.addUpdateListener {
            val overScrollAmount = it.animatedValue as Int
            qS.setOverScrollAmount(overScrollAmount)
            scrimController.setNotificationsOverScrollAmount(overScrollAmount)
            nsslController.setOverScrollAmount(overScrollAmount)
        }
        animator.interpolator = Interpolators.STANDARD
        animator.duration = releaseOverScrollDuration
        animator.start()
        releaseOverScrollAnimator = animator
        previousOverscrollAmount = 0
    }

    @VisibleForTesting
    internal fun finishAnimations() {
        releaseOverScrollAnimator?.end()
        releaseOverScrollAnimator = null
    }

    private fun dump(pw: PrintWriter) {
        pw.println(
            """
            SplitShadeOverScroller:
                Resources:
                    releaseOverScrollDuration: $releaseOverScrollDuration
                    maxOverScrollAmount: $maxOverScrollAmount
                State:
                    previousOverscrollAmount: $previousOverscrollAmount
                    dragDownAmount: $dragDownAmount
                    panelState: $panelState
            """.trimIndent())
    }

    @AssistedFactory
    fun interface Factory {
        fun create(
            qSProvider: () -> QS,
            nsslControllerProvider: () -> NotificationStackScrollLayoutController
        ): SplitShadeOverScroller
    }
}
+0 −130
Original line number Diff line number Diff line
@@ -2,37 +2,24 @@ package com.android.systemui.shade.transition

import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
import com.android.systemui.plugins.qs.QS
import com.android.systemui.shade.NotificationPanelViewController
import com.android.systemui.shade.STATE_OPENING
import com.android.systemui.shade.ShadeExpansionChangeEvent
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
import com.android.systemui.statusbar.policy.FakeConfigurationController
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations

@RunWith(AndroidTestingRunner::class)
@SmallTest
class ShadeTransitionControllerTest : SysuiTestCase() {

    @Mock private lateinit var npvc: NotificationPanelViewController
    @Mock private lateinit var nsslController: NotificationStackScrollLayoutController
    @Mock private lateinit var qs: QS
    @Mock private lateinit var noOpOverScroller: NoOpOverScroller
    @Mock private lateinit var splitShadeOverScroller: SplitShadeOverScroller
    @Mock private lateinit var scrimShadeTransitionController: ScrimShadeTransitionController
    @Mock private lateinit var dumpManager: DumpManager
    @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
@@ -52,119 +39,19 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
                shadeExpansionStateManager,
                dumpManager,
                context,
                splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
                noOpOverScroller,
                scrimShadeTransitionController,
                statusBarStateController,
            )

        // Resetting as they are notified upon initialization.
        reset(noOpOverScroller, splitShadeOverScroller)
    }

    @Test
    fun onPanelExpansionChanged_inSplitShade_forwardsToSplitShadeOverScroller() {
        initLateProperties()
        enableSplitShade()

        startPanelExpansion()

        verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING)
        verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
        verifyZeroInteractions(noOpOverScroller)
    }

    @Test
    fun onPanelStateChanged_inSplitShade_propertiesNotInitialized_forwardsToNoOpOverScroller() {
        enableSplitShade()

        startPanelExpansion()

        verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
        verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
        verifyZeroInteractions(splitShadeOverScroller)
    }

    @Test
    fun onPanelStateChanged_inSplitShade_onKeyguard_forwardsToNoOpOverScroller() {
        initLateProperties()
        enableSplitShade()
        setOnKeyguard()

        startPanelExpansion()

        verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
        verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
        verifyZeroInteractions(splitShadeOverScroller)
    }

    @Test
    fun onPanelStateChanged_inSplitShade_onLockedShade_forwardsToNoOpOverScroller() {
        initLateProperties()
        enableSplitShade()
        setOnLockedShade()

        startPanelExpansion()

        verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
        verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
        verifyZeroInteractions(splitShadeOverScroller)
    }

    @Test
    fun onPanelExpansionChanged_inSplitShade_onUnlockedShade_forwardsToSplitShadeOverScroller() {
        initLateProperties()
        enableSplitShade()
        setOnUnlockedShade()

        startPanelExpansion()

        verify(splitShadeOverScroller).onPanelStateChanged(STATE_OPENING)
        verify(splitShadeOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
        verifyZeroInteractions(noOpOverScroller)
    }

    @Test
    fun onPanelStateChanged_notInSplitShade_forwardsToNoOpOverScroller() {
        initLateProperties()
        disableSplitShade()

        startPanelExpansion()

        verify(noOpOverScroller).onPanelStateChanged(STATE_OPENING)
        verify(noOpOverScroller).onDragDownAmountChanged(DEFAULT_DRAG_DOWN_AMOUNT)
        verifyZeroInteractions(splitShadeOverScroller)
    }

    @Test
    fun onPanelStateChanged_forwardsToScrimTransitionController() {
        initLateProperties()

        startPanelExpansion()

        verify(scrimShadeTransitionController).onPanelStateChanged(STATE_OPENING)
        verify(scrimShadeTransitionController).onPanelExpansionChanged(DEFAULT_EXPANSION_EVENT)
    }

    private fun initLateProperties() {
        controller.qs = qs
        controller.notificationStackScrollLayoutController = nsslController
        controller.notificationPanelViewController = npvc
    }

    private fun disableSplitShade() {
        setSplitShadeEnabled(false)
    }

    private fun enableSplitShade() {
        setSplitShadeEnabled(true)
    }

    private fun setSplitShadeEnabled(enabled: Boolean) {
        overrideResource(R.bool.config_use_split_notification_shade, enabled)
        configurationController.notifyConfigurationChanged()
    }

    private fun startPanelExpansion() {
        shadeExpansionStateManager.onPanelExpansionChanged(
            DEFAULT_EXPANSION_EVENT.fraction,
@@ -174,23 +61,6 @@ class ShadeTransitionControllerTest : SysuiTestCase() {
        )
    }

    private fun setOnKeyguard() {
        setShadeState(StatusBarState.KEYGUARD)
    }

    private fun setOnLockedShade() {
        setShadeState(StatusBarState.SHADE_LOCKED)
    }

    private fun setOnUnlockedShade() {
        setShadeState(StatusBarState.SHADE)
    }

    private fun setShadeState(state: Int) {
        whenever(statusBarStateController.state).thenReturn(state)
        whenever(statusBarStateController.currentOrUpcomingState).thenReturn(state)
    }

    companion object {
        private const val DEFAULT_DRAG_DOWN_AMOUNT = 123f
        private val DEFAULT_EXPANSION_EVENT =
Loading