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:
@@ -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) {
|
||||
|
||||
@@ -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)"
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user