Merge "[Status Bar] Display notif icon area during HUN even in lights out mode." into udc-qpr-dev

This commit is contained in:
Caitlin Shkuratov
2023-08-10 14:13:34 +00:00
committed by Android (Google) Code Review
4 changed files with 300 additions and 13 deletions

View File

@@ -81,6 +81,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
private final BiConsumer<Float, Float> mSetExpandedHeight = this::setAppearFraction;
private final KeyguardBypassController mBypassController;
private final StatusBarStateController mStatusBarStateController;
private final PhoneStatusBarTransitions mPhoneStatusBarTransitions;
private final CommandQueue mCommandQueue;
private final NotificationWakeUpCoordinator mWakeUpCoordinator;
@@ -109,6 +110,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
NotificationIconAreaController notificationIconAreaController,
HeadsUpManagerPhone headsUpManager,
StatusBarStateController stateController,
PhoneStatusBarTransitions phoneStatusBarTransitions,
KeyguardBypassController bypassController,
NotificationWakeUpCoordinator wakeUpCoordinator,
DarkIconDispatcher darkIconDispatcher,
@@ -156,6 +158,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
});
mBypassController = bypassController;
mStatusBarStateController = stateController;
mPhoneStatusBarTransitions = phoneStatusBarTransitions;
mWakeUpCoordinator = wakeUpCoordinator;
mCommandQueue = commandQueue;
mKeyguardStateController = keyguardStateController;
@@ -203,6 +206,7 @@ public class HeadsUpAppearanceController extends ViewController<HeadsUpStatusBar
@Override
public void onHeadsUpStateChanged(@NonNull NotificationEntry entry, boolean isHeadsUp) {
updateHeadsUpAndPulsingRoundness(entry);
mPhoneStatusBarTransitions.onHeadsUpStateChanged(isHeadsUp);
}
private void updateTopEntry() {

View File

@@ -31,6 +31,8 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
private final float mIconAlphaWhenOpaque;
private boolean mIsHeadsUp;
private View mStartSide, mStatusIcons, mBattery;
private Animator mCurrentAnimation;
@@ -52,15 +54,32 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
return ObjectAnimator.ofFloat(v, "alpha", v.getAlpha(), toAlpha);
}
private float getNonBatteryClockAlphaFor(int mode) {
return isLightsOut(mode) ? ICON_ALPHA_WHEN_LIGHTS_OUT_NON_BATTERY_CLOCK
: !isOpaque(mode) ? ICON_ALPHA_WHEN_NOT_OPAQUE
: mIconAlphaWhenOpaque;
private float getStatusIconsAlphaFor(int mode) {
return getDefaultAlphaFor(mode);
}
private float getStartSideAlphaFor(int mode) {
// When there's a heads up notification, we need the start side icons to show regardless of
// lights out mode.
if (mIsHeadsUp) {
return getIconAlphaBasedOnOpacity(mode);
}
return getDefaultAlphaFor(mode);
}
private float getBatteryClockAlpha(int mode) {
return isLightsOut(mode) ? ICON_ALPHA_WHEN_LIGHTS_OUT_BATTERY_CLOCK
: getNonBatteryClockAlphaFor(mode);
: getIconAlphaBasedOnOpacity(mode);
}
private float getDefaultAlphaFor(int mode) {
return isLightsOut(mode) ? ICON_ALPHA_WHEN_LIGHTS_OUT_NON_BATTERY_CLOCK
: getIconAlphaBasedOnOpacity(mode);
}
private float getIconAlphaBasedOnOpacity(int mode) {
return !isOpaque(mode) ? ICON_ALPHA_WHEN_NOT_OPAQUE
: mIconAlphaWhenOpaque;
}
private boolean isOpaque(int mode) {
@@ -74,19 +93,28 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
applyMode(newMode, animate);
}
/** Informs this controller that the heads up notification state has changed. */
public void onHeadsUpStateChanged(boolean isHeadsUp) {
mIsHeadsUp = isHeadsUp;
// We want the icon to be fully visible when the HUN appears, so just immediately change the
// icon visibility and don't animate.
applyMode(getMode(), /* animate= */ false);
}
private void applyMode(int mode, boolean animate) {
if (mStartSide == null) return; // pre-init
float newAlpha = getNonBatteryClockAlphaFor(mode);
float newAlphaBC = getBatteryClockAlpha(mode);
float newStartSideAlpha = getStartSideAlphaFor(mode);
float newStatusIconsAlpha = getStatusIconsAlphaFor(mode);
float newBatteryAlpha = getBatteryClockAlpha(mode);
if (mCurrentAnimation != null) {
mCurrentAnimation.cancel();
}
if (animate) {
AnimatorSet anims = new AnimatorSet();
anims.playTogether(
animateTransitionTo(mStartSide, newAlpha),
animateTransitionTo(mStatusIcons, newAlpha),
animateTransitionTo(mBattery, newAlphaBC)
animateTransitionTo(mStartSide, newStartSideAlpha),
animateTransitionTo(mStatusIcons, newStatusIconsAlpha),
animateTransitionTo(mBattery, newBatteryAlpha)
);
if (isLightsOut(mode)) {
anims.setDuration(LIGHTS_OUT_DURATION);
@@ -94,9 +122,9 @@ public final class PhoneStatusBarTransitions extends BarTransitions {
anims.start();
mCurrentAnimation = anims;
} else {
mStartSide.setAlpha(newAlpha);
mStatusIcons.setAlpha(newAlpha);
mBattery.setAlpha(newAlphaBC);
mStartSide.setAlpha(newStartSideAlpha);
mStatusIcons.setAlpha(newStatusIconsAlpha);
mBattery.setAlpha(newBatteryAlpha);
}
}
}

View File

@@ -75,6 +75,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
private HeadsUpManagerPhone mHeadsUpManager;
private View mOperatorNameView;
private StatusBarStateController mStatusbarStateController;
private PhoneStatusBarTransitions mPhoneStatusBarTransitions;
private KeyguardBypassController mBypassController;
private NotificationWakeUpCoordinator mWakeUpCoordinator;
private KeyguardStateController mKeyguardStateController;
@@ -95,6 +96,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mHeadsUpManager = mock(HeadsUpManagerPhone.class);
mOperatorNameView = new View(mContext);
mStatusbarStateController = mock(StatusBarStateController.class);
mPhoneStatusBarTransitions = mock(PhoneStatusBarTransitions.class);
mBypassController = mock(KeyguardBypassController.class);
mWakeUpCoordinator = mock(NotificationWakeUpCoordinator.class);
mKeyguardStateController = mock(KeyguardStateController.class);
@@ -105,6 +107,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mock(NotificationIconAreaController.class),
mHeadsUpManager,
mStatusbarStateController,
mPhoneStatusBarTransitions,
mBypassController,
mWakeUpCoordinator,
mDarkIconDispatcher,
@@ -188,6 +191,7 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
mock(NotificationIconAreaController.class),
mHeadsUpManager,
mStatusbarStateController,
mPhoneStatusBarTransitions,
mBypassController,
mWakeUpCoordinator,
mDarkIconDispatcher,
@@ -283,4 +287,18 @@ public class HeadsUpAppearanceControllerTest extends SysuiTestCase {
/* delta = */ 0.001
);
}
@Test
public void onHeadsUpStateChanged_true_transitionsNotified() {
mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, true);
verify(mPhoneStatusBarTransitions).onHeadsUpStateChanged(true);
}
@Test
public void onHeadsUpStateChanged_false_transitionsNotified() {
mHeadsUpAppearanceController.onHeadsUpStateChanged(mEntry, false);
verify(mPhoneStatusBarTransitions).onHeadsUpStateChanged(false);
}
}

View File

@@ -0,0 +1,237 @@
/*
* Copyright (C) 2023 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.statusbar.phone
import android.testing.TestableLooper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT
import com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT_TRANSPARENT
import com.android.systemui.statusbar.phone.BarTransitions.MODE_OPAQUE
import com.android.systemui.statusbar.phone.BarTransitions.MODE_SEMI_TRANSPARENT
import com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSLUCENT
import com.android.systemui.statusbar.phone.BarTransitions.MODE_TRANSPARENT
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class PhoneStatusBarTransitionsTest : SysuiTestCase() {
// PhoneStatusBarView does a lot of non-standard things when inflating, so just use mocks.
private val batteryView = mock<View>()
private val statusIcons = mock<View>()
private val startIcons = mock<View>()
private val statusBarView =
mock<PhoneStatusBarView>().apply {
whenever(this.context).thenReturn(mContext)
whenever(this.findViewById<View>(R.id.battery)).thenReturn(batteryView)
whenever(this.findViewById<View>(R.id.statusIcons)).thenReturn(statusIcons)
whenever(this.findViewById<View>(R.id.status_bar_start_side_except_heads_up))
.thenReturn(startIcons)
}
private val backgroundView = mock<View>().apply { whenever(this.context).thenReturn(mContext) }
private val underTest: PhoneStatusBarTransitions by lazy {
PhoneStatusBarTransitions(statusBarView, backgroundView).also {
// The views' alphas will be set when PhoneStatusBarTransitions is created and we want
// to ignore those in the tests, so clear those verifications here.
reset(batteryView)
reset(statusIcons)
reset(startIcons)
}
}
@Before
fun setUp() {
context.orCreateTestableResources.addOverride(
R.dimen.status_bar_icon_drawing_alpha,
RESOURCE_ALPHA,
)
}
@Test
fun transitionTo_lightsOutMode_batteryTranslucent() {
underTest.transitionTo(/* mode= */ MODE_LIGHTS_OUT, /* animate= */ false)
val alpha = batteryView.capturedAlpha()
assertThat(alpha).isGreaterThan(0)
assertThat(alpha).isLessThan(1)
}
@Test
fun transitionTo_lightsOutMode_statusIconsHidden() {
underTest.transitionTo(/* mode= */ MODE_LIGHTS_OUT, /* animate= */ false)
assertThat(statusIcons.capturedAlpha()).isEqualTo(0)
}
@Test
fun transitionTo_lightsOutMode_startIconsHidden() {
underTest.transitionTo(/* mode= */ MODE_LIGHTS_OUT, /* animate= */ false)
assertThat(startIcons.capturedAlpha()).isEqualTo(0)
}
@Test
fun transitionTo_lightsOutTransparentMode_batteryTranslucent() {
underTest.transitionTo(/* mode= */ MODE_LIGHTS_OUT_TRANSPARENT, /* animate= */ false)
val alpha = batteryView.capturedAlpha()
assertThat(alpha).isGreaterThan(0)
assertThat(alpha).isLessThan(1)
}
@Test
fun transitionTo_lightsOutTransparentMode_statusIconsHidden() {
underTest.transitionTo(/* mode= */ MODE_LIGHTS_OUT_TRANSPARENT, /* animate= */ false)
assertThat(statusIcons.capturedAlpha()).isEqualTo(0)
}
@Test
fun transitionTo_lightsOutTransparentMode_startIconsHidden() {
underTest.transitionTo(/* mode= */ MODE_LIGHTS_OUT_TRANSPARENT, /* animate= */ false)
assertThat(startIcons.capturedAlpha()).isEqualTo(0)
}
@Test
fun transitionTo_translucentMode_batteryIconShown() {
underTest.transitionTo(/* mode= */ MODE_TRANSLUCENT, /* animate= */ false)
assertThat(batteryView.capturedAlpha()).isEqualTo(1)
}
@Test
fun transitionTo_semiTransparentMode_statusIconsShown() {
underTest.transitionTo(/* mode= */ MODE_SEMI_TRANSPARENT, /* animate= */ false)
assertThat(statusIcons.capturedAlpha()).isEqualTo(1)
}
@Test
fun transitionTo_transparentMode_startIconsShown() {
// Transparent is the default, so we need to switch to a different mode first
underTest.transitionTo(/* mode= */ MODE_OPAQUE, /* animate= */ false)
reset(startIcons)
underTest.transitionTo(/* mode= */ MODE_TRANSPARENT, /* animate= */ false)
assertThat(startIcons.capturedAlpha()).isEqualTo(1)
}
@Test
fun transitionTo_opaqueMode_batteryIconUsesResourceAlpha() {
underTest.transitionTo(/* mode= */ MODE_OPAQUE, /* animate= */ false)
assertThat(batteryView.capturedAlpha()).isEqualTo(RESOURCE_ALPHA)
}
@Test
fun transitionTo_opaqueMode_statusIconsUseResourceAlpha() {
underTest.transitionTo(/* mode= */ MODE_OPAQUE, /* animate= */ false)
assertThat(statusIcons.capturedAlpha()).isEqualTo(RESOURCE_ALPHA)
}
@Test
fun transitionTo_opaqueMode_startIconsUseResourceAlpha() {
underTest.transitionTo(/* mode= */ MODE_OPAQUE, /* animate= */ false)
assertThat(startIcons.capturedAlpha()).isEqualTo(RESOURCE_ALPHA)
}
@Test
fun onHeadsUpStateChanged_true_semiTransparentMode_startIconsShown() {
underTest.transitionTo(/* mode= */ MODE_SEMI_TRANSPARENT, /* animate= */ false)
reset(startIcons)
underTest.onHeadsUpStateChanged(true)
assertThat(startIcons.capturedAlpha()).isEqualTo(1)
}
@Test
fun onHeadsUpStateChanged_true_opaqueMode_startIconsUseResourceAlpha() {
underTest.transitionTo(/* mode= */ MODE_OPAQUE, /* animate= */ false)
reset(startIcons)
underTest.onHeadsUpStateChanged(true)
assertThat(startIcons.capturedAlpha()).isEqualTo(RESOURCE_ALPHA)
}
/** Regression test for b/291173113. */
@Test
fun onHeadsUpStateChanged_true_lightsOutMode_startIconsUseResourceAlpha() {
underTest.transitionTo(/* mode= */ MODE_LIGHTS_OUT, /* animate= */ false)
reset(startIcons)
underTest.onHeadsUpStateChanged(true)
assertThat(startIcons.capturedAlpha()).isEqualTo(RESOURCE_ALPHA)
}
@Test
fun onHeadsUpStateChanged_false_semiTransparentMode_startIconsShown() {
underTest.transitionTo(/* mode= */ MODE_SEMI_TRANSPARENT, /* animate= */ false)
reset(startIcons)
underTest.onHeadsUpStateChanged(false)
assertThat(startIcons.capturedAlpha()).isEqualTo(1)
}
@Test
fun onHeadsUpStateChanged_false_opaqueMode_startIconsUseResourceAlpha() {
underTest.transitionTo(/* mode= */ MODE_OPAQUE, /* animate= */ false)
reset(startIcons)
underTest.onHeadsUpStateChanged(false)
assertThat(startIcons.capturedAlpha()).isEqualTo(RESOURCE_ALPHA)
}
@Test
fun onHeadsUpStateChanged_false_lightsOutMode_startIconsHidden() {
underTest.transitionTo(/* mode= */ MODE_LIGHTS_OUT, /* animate= */ false)
reset(startIcons)
underTest.onHeadsUpStateChanged(false)
assertThat(startIcons.capturedAlpha()).isEqualTo(0)
}
private fun View.capturedAlpha(): Float {
val captor = argumentCaptor<Float>()
verify(this).alpha = captor.capture()
return captor.value
}
private companion object {
const val RESOURCE_ALPHA = 0.34f
}
}