summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Christian Göllner <chrisgollner@google.com> 2022-08-11 14:08:28 +0000
committer Android (Google) Code Review <android-gerrit@google.com> 2022-08-11 14:08:28 +0000
commit910d009002d30c36a6b85284af4ae6c796018f72 (patch)
tree53009962153424d6ad8ba6180fe7ce6d99aaa32c
parent6ac23a11f99f1785ac3128a9c919afd7f482d867 (diff)
parentd6a6294fe422bf901ea3551bc0aa985a569f99f0 (diff)
Merge "[Split shade] Fix QS actions footer border visible on shade expansion" into tm-qpr-dev
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt57
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt16
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/QSFragment.java8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt189
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java34
5 files changed, 255 insertions, 49 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index d701f33c4c66..c790cfe7b7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -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) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
index 05038b7b1b1d..309ac2a66e6b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -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)" \ No newline at end of file
+ get() = "($id): ($x,$y)"
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 8c5e6cc63147..139fb8b0bc14 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -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);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 642e29b364d3..2ba8782c6d02 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -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
+ }
}
-} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 32c66d25d4aa..10f6ce8c0ec9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -205,6 +205,40 @@ public class QSFragmentTest extends SysuiBaseFragmentTest {
}
@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();
disableSplitShade();