blob: d4ad4b6d4f16639c5c278dcb7ebc4141d775269f [file] [log] [blame]
/*
* Copyright (C) 2017 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 static com.android.systemui.statusbar.phone.ScrimController.OPAQUE;
import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.app.AlarmManager;
import android.graphics.Color;
import android.os.Handler;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.internal.colorextraction.ColorExtractor.GradientColors;
import com.android.internal.util.function.TriConsumer;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.statusbar.policy.KeyguardMonitor;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.utils.os.FakeHandler;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.util.Collections;
import java.util.HashSet;
import java.util.function.Consumer;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class ScrimControllerTest extends SysuiTestCase {
private SynchronousScrimController mScrimController;
private ScrimView mScrimBehind;
private ScrimView mScrimInFront;
private ScrimView mScrimForBubble;
private ScrimState mScrimState;
private float mScrimBehindAlpha;
private GradientColors mScrimInFrontColor;
private int mScrimVisibility;
private DozeParameters mDozeParamenters;
private WakeLock mWakeLock;
private boolean mAlwaysOnEnabled;
private AlarmManager mAlarmManager;
private TestableLooper mLooper;
@Before
public void setup() {
mScrimBehind = spy(new ScrimView(getContext()));
mScrimInFront = new ScrimView(getContext());
mScrimForBubble = new ScrimView(getContext());
mWakeLock = mock(WakeLock.class);
mAlarmManager = mock(AlarmManager.class);
mAlwaysOnEnabled = true;
mDozeParamenters = mock(DozeParameters.class);
mLooper = TestableLooper.get(this);
when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
mScrimController = new SynchronousScrimController(mScrimBehind, mScrimInFront,
mScrimForBubble,
(scrimState, scrimBehindAlpha, scrimInFrontColor) -> {
mScrimState = scrimState;
mScrimBehindAlpha = scrimBehindAlpha;
mScrimInFrontColor = scrimInFrontColor;
},
visible -> mScrimVisibility = visible, mDozeParamenters, mAlarmManager,
mock(KeyguardMonitor.class));
mScrimController.setHasBackdrop(false);
mScrimController.setWallpaperSupportsAmbientMode(false);
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.finishAnimationsImmediately();
}
@After
public void tearDown() {
mScrimController.finishAnimationsImmediately();
}
@Test
public void transitionToKeyguard() {
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
assertScrimTint(true /* front */,
true /* behind */,
false /* bubble */);
}
@Test
public void transitionToAod_withRegularWallpaper() {
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
assertScrimTint(true /* front */,
true /* behind */,
false /* bubble */);
}
@Test
public void transitionToAod_withAodWallpaper() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
// Pulsing notification should conserve AOD wallpaper.
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
}
@Test
public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
mScrimController.setHasBackdrop(true);
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
assertScrimTint(true /* front */,
true /* behind */,
false /* bubble */);
}
@Test
public void setHasBackdrop_withAodWallpaperAndAlbumArt() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
mScrimController.setHasBackdrop(true);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
assertScrimTint(true /* front */,
true /* behind */,
false /* bubble */);
}
@Test
public void transitionToAod_withFrontAlphaUpdates() {
// Assert that setting the AOD front scrim alpha doesn't take effect in a non-AOD state.
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.setAodFrontScrimAlpha(0.5f);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
// ... but that it does take effect once we enter the AOD state.
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(SEMI_TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
// ... and that if we set it while we're in AOD, it does take immediate effect.
mScrimController.setAodFrontScrimAlpha(1f);
assertScrimAlpha(OPAQUE /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
// ... and make sure we recall the previous front scrim alpha even if we transition away
// for a bit.
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(OPAQUE /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
// ... and alpha updates should be completely ignored if always_on is off.
// Passing it forward would mess up the wake-up transition.
mAlwaysOnEnabled = false;
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
mScrimController.setAodFrontScrimAlpha(0.3f);
Assert.assertEquals(ScrimState.AOD.getFrontAlpha(), mScrimInFront.getViewAlpha(), 0.001f);
Assert.assertNotEquals(0.3f, mScrimInFront.getViewAlpha(), 0.001f);
// Reset value since enums are static.
mScrimController.setAodFrontScrimAlpha(0f);
}
@Test
public void transitionToPulsing_withFrontAlphaUpdates() {
// Pre-condition
// Need to go to AoD first because PULSING doesn't change
// the back scrim opacity - otherwise it would hide AoD wallpapers.
mScrimController.setWallpaperSupportsAmbientMode(false);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent, but tinted
// Back scrim should be semi-transparent so the user can see the wallpaper
// Pulse callback should have been invoked
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
assertScrimTint(true /* front */,
true /* behind */,
false /* bubble */);
// ... and when ambient goes dark, front scrim should be semi-transparent
mScrimController.setAodFrontScrimAlpha(0.5f);
mScrimController.finishAnimationsImmediately();
// Front scrim should be semi-transparent
assertScrimAlpha(SEMI_TRANSPARENT /* front */,
OPAQUE /* back */,
TRANSPARENT /* bubble */);
mScrimController.setWakeLockScreenSensorActive(true);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(SEMI_TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
// Reset value since enums are static.
mScrimController.setAodFrontScrimAlpha(0f);
}
@Test
public void transitionToKeyguardBouncer() {
mScrimController.transitionTo(ScrimState.BOUNCER);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint
assertScrimAlpha(TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
assertScrimTint(false /* front */,
false /* behind */,
false /* bubble */);
}
@Test
public void transitionToBouncer() {
mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint
assertScrimAlpha(SEMI_TRANSPARENT /* front */,
TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
assertScrimTint(false /* front */,
false /* behind */,
false /* bubble */);
}
@Test
public void transitionToUnlocked() {
mScrimController.setPanelExpansion(0f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
assertScrimTint(false /* front */,
false /* behind */,
false /* bubble */);
// Back scrim should be visible after start dragging
mScrimController.setPanelExpansion(0.5f);
assertScrimAlpha(TRANSPARENT /* front */,
SEMI_TRANSPARENT /* back */,
TRANSPARENT /* bubble */);
}
@Test
public void transitionToBubbleExpanded() {
mScrimController.transitionTo(ScrimState.BUBBLE_EXPANDED);
mScrimController.finishAnimationsImmediately();
assertScrimTint(false /* front */,
false /* behind */,
false /* bubble */);
// Front scrim should be transparent
Assert.assertEquals(ScrimController.TRANSPARENT,
mScrimInFront.getViewAlpha(), 0.0f);
// Back scrim should be visible
Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
mScrimBehind.getViewAlpha(), 0.0f);
// Bubble scrim should be visible
Assert.assertEquals(ScrimController.GRADIENT_SCRIM_ALPHA_BUSY,
mScrimBehind.getViewAlpha(), 0.0f);
}
@Test
public void scrimStateCallback() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
Assert.assertEquals(mScrimState, ScrimState.UNLOCKED);
mScrimController.transitionTo(ScrimState.BOUNCER);
mScrimController.finishAnimationsImmediately();
Assert.assertEquals(mScrimState, ScrimState.BOUNCER);
mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
mScrimController.finishAnimationsImmediately();
Assert.assertEquals(mScrimState, ScrimState.BOUNCER_SCRIMMED);
}
@Test
public void panelExpansion() {
mScrimController.setPanelExpansion(0f);
mScrimController.setPanelExpansion(0.5f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
reset(mScrimBehind);
mScrimController.setPanelExpansion(0f);
mScrimController.setPanelExpansion(1.0f);
mScrimController.finishAnimationsImmediately();
Assert.assertEquals("Scrim alpha should change after setPanelExpansion",
mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
mScrimController.setPanelExpansion(0f);
mScrimController.finishAnimationsImmediately();
Assert.assertEquals("Scrim alpha should change after setPanelExpansion",
mScrimBehindAlpha, mScrimBehind.getViewAlpha(), 0.01f);
}
@Test
public void panelExpansionAffectsAlpha() {
mScrimController.setPanelExpansion(0f);
mScrimController.setPanelExpansion(0.5f);
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
final float scrimAlpha = mScrimBehind.getViewAlpha();
reset(mScrimBehind);
mScrimController.setExpansionAffectsAlpha(false);
mScrimController.setPanelExpansion(0.8f);
verifyZeroInteractions(mScrimBehind);
Assert.assertEquals("Scrim opacity shouldn't change when setExpansionAffectsAlpha "
+ "is false", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
mScrimController.setExpansionAffectsAlpha(true);
mScrimController.setPanelExpansion(0.1f);
mScrimController.finishAnimationsImmediately();
Assert.assertNotEquals("Scrim opacity should change when setExpansionAffectsAlpha "
+ "is true", scrimAlpha, mScrimBehind.getViewAlpha(), 0.01f);
}
@Test
public void transitionToUnlockedFromAod() {
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.setPanelExpansion(0f);
mScrimController.finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED);
// Immediately tinted black after the transition starts
assertScrimTint(true /* front */,
true /* behind */,
true /* bubble */);
mScrimController.finishAnimationsImmediately();
// All scrims should be transparent at the end of fade transition.
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* behind */,
TRANSPARENT /* bubble */);
// Make sure at the very end of the animation, we're reset to transparent
assertScrimTint(false /* front */,
false /* behind */,
false /* bubble */);
}
@Test
public void scrimBlanksBeforeLeavingAod() {
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED,
new ScrimController.Callback() {
@Override
public void onDisplayBlanked() {
// Front scrim should be black in the middle of the transition
Assert.assertTrue("Scrim should be visible during transition. Alpha: "
+ mScrimInFront.getViewAlpha(), mScrimInFront.getViewAlpha() > 0);
assertScrimTint(true /* front */,
true /* behind */,
true /* bubble */);
Assert.assertSame("Scrim should be visible during transition.",
mScrimVisibility, OPAQUE);
}
});
mScrimController.finishAnimationsImmediately();
}
@Test
public void scrimBlanksWhenUnlockingFromPulse() {
boolean[] blanked = {false};
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED,
new ScrimController.Callback() {
@Override
public void onDisplayBlanked() {
blanked[0] = true;
}
});
mScrimController.finishAnimationsImmediately();
Assert.assertTrue("Scrim should blank when unlocking from pulse.", blanked[0]);
}
@Test
public void testScrimCallback() {
int[] callOrder = {0, 0, 0};
int[] currentCall = {0};
mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
@Override
public void onStart() {
callOrder[0] = ++currentCall[0];
}
@Override
public void onDisplayBlanked() {
callOrder[1] = ++currentCall[0];
}
@Override
public void onFinished() {
callOrder[2] = ++currentCall[0];
}
});
mScrimController.finishAnimationsImmediately();
Assert.assertEquals("onStart called in wrong order", 1, callOrder[0]);
Assert.assertEquals("onDisplayBlanked called in wrong order", 2, callOrder[1]);
Assert.assertEquals("onFinished called in wrong order", 3, callOrder[2]);
}
@Test
public void testScrimCallbacksWithoutAmbientDisplay() {
mAlwaysOnEnabled = false;
testScrimCallback();
}
@Test
public void testScrimCallbackCancelled() {
boolean[] cancelledCalled = {false};
mScrimController.transitionTo(ScrimState.AOD, new ScrimController.Callback() {
@Override
public void onCancelled() {
cancelledCalled[0] = true;
}
});
mScrimController.transitionTo(ScrimState.PULSING);
Assert.assertTrue("onCancelled should have been called", cancelledCalled[0]);
}
@Test
public void testHoldsWakeLock_whenAOD() {
mScrimController.transitionTo(ScrimState.AOD);
verify(mWakeLock).acquire(anyString());
verify(mWakeLock, never()).release(anyString());
mScrimController.finishAnimationsImmediately();
verify(mWakeLock).release(anyString());
}
@Test
public void testDoesNotHoldWakeLock_whenUnlocking() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
verifyZeroInteractions(mWakeLock);
}
@Test
public void testCallbackInvokedOnSameStateTransition() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
ScrimController.Callback callback = mock(ScrimController.Callback.class);
mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
verify(callback).onFinished();
}
@Test
public void testHoldsAodWallpaperAnimationLock() {
// Pre-conditions
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
reset(mWakeLock);
mScrimController.onHideWallpaperTimeout();
verify(mWakeLock).acquire(anyString());
verify(mWakeLock, never()).release(anyString());
mScrimController.finishAnimationsImmediately();
verify(mWakeLock).release(anyString());
}
@Test
public void testHoldsPulsingWallpaperAnimationLock() {
// Pre-conditions
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
reset(mWakeLock);
mScrimController.onHideWallpaperTimeout();
verify(mWakeLock).acquire(anyString());
verify(mWakeLock, never()).release(anyString());
mScrimController.finishAnimationsImmediately();
verify(mWakeLock).release(anyString());
}
@Test
public void testWillHideAodWallpaper() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
mScrimController.transitionTo(ScrimState.KEYGUARD);
verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class));
}
@Test
public void transitionToPulsing_withTimeoutWallpaperCallback_willHideWallpaper() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() {
@Override
public boolean shouldTimeoutWallpaper() {
return true;
}
});
verify(mAlarmManager).setExact(anyInt(), anyLong(), any(), any(), any());
}
@Test
public void transitionToPulsing_withDefaultCallback_wontHideWallpaper() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.PULSING, new ScrimController.Callback() {});
verify(mAlarmManager, never()).setExact(anyInt(), anyLong(), any(), any(), any());
}
@Test
public void transitionToPulsing_withoutCallback_wontHideWallpaper() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.PULSING);
verify(mAlarmManager, never()).setExact(anyInt(), anyLong(), any(), any(), any());
}
@Test
public void testConservesExpansionOpacityAfterTransition() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.setPanelExpansion(0.5f);
mScrimController.finishAnimationsImmediately();
final float expandedAlpha = mScrimBehind.getViewAlpha();
mScrimController.transitionTo(ScrimState.BRIGHTNESS_MIRROR);
mScrimController.finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
Assert.assertEquals("Scrim expansion opacity wasn't conserved when transitioning back",
expandedAlpha, mScrimBehind.getViewAlpha(), 0.01f);
}
@Test
public void cancelsOldAnimationBeforeBlanking() {
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
// Consume whatever value we had before
mScrimController.wasAnimationJustCancelled();
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.finishAnimationsImmediately();
Assert.assertTrue(mScrimController.wasAnimationJustCancelled());
}
@Test
public void testScrimFocus() {
mScrimController.transitionTo(ScrimState.AOD);
Assert.assertFalse("Should not be focusable on AOD", mScrimBehind.isFocusable());
Assert.assertFalse("Should not be focusable on AOD", mScrimInFront.isFocusable());
mScrimController.transitionTo(ScrimState.KEYGUARD);
Assert.assertTrue("Should be focusable on keyguard", mScrimBehind.isFocusable());
Assert.assertTrue("Should be focusable on keyguard", mScrimInFront.isFocusable());
}
@Test
public void testHidesShowWhenLockedActivity() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.setKeyguardOccluded(true);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* behind */,
TRANSPARENT /* bubble */);
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* behind */,
TRANSPARENT /* bubble */);
}
@Test
public void testHidesShowWhenLockedActivity_whenAlreadyInAod() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
TRANSPARENT /* behind */,
TRANSPARENT /* bubble */);
mScrimController.setKeyguardOccluded(true);
mScrimController.finishAnimationsImmediately();
assertScrimAlpha(TRANSPARENT /* front */,
OPAQUE /* behind */,
TRANSPARENT /* bubble */);
}
@Test
public void testEatsTouchEvent() {
HashSet<ScrimState> eatsTouches =
new HashSet<>(Collections.singletonList(ScrimState.AOD));
for (ScrimState state : ScrimState.values()) {
if (state == ScrimState.UNINITIALIZED) {
continue;
}
mScrimController.transitionTo(state);
mScrimController.finishAnimationsImmediately();
Assert.assertEquals("Should be clickable unless AOD or PULSING, was: " + state,
mScrimBehind.getViewAlpha() != 0 && !eatsTouches.contains(state),
mScrimBehind.isClickable());
}
}
@Test
public void testAnimatesTransitionToAod() {
when(mDozeParamenters.shouldControlScreenOff()).thenReturn(false);
ScrimState.AOD.prepare(ScrimState.KEYGUARD);
Assert.assertFalse("No animation when ColorFade kicks in",
ScrimState.AOD.getAnimateChange());
reset(mDozeParamenters);
when(mDozeParamenters.shouldControlScreenOff()).thenReturn(true);
ScrimState.AOD.prepare(ScrimState.KEYGUARD);
Assert.assertTrue("Animate scrims when ColorFade won't be triggered",
ScrimState.AOD.getAnimateChange());
}
@Test
public void testViewsDontHaveFocusHighlight() {
Assert.assertFalse("Scrim shouldn't have focus highlight",
mScrimInFront.getDefaultFocusHighlightEnabled());
Assert.assertFalse("Scrim shouldn't have focus highlight",
mScrimBehind.getDefaultFocusHighlightEnabled());
Assert.assertFalse("Scrim shouldn't have focus highlight",
mScrimForBubble.getDefaultFocusHighlightEnabled());
}
private void assertScrimTint(boolean front, boolean behind, boolean bubble) {
Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
+ " with scrim: " + getScrimName(mScrimInFront) + " and tint: "
+ Integer.toHexString(mScrimInFront.getTint()),
front, mScrimInFront.getTint() != Color.TRANSPARENT);
Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
+ " with scrim: " + getScrimName(mScrimBehind) + " and tint: "
+ Integer.toHexString(mScrimBehind.getTint()),
behind, mScrimBehind.getTint() != Color.TRANSPARENT);
Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
+ " with scrim: " + getScrimName(mScrimForBubble) + " and tint: "
+ Integer.toHexString(mScrimForBubble.getTint()),
bubble, mScrimForBubble.getTint() != Color.TRANSPARENT);
}
private String getScrimName(ScrimView scrim) {
if (scrim == mScrimInFront) {
return "front";
} else if (scrim == mScrimBehind) {
return "back";
} else if (scrim == mScrimForBubble) {
return "bubble";
}
return "unknown_scrim";
}
private void assertScrimAlpha(int front, int behind, int bubble) {
// Check single scrim visibility.
Assert.assertEquals("Unexpected front scrim alpha: "
+ mScrimInFront.getViewAlpha(),
front != TRANSPARENT /* expected */,
mScrimInFront.getViewAlpha() > TRANSPARENT /* actual */);
Assert.assertEquals("Unexpected back scrim alpha: "
+ mScrimBehind.getViewAlpha(),
behind != TRANSPARENT /* expected */,
mScrimBehind.getViewAlpha() > TRANSPARENT /* actual */);
Assert.assertEquals(
"Unexpected bubble scrim alpha: "
+ mScrimForBubble.getViewAlpha(), /* message */
bubble != TRANSPARENT /* expected */,
mScrimForBubble.getViewAlpha() > TRANSPARENT /* actual */);
// Check combined scrim visibility.
final int visibility;
if (front == OPAQUE || behind == OPAQUE || bubble == OPAQUE) {
visibility = OPAQUE;
} else if (front > TRANSPARENT || behind > TRANSPARENT || bubble > TRANSPARENT) {
visibility = SEMI_TRANSPARENT;
} else {
visibility = TRANSPARENT;
}
Assert.assertEquals("Invalid visibility.",
visibility /* expected */,
mScrimVisibility);
}
/**
* Special version of ScrimController where animations have 0 duration for test purposes.
*/
private class SynchronousScrimController extends ScrimController {
private boolean mAnimationCancelled;
boolean mOnPreDrawCalled;
SynchronousScrimController(ScrimView scrimBehind, ScrimView scrimInFront,
ScrimView scrimForBubble,
TriConsumer<ScrimState, Float, GradientColors> scrimStateListener,
Consumer<Integer> scrimVisibleListener, DozeParameters dozeParameters,
AlarmManager alarmManager, KeyguardMonitor keyguardMonitor) {
super(scrimBehind, scrimInFront, scrimForBubble, scrimStateListener,
scrimVisibleListener, dozeParameters, alarmManager, keyguardMonitor);
}
@Override
public boolean onPreDraw() {
mOnPreDrawCalled = true;
return super.onPreDraw();
}
void finishAnimationsImmediately() {
boolean[] animationFinished = {false};
setOnAnimationFinished(() -> animationFinished[0] = true);
// Execute code that will trigger animations.
onPreDraw();
// Force finish all animations.
mLooper.processAllMessages();
endAnimation(mScrimBehind, TAG_KEY_ANIM);
endAnimation(mScrimInFront, TAG_KEY_ANIM);
endAnimation(mScrimForBubble, TAG_KEY_ANIM);
if (!animationFinished[0]) {
throw new IllegalStateException("Animation never finished");
}
}
boolean wasAnimationJustCancelled() {
final boolean wasCancelled = mAnimationCancelled;
mAnimationCancelled = false;
return wasCancelled;
}
private void endAnimation(View scrimView, int tag) {
Animator animator = (Animator) scrimView.getTag(tag);
if (animator != null) {
animator.end();
}
}
@Override
protected void cancelAnimator(ValueAnimator previousAnimator) {
super.cancelAnimator(previousAnimator);
mAnimationCancelled = true;
}
@Override
protected Handler getHandler() {
return new FakeHandler(mLooper.getLooper());
}
@Override
protected WakeLock createWakeLock() {
return mWakeLock;
}
/**
* Do not wait for a frame since we're in a test environment.
*
* @param callback What to execute.
*/
@Override
protected void doOnTheNextFrame(Runnable callback) {
callback.run();
}
}
}