Merge "[Split shade] Fix QS actions footer border visible on shade expansion" into tm-qpr-dev am: 910d009002 am: 819d56b3ba

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

Change-Id: I8dd8140c4cee0f29f09a310ec26672c6d0279e31
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Christian Göllner
2022-08-11 15:07:44 +00:00
committed by Automerger Merge Worker
5 changed files with 255 additions and 49 deletions

View File

@@ -17,6 +17,7 @@
package com.android.systemui.qs
import android.content.Intent
import android.content.res.Configuration
import android.os.Handler
import android.os.UserManager
import android.provider.Settings
@@ -38,9 +39,11 @@ import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
import com.android.systemui.qs.dagger.QSScope
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
import com.android.systemui.util.LargeScreenUtils
import com.android.systemui.util.ViewController
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
@@ -69,18 +72,43 @@ internal class FooterActionsController @Inject constructor(
private val uiEventLogger: UiEventLogger,
@Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
private val globalSetting: GlobalSettings,
private val handler: Handler
private val handler: Handler,
private val configurationController: ConfigurationController,
) : ViewController<FooterActionsView>(view) {
private var globalActionsDialog: GlobalActionsDialogLite? = null
private var lastExpansion = -1f
private var listening: Boolean = false
private var inSplitShade = false
private val alphaAnimator = TouchAnimator.Builder()
.addFloat(mView, "alpha", 0f, 1f)
.setStartDelay(0.9f)
private val singleShadeAnimator by lazy {
// In single shade, the actions footer should only appear at the end of the expansion,
// so that it doesn't overlap with the notifications panel.
TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).setStartDelay(0.9f).build()
}
private val splitShadeAnimator by lazy {
// The Actions footer view has its own background which is the same color as the qs panel's
// background.
// We don't want it to fade in at the same time as the rest of the panel, otherwise it is
// more opaque than the rest of the panel's background. Only applies to split shade.
val alphaAnimator = TouchAnimator.Builder().addFloat(mView, "alpha", 0f, 1f).build()
val bgAlphaAnimator =
TouchAnimator.Builder()
.addFloat(mView, "backgroundAlpha", 0f, 1f)
.setStartDelay(0.9f)
.build()
// In split shade, we want the actions footer to fade in exactly at the same time as the
// rest of the shade, as there is no overlap.
TouchAnimator.Builder()
.addFloat(alphaAnimator, "position", 0f, 1f)
.addFloat(bgAlphaAnimator, "position", 0f, 1f)
.build()
}
private val animators: TouchAnimator
get() = if (inSplitShade) splitShadeAnimator else singleShadeAnimator
var visible = true
set(value) {
@@ -95,9 +123,7 @@ internal class FooterActionsController @Inject constructor(
private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view)
@VisibleForTesting
internal val securityFootersSeparator = View(context).apply {
visibility = View.GONE
}
internal val securityFootersSeparator = View(context).apply { visibility = View.GONE }
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
@@ -133,6 +159,17 @@ internal class FooterActionsController @Inject constructor(
}
}
private val configurationListener =
object : ConfigurationController.ConfigurationListener {
override fun onConfigChanged(newConfig: Configuration?) {
updateResources()
}
}
private fun updateResources() {
inSplitShade = LargeScreenUtils.shouldUseSplitNotificationShade(resources)
}
override fun onInit() {
multiUserSwitchController.init()
securityFooterController.init()
@@ -189,6 +226,9 @@ internal class FooterActionsController @Inject constructor(
securityFooterController.setOnVisibilityChangedListener(visibilityListener)
fgsManagerFooterController.setOnVisibilityChangedListener(visibilityListener)
configurationController.addCallback(configurationListener)
updateResources()
updateView()
}
@@ -201,6 +241,7 @@ internal class FooterActionsController @Inject constructor(
globalActionsDialog = null
setListening(false)
multiUserSetting.isListening = false
configurationController.removeCallback(configurationListener)
}
fun setListening(listening: Boolean) {
@@ -224,7 +265,7 @@ internal class FooterActionsController @Inject constructor(
}
fun setExpansion(headerExpansionFraction: Float) {
alphaAnimator.setPosition(headerExpansionFraction)
animators.setPosition(headerExpansionFraction)
}
fun setKeyguardShowing(showing: Boolean) {

View File

@@ -27,6 +27,7 @@ import android.view.MotionEvent
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import androidx.annotation.Keep
import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable
import com.android.systemui.R
@@ -45,6 +46,19 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private var qsDisabled = false
private var expansionAmount = 0f
/**
* Sets the alpha of the background of this view.
*
* Used from a [TouchAnimator] in the controller.
*/
var backgroundAlpha: Float = 1f
@Keep
set(value) {
field = value
background?.alpha = (value * 255).toInt()
}
@Keep get
override fun onFinishInflate() {
super.onFinishInflate()
settingsContainer = findViewById(R.id.settings_button_container)
@@ -117,4 +131,4 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private const val TAG = "FooterActionsView"
private val VERBOSE = Log.isLoggable(TAG, Log.VERBOSE)
private val MotionEvent.string
get() = "($id): ($x,$y)"
get() = "($id): ($x,$y)"

View File

@@ -557,9 +557,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
public void setQsExpansion(float expansion, float panelExpansionFraction,
float proposedTranslation, float squishinessFraction) {
float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
float progress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD
float alphaProgress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD
? mFullShadeProgress : panelExpansionFraction;
setAlphaAnimationProgress(mInSplitShade ? progress : 1);
setAlphaAnimationProgress(mInSplitShade ? alphaProgress : 1);
mContainer.setExpansion(expansion);
final float translationScaleY = (mInSplitShade
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
@@ -600,7 +600,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
}
mQSPanelController.setIsOnKeyguard(onKeyguard);
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
float footerActionsExpansion =
onKeyguardAndExpanded ? 1 : mInSplitShade ? alphaProgress : expansion;
mQSFooterActionController.setExpansion(footerActionsExpansion);
mQSPanelController.setRevealExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);

View File

@@ -22,13 +22,17 @@ import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.leaks.LeakCheckedTest
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat
import javax.inject.Provider
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
@@ -42,47 +46,38 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import javax.inject.Provider
import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
class FooterActionsControllerTest : LeakCheckedTest() {
@Mock
private lateinit var userManager: UserManager
@Mock
private lateinit var userTracker: UserTracker
@Mock
private lateinit var activityStarter: ActivityStarter
@Mock
private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock
private lateinit var userInfoController: UserInfoController
@Mock
private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
@Mock
private lateinit var multiUserSwitchController: MultiUserSwitchController
@Mock
private lateinit var globalActionsDialogProvider: Provider<GlobalActionsDialogLite>
@Mock
private lateinit var globalActionsDialog: GlobalActionsDialogLite
@Mock
private lateinit var uiEventLogger: UiEventLogger
@Mock
private lateinit var securityFooterController: QSSecurityFooter
@Mock
private lateinit var fgsManagerController: QSFgsManagerFooter
@get:Rule var expect: Expect = Expect.create()
@Mock private lateinit var userManager: UserManager
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var activityStarter: ActivityStarter
@Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
@Mock private lateinit var userInfoController: UserInfoController
@Mock private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
@Mock private lateinit var multiUserSwitchController: MultiUserSwitchController
@Mock private lateinit var globalActionsDialogProvider: Provider<GlobalActionsDialogLite>
@Mock private lateinit var globalActionsDialog: GlobalActionsDialogLite
@Mock private lateinit var uiEventLogger: UiEventLogger
@Mock private lateinit var securityFooterController: QSSecurityFooter
@Mock private lateinit var fgsManagerController: QSFgsManagerFooter
@Captor
private lateinit var visibilityChangedCaptor:
ArgumentCaptor<VisibilityChangedDispatcher.OnVisibilityChangedListener>
private lateinit var controller: FooterActionsController
private val configurationController = FakeConfigurationController()
private val metricsLogger: MetricsLogger = FakeMetricsLogger()
private lateinit var view: FooterActionsView
private val falsingManager: FalsingManagerFake = FalsingManagerFake()
private lateinit var view: FooterActionsView
private lateinit var testableLooper: TestableLooper
private lateinit var fakeSettings: FakeSettings
private lateinit var securityFooter: View
@@ -90,12 +85,15 @@ class FooterActionsControllerTest : LeakCheckedTest() {
@Before
fun setUp() {
// We want to make sure testable resources are always used
context.ensureTestableResources()
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
fakeSettings = FakeSettings()
whenever(multiUserSwitchControllerFactory.create(any()))
.thenReturn(multiUserSwitchController)
.thenReturn(multiUserSwitchController)
whenever(globalActionsDialogProvider.get()).thenReturn(globalActionsDialog)
securityFooter = View(mContext)
@@ -135,7 +133,7 @@ class FooterActionsControllerTest : LeakCheckedTest() {
view.findViewById<View>(R.id.pm_lite).performClick()
// Verify clicks are logged
verify(uiEventLogger, Mockito.times(1))
.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
}
@Test
@@ -299,6 +297,86 @@ class FooterActionsControllerTest : LeakCheckedTest() {
assertThat(booleanCaptor.allValues.last()).isTrue()
}
@Test
fun setExpansion_inSplitShade_alphaFollowsExpansion() {
enableSplitShade()
controller.setExpansion(0f)
expect.that(view.alpha).isEqualTo(0f)
controller.setExpansion(0.25f)
expect.that(view.alpha).isEqualTo(0.25f)
controller.setExpansion(0.5f)
expect.that(view.alpha).isEqualTo(0.5f)
controller.setExpansion(0.75f)
expect.that(view.alpha).isEqualTo(0.75f)
controller.setExpansion(1f)
expect.that(view.alpha).isEqualTo(1f)
}
@Test
fun setExpansion_inSplitShade_backgroundAlphaFollowsExpansion_with_0_9_delay() {
enableSplitShade()
controller.setExpansion(0f)
expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
controller.setExpansion(0.5f)
expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
controller.setExpansion(0.9f)
expect.that(view.backgroundAlphaFraction).isEqualTo(0f)
controller.setExpansion(0.91f)
expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.1f)
controller.setExpansion(0.95f)
expect.that(view.backgroundAlphaFraction).isWithin(FLOAT_TOLERANCE).of(0.5f)
controller.setExpansion(1f)
expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
}
@Test
fun setExpansion_inSingleShade_alphaFollowsExpansion_with_0_9_delay() {
disableSplitShade()
controller.setExpansion(0f)
expect.that(view.alpha).isEqualTo(0f)
controller.setExpansion(0.5f)
expect.that(view.alpha).isEqualTo(0f)
controller.setExpansion(0.9f)
expect.that(view.alpha).isEqualTo(0f)
controller.setExpansion(0.91f)
expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.1f)
controller.setExpansion(0.95f)
expect.that(view.alpha).isWithin(FLOAT_TOLERANCE).of(0.5f)
controller.setExpansion(1f)
expect.that(view.alpha).isEqualTo(1f)
}
@Test
fun setExpansion_inSingleShade_backgroundAlphaAlways1() {
disableSplitShade()
controller.setExpansion(0f)
expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
controller.setExpansion(0.5f)
expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
controller.setExpansion(1f)
expect.that(view.backgroundAlphaFraction).isEqualTo(1f)
}
private fun setVisibilities(
securityFooterVisible: Boolean,
fgsFooterVisible: Boolean,
@@ -311,15 +389,52 @@ class FooterActionsControllerTest : LeakCheckedTest() {
}
private fun inflateView(): FooterActionsView {
return LayoutInflater.from(context)
.inflate(R.layout.footer_actions, null) as FooterActionsView
return LayoutInflater.from(context).inflate(R.layout.footer_actions, null)
as FooterActionsView
}
private fun constructFooterActionsController(view: FooterActionsView): FooterActionsController {
return FooterActionsController(view, multiUserSwitchControllerFactory,
activityStarter, userManager, userTracker, userInfoController,
deviceProvisionedController, securityFooterController, fgsManagerController,
falsingManager, metricsLogger, globalActionsDialogProvider, uiEventLogger,
showPMLiteButton = true, fakeSettings, Handler(testableLooper.looper))
return FooterActionsController(
view,
multiUserSwitchControllerFactory,
activityStarter,
userManager,
userTracker,
userInfoController,
deviceProvisionedController,
securityFooterController,
fgsManagerController,
falsingManager,
metricsLogger,
globalActionsDialogProvider,
uiEventLogger,
showPMLiteButton = true,
fakeSettings,
Handler(testableLooper.looper),
configurationController)
}
private fun enableSplitShade() {
setSplitShadeEnabled(true)
}
private fun disableSplitShade() {
setSplitShadeEnabled(false)
}
private fun setSplitShadeEnabled(enabled: Boolean) {
overrideResource(R.bool.config_use_split_notification_shade, enabled)
configurationController.notifyConfigurationChanged()
}
}
private const val FLOAT_TOLERANCE = 0.01f
private val View.backgroundAlphaFraction: Float?
get() {
return if (background != null) {
background.alpha / 255f
} else {
null
}
}
}

View File

@@ -204,6 +204,40 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
assertThat(mQsFragmentView.getAlpha()).isEqualTo(1);
}
@Test
public void setQsExpansion_inSplitShade_setsFooterActionsExpansion_basedOnPanelExpFraction() {
// Random test values without any meaning. They just have to be different from each other.
float expansion = 0.123f;
float panelExpansionFraction = 0.321f;
float proposedTranslation = 456f;
float squishinessFraction = 0.987f;
QSFragment fragment = resumeAndGetFragment();
enableSplitShade();
fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
squishinessFraction);
verify(mQSFooterActionController).setExpansion(panelExpansionFraction);
}
@Test
public void setQsExpansion_notInSplitShade_setsFooterActionsExpansion_basedOnExpansion() {
// Random test values without any meaning. They just have to be different from each other.
float expansion = 0.123f;
float panelExpansionFraction = 0.321f;
float proposedTranslation = 456f;
float squishinessFraction = 0.987f;
QSFragment fragment = resumeAndGetFragment();
disableSplitShade();
fragment.setQsExpansion(expansion, panelExpansionFraction, proposedTranslation,
squishinessFraction);
verify(mQSFooterActionController).setExpansion(expansion);
}
@Test
public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() {
QSFragment fragment = resumeAndGetFragment();