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

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

Change-Id: I3876de17d23f2ef922664a85b902d66017c69d6f
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:03:38 +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 package com.android.systemui.qs
import android.content.Intent import android.content.Intent
import android.content.res.Configuration
import android.os.Handler import android.os.Handler
import android.os.UserManager import android.os.UserManager
import android.provider.Settings 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.qs.dagger.QSScope
import com.android.systemui.settings.UserTracker import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController 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.DeviceProvisionedController
import com.android.systemui.statusbar.policy.UserInfoController import com.android.systemui.statusbar.policy.UserInfoController
import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener 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.ViewController
import com.android.systemui.util.settings.GlobalSettings import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject import javax.inject.Inject
@@ -69,18 +72,43 @@ internal class FooterActionsController @Inject constructor(
private val uiEventLogger: UiEventLogger, private val uiEventLogger: UiEventLogger,
@Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean, @Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
private val globalSetting: GlobalSettings, private val globalSetting: GlobalSettings,
private val handler: Handler private val handler: Handler,
private val configurationController: ConfigurationController,
) : ViewController<FooterActionsView>(view) { ) : ViewController<FooterActionsView>(view) {
private var globalActionsDialog: GlobalActionsDialogLite? = null private var globalActionsDialog: GlobalActionsDialogLite? = null
private var lastExpansion = -1f private var lastExpansion = -1f
private var listening: Boolean = false private var listening: Boolean = false
private var inSplitShade = false
private val alphaAnimator = TouchAnimator.Builder() private val singleShadeAnimator by lazy {
.addFloat(mView, "alpha", 0f, 1f) // In single shade, the actions footer should only appear at the end of the expansion,
.setStartDelay(0.9f) // 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() .build()
}
private val animators: TouchAnimator
get() = if (inSplitShade) splitShadeAnimator else singleShadeAnimator
var visible = true var visible = true
set(value) { set(value) {
@@ -95,9 +123,7 @@ internal class FooterActionsController @Inject constructor(
private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view) private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view)
@VisibleForTesting @VisibleForTesting
internal val securityFootersSeparator = View(context).apply { internal val securityFootersSeparator = View(context).apply { visibility = View.GONE }
visibility = View.GONE
}
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ -> private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser()) 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() { override fun onInit() {
multiUserSwitchController.init() multiUserSwitchController.init()
securityFooterController.init() securityFooterController.init()
@@ -189,6 +226,9 @@ internal class FooterActionsController @Inject constructor(
securityFooterController.setOnVisibilityChangedListener(visibilityListener) securityFooterController.setOnVisibilityChangedListener(visibilityListener)
fgsManagerFooterController.setOnVisibilityChangedListener(visibilityListener) fgsManagerFooterController.setOnVisibilityChangedListener(visibilityListener)
configurationController.addCallback(configurationListener)
updateResources()
updateView() updateView()
} }
@@ -201,6 +241,7 @@ internal class FooterActionsController @Inject constructor(
globalActionsDialog = null globalActionsDialog = null
setListening(false) setListening(false)
multiUserSetting.isListening = false multiUserSetting.isListening = false
configurationController.removeCallback(configurationListener)
} }
fun setListening(listening: Boolean) { fun setListening(listening: Boolean) {
@@ -224,7 +265,7 @@ internal class FooterActionsController @Inject constructor(
} }
fun setExpansion(headerExpansionFraction: Float) { fun setExpansion(headerExpansionFraction: Float) {
alphaAnimator.setPosition(headerExpansionFraction) animators.setPosition(headerExpansionFraction)
} }
fun setKeyguardShowing(showing: Boolean) { fun setKeyguardShowing(showing: Boolean) {

View File

@@ -27,6 +27,7 @@ import android.view.MotionEvent
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.annotation.Keep
import com.android.settingslib.Utils import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable import com.android.settingslib.drawable.UserIconDrawable
import com.android.systemui.R import com.android.systemui.R
@@ -45,6 +46,19 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private var qsDisabled = false private var qsDisabled = false
private var expansionAmount = 0f 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() { override fun onFinishInflate() {
super.onFinishInflate() super.onFinishInflate()
settingsContainer = findViewById(R.id.settings_button_container) settingsContainer = findViewById(R.id.settings_button_container)
@@ -117,4 +131,4 @@ class FooterActionsView(context: Context?, attrs: AttributeSet?) : LinearLayout(
private const val TAG = "FooterActionsView" private const val TAG = "FooterActionsView"
private val VERBOSE = Log.isLoggable(TAG, Log.VERBOSE) private val VERBOSE = Log.isLoggable(TAG, Log.VERBOSE)
private val MotionEvent.string 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, public void setQsExpansion(float expansion, float panelExpansionFraction,
float proposedTranslation, float squishinessFraction) { float proposedTranslation, float squishinessFraction) {
float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation; float headerTranslation = mTransitioningToFullShade ? 0 : proposedTranslation;
float progress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD float alphaProgress = mTransitioningToFullShade || mState == StatusBarState.KEYGUARD
? mFullShadeProgress : panelExpansionFraction; ? mFullShadeProgress : panelExpansionFraction;
setAlphaAnimationProgress(mInSplitShade ? progress : 1); setAlphaAnimationProgress(mInSplitShade ? alphaProgress : 1);
mContainer.setExpansion(expansion); mContainer.setExpansion(expansion);
final float translationScaleY = (mInSplitShade final float translationScaleY = (mInSplitShade
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1); ? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
@@ -600,7 +600,9 @@ public class QSFragment extends LifecycleFragment implements QS, CommandQueue.Ca
} }
mQSPanelController.setIsOnKeyguard(onKeyguard); mQSPanelController.setIsOnKeyguard(onKeyguard);
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion); mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion); float footerActionsExpansion =
onKeyguardAndExpanded ? 1 : mInSplitShade ? alphaProgress : expansion;
mQSFooterActionController.setExpansion(footerActionsExpansion);
mQSPanelController.setRevealExpansion(expansion); mQSPanelController.setRevealExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation); mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
mQuickQSPanelController.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.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.DeviceProvisionedController 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.statusbar.policy.UserInfoController
import com.android.systemui.util.mockito.capture import com.android.systemui.util.mockito.capture
import com.android.systemui.util.settings.FakeSettings import com.android.systemui.util.settings.FakeSettings
import com.android.systemui.utils.leaks.LeakCheckedTest import com.android.systemui.utils.leaks.LeakCheckedTest
import com.google.common.truth.Expect
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import javax.inject.Provider
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor import org.mockito.ArgumentCaptor
@@ -42,47 +46,38 @@ import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never import org.mockito.Mockito.never
import org.mockito.Mockito.reset import org.mockito.Mockito.reset
import org.mockito.Mockito.verify import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import javax.inject.Provider
import org.mockito.Mockito.`when` as whenever import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
@SmallTest @SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true) @TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class) @RunWith(AndroidTestingRunner::class)
class FooterActionsControllerTest : LeakCheckedTest() { class FooterActionsControllerTest : LeakCheckedTest() {
@Mock
private lateinit var userManager: UserManager @get:Rule var expect: Expect = Expect.create()
@Mock
private lateinit var userTracker: UserTracker @Mock private lateinit var userManager: UserManager
@Mock @Mock private lateinit var userTracker: UserTracker
private lateinit var activityStarter: ActivityStarter @Mock private lateinit var activityStarter: ActivityStarter
@Mock @Mock private lateinit var deviceProvisionedController: DeviceProvisionedController
private lateinit var deviceProvisionedController: DeviceProvisionedController @Mock private lateinit var userInfoController: UserInfoController
@Mock @Mock private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
private lateinit var userInfoController: UserInfoController @Mock private lateinit var multiUserSwitchController: MultiUserSwitchController
@Mock @Mock private lateinit var globalActionsDialogProvider: Provider<GlobalActionsDialogLite>
private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory @Mock private lateinit var globalActionsDialog: GlobalActionsDialogLite
@Mock @Mock private lateinit var uiEventLogger: UiEventLogger
private lateinit var multiUserSwitchController: MultiUserSwitchController @Mock private lateinit var securityFooterController: QSSecurityFooter
@Mock @Mock private lateinit var fgsManagerController: QSFgsManagerFooter
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 @Captor
private lateinit var visibilityChangedCaptor: private lateinit var visibilityChangedCaptor:
ArgumentCaptor<VisibilityChangedDispatcher.OnVisibilityChangedListener> ArgumentCaptor<VisibilityChangedDispatcher.OnVisibilityChangedListener>
private lateinit var controller: FooterActionsController private lateinit var controller: FooterActionsController
private val configurationController = FakeConfigurationController()
private val metricsLogger: MetricsLogger = FakeMetricsLogger() private val metricsLogger: MetricsLogger = FakeMetricsLogger()
private lateinit var view: FooterActionsView
private val falsingManager: FalsingManagerFake = FalsingManagerFake() private val falsingManager: FalsingManagerFake = FalsingManagerFake()
private lateinit var view: FooterActionsView
private lateinit var testableLooper: TestableLooper private lateinit var testableLooper: TestableLooper
private lateinit var fakeSettings: FakeSettings private lateinit var fakeSettings: FakeSettings
private lateinit var securityFooter: View private lateinit var securityFooter: View
@@ -90,12 +85,15 @@ class FooterActionsControllerTest : LeakCheckedTest() {
@Before @Before
fun setUp() { fun setUp() {
// We want to make sure testable resources are always used
context.ensureTestableResources()
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this) testableLooper = TestableLooper.get(this)
fakeSettings = FakeSettings() fakeSettings = FakeSettings()
whenever(multiUserSwitchControllerFactory.create(any())) whenever(multiUserSwitchControllerFactory.create(any()))
.thenReturn(multiUserSwitchController) .thenReturn(multiUserSwitchController)
whenever(globalActionsDialogProvider.get()).thenReturn(globalActionsDialog) whenever(globalActionsDialogProvider.get()).thenReturn(globalActionsDialog)
securityFooter = View(mContext) securityFooter = View(mContext)
@@ -135,7 +133,7 @@ class FooterActionsControllerTest : LeakCheckedTest() {
view.findViewById<View>(R.id.pm_lite).performClick() view.findViewById<View>(R.id.pm_lite).performClick()
// Verify clicks are logged // Verify clicks are logged
verify(uiEventLogger, Mockito.times(1)) verify(uiEventLogger, Mockito.times(1))
.log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS) .log(GlobalActionsDialogLite.GlobalActionsEvent.GA_OPEN_QS)
} }
@Test @Test
@@ -299,6 +297,86 @@ class FooterActionsControllerTest : LeakCheckedTest() {
assertThat(booleanCaptor.allValues.last()).isTrue() 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( private fun setVisibilities(
securityFooterVisible: Boolean, securityFooterVisible: Boolean,
fgsFooterVisible: Boolean, fgsFooterVisible: Boolean,
@@ -311,15 +389,52 @@ class FooterActionsControllerTest : LeakCheckedTest() {
} }
private fun inflateView(): FooterActionsView { private fun inflateView(): FooterActionsView {
return LayoutInflater.from(context) return LayoutInflater.from(context).inflate(R.layout.footer_actions, null)
.inflate(R.layout.footer_actions, null) as FooterActionsView as FooterActionsView
} }
private fun constructFooterActionsController(view: FooterActionsView): FooterActionsController { private fun constructFooterActionsController(view: FooterActionsView): FooterActionsController {
return FooterActionsController(view, multiUserSwitchControllerFactory, return FooterActionsController(
activityStarter, userManager, userTracker, userInfoController, view,
deviceProvisionedController, securityFooterController, fgsManagerController, multiUserSwitchControllerFactory,
falsingManager, metricsLogger, globalActionsDialogProvider, uiEventLogger, activityStarter,
showPMLiteButton = true, fakeSettings, Handler(testableLooper.looper)) 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); 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 @Test
public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() { public void getQsMinExpansionHeight_notInSplitShade_returnsHeaderHeight() {
QSFragment fragment = resumeAndGetFragment(); QSFragment fragment = resumeAndGetFragment();