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

Commit 7f657604 authored by Selim Cinek's avatar Selim Cinek
Browse files

Fixes an issue where the media players would animate on screen off

Because qs collapses when we start dozing, we triggered an animation
that looked janky. We now instead don't reattach them at all
and block the animation.

Test: atest SystemUItests
Bug: 154137987
Change-Id: I75a386d7f1dfb65c040d42b8d7aeaf34314b5d10
parent 2de5ebb4
Loading
Loading
Loading
Loading
+69 −1
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import android.view.View
import android.view.ViewGroup
import android.view.ViewGroupOverlay
import com.android.systemui.Interpolators
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
@@ -49,7 +50,8 @@ class MediaHierarchyManager @Inject constructor(
    private val keyguardStateController: KeyguardStateController,
    private val bypassController: KeyguardBypassController,
    private val mediaViewManager: MediaViewManager,
    private val notifLockscreenUserManager: NotificationLockscreenUserManager
    private val notifLockscreenUserManager: NotificationLockscreenUserManager,
    wakefulnessLifecycle: WakefulnessLifecycle
) {
    /**
     * The root overlay of the hierarchy. This is where the media notification is attached to
@@ -137,6 +139,40 @@ class MediaHierarchyManager @Inject constructor(
            }
        }

    /**
     * Are location changes currently blocked?
     */
    private val blockLocationChanges: Boolean
        get() {
            return goingToSleep || dozeAnimationRunning
        }

    /**
     * Are we currently going to sleep
     */
    private var goingToSleep: Boolean = false
        set(value) {
            if (field != value) {
                field = value
                if (!value) {
                    updateDesiredLocation()
                }
            }
        }

    /**
     * Is the doze animation currently Running
     */
    private var dozeAnimationRunning: Boolean = false
        private set(value) {
            if (field != value) {
                field = value
                if (!value) {
                    updateDesiredLocation()
                }
            }
        }

    init {
        statusBarStateController.addCallback(object : StatusBarStateController.StateListener {
            override fun onStatePreChange(oldState: Int, newState: Int) {
@@ -149,6 +185,34 @@ class MediaHierarchyManager @Inject constructor(
            override fun onStateChanged(newState: Int) {
                updateTargetState()
            }

            override fun onDozeAmountChanged(linear: Float, eased: Float) {
                dozeAnimationRunning = linear != 0.0f && linear != 1.0f
            }

            override fun onDozingChanged(isDozing: Boolean) {
                if (!isDozing) {
                    dozeAnimationRunning = false
                }
            }
        })

        wakefulnessLifecycle.addObserver(object : WakefulnessLifecycle.Observer {
            override fun onFinishedGoingToSleep() {
                goingToSleep = false
            }

            override fun onStartedGoingToSleep() {
                goingToSleep = true
            }

            override fun onFinishedWakingUp() {
                goingToSleep = false
            }

            override fun onStartedWakingUp() {
                goingToSleep = false
            }
        })
    }

@@ -428,6 +492,10 @@ class MediaHierarchyManager @Inject constructor(

    @MediaLocation
    private fun calculateLocation(): Int {
        if (blockLocationChanges) {
            // Keep the current location until we're allowed to again
            return desiredLocation
        }
        val onLockscreen = (!bypassController.bypassEnabled &&
                (statusbarState == StatusBarState.KEYGUARD ||
                        statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
+145 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.media

import android.graphics.Rect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.ViewGroup
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.controller.ControlsControllerImplTest.Companion.eq
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.NotificationLockscreenUserManager
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.SysuiStatusBarStateController
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.animation.UniqueObjectHostView
import org.junit.Assert.assertNotNull
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.any
import org.mockito.Mockito.anyBoolean
import org.mockito.Mockito.anyLong
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit

@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class MediaHierarchyManagerTest : SysuiTestCase() {

    @Mock
    private lateinit var lockHost: MediaHost
    @Mock
    private lateinit var qsHost: MediaHost
    @Mock
    private lateinit var qqsHost: MediaHost
    @Mock
    private lateinit var bypassController: KeyguardBypassController
    @Mock
    private lateinit var mediaFrame: ViewGroup
    @Mock
    private lateinit var keyguardStateController: KeyguardStateController
    @Mock
    private lateinit var statusBarStateController: SysuiStatusBarStateController
    @Mock
    private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
    @Mock
    private lateinit var mediaViewManager: MediaViewManager
    @Mock
    private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
    @Captor
    private lateinit var wakefullnessObserver: ArgumentCaptor<(WakefulnessLifecycle.Observer)>
    @JvmField
    @Rule
    val mockito = MockitoJUnit.rule()
    private lateinit var mediaHiearchyManager: MediaHierarchyManager

    @Before
    fun setup() {
        `when`(mediaViewManager.mediaFrame).thenReturn(mediaFrame)
        mediaHiearchyManager = MediaHierarchyManager(
                context,
                statusBarStateController,
                keyguardStateController,
                bypassController,
                mediaViewManager,
                notificationLockscreenUserManager,
                wakefulnessLifecycle)
        verify(wakefulnessLifecycle).addObserver(wakefullnessObserver.capture())
        setupHost(lockHost, MediaHierarchyManager.LOCATION_LOCKSCREEN)
        setupHost(qsHost, MediaHierarchyManager.LOCATION_QS)
        setupHost(qqsHost, MediaHierarchyManager.LOCATION_QQS)
        `when`(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
        // We'll use the viewmanager to verify a few calls below, let's reset this.
        clearInvocations(mediaViewManager)

    }

    private fun setupHost(host: MediaHost, location: Int) {
        `when`(host.location).thenReturn(location)
        `when`(host.currentBounds).thenReturn(Rect())
        `when`(host.hostView).thenReturn(UniqueObjectHostView(context))
        mediaHiearchyManager.register(host)
    }

    @Test
    fun testHostViewSetOnRegister() {
        val host = mediaHiearchyManager.register(lockHost)
        verify(lockHost).hostView = eq(host)
    }

    @Test
    fun testBlockedWhenScreenTurningOff() {
        // Let's set it onto QS:
        mediaHiearchyManager.qsExpansion = 1.0f
        verify(mediaViewManager).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
        val observer = wakefullnessObserver.value
        assertNotNull("lifecycle observer wasn't registered", observer)
        observer.onStartedGoingToSleep()
        clearInvocations(mediaViewManager)
        mediaHiearchyManager.qsExpansion = 0.0f
        verify(mediaViewManager, times(0)).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
    }

    @Test
    fun testAllowedWhenNotTurningOff() {
        // Let's set it onto QS:
        mediaHiearchyManager.qsExpansion = 1.0f
        verify(mediaViewManager).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
        val observer = wakefullnessObserver.value
        assertNotNull("lifecycle observer wasn't registered", observer)
        clearInvocations(mediaViewManager)
        mediaHiearchyManager.qsExpansion = 0.0f
        verify(mediaViewManager).onDesiredLocationChanged(ArgumentMatchers.anyInt(),
                any(MediaHostState::class.java), anyBoolean(), anyLong(), anyLong())
    }
}
 No newline at end of file