blob: 691d8716dee39d8cd6b42488a11c10979fd4c4b4 [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 org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.animation.Animator;
import android.graphics.Color;
import android.os.Handler;
import android.os.Looper;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.View;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.ScrimView;
import com.android.systemui.util.wakelock.WakeLock;
import com.android.systemui.utils.os.FakeHandler;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
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 View mHeadsUpScrim;
private Consumer<Boolean> mScrimVisibilityCallback;
private Boolean mScrimVisibile;
private LightBarController mLightBarController;
private DozeParameters mDozeParamenters;
private WakeLock mWakeLock;
private boolean mAlwaysOnEnabled;
@Before
public void setup() {
mLightBarController = mock(LightBarController.class);
mScrimBehind = new ScrimView(getContext());
mScrimInFront = new ScrimView(getContext());
mHeadsUpScrim = mock(View.class);
mWakeLock = mock(WakeLock.class);
mAlwaysOnEnabled = true;
mScrimVisibilityCallback = (Boolean visible) -> mScrimVisibile = visible;
mDozeParamenters = mock(DozeParameters.class);
when(mDozeParamenters.getAlwaysOn()).thenAnswer(invocation -> mAlwaysOnEnabled);
when(mDozeParamenters.getDisplayNeedsBlanking()).thenReturn(true);
mScrimController = new SynchronousScrimController(mLightBarController, mScrimBehind,
mScrimInFront, mHeadsUpScrim, mScrimVisibilityCallback, mDozeParamenters);
}
@Test
public void initialState() {
Assert.assertEquals("ScrimController should start initialized",
mScrimController.getState(), ScrimState.UNINITIALIZED);
}
@Test
public void transitionToKeyguard() {
mScrimController.transitionTo(ScrimState.KEYGUARD);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint
assertScrimVisibility(false /* front */, true /* behind */);
assertScrimTint(mScrimBehind, false /* tinted */);
}
@Test
public void transitionToAod_withRegularWallpaper() {
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible with tint
assertScrimVisibility(false /* front */, true /* behind */);
assertScrimTint(mScrimBehind, true /* tinted */);
assertScrimTint(mScrimInFront, true /* tinted */);
}
@Test
public void transitionToAod_withAodWallpaper() {
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be transparent
assertScrimVisibility(false /* front */, false /* behind */);
}
@Test
public void transitionToAod_withAodWallpaperAndLockScreenWallpaper() {
ScrimState.AOD.mKeyguardUpdateMonitor = new KeyguardUpdateMonitor(getContext()) {
@Override
public boolean hasLockscreenWallpaper() {
return true;
}
};
mScrimController.setWallpaperSupportsAmbientMode(true);
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible with tint
assertScrimVisibility(false /* front */, true /* behind */);
assertScrimTint(mScrimBehind, true /* tinted */);
assertScrimTint(mScrimInFront, true /* tinted */);
}
@Test
public void transitionToPulsing() {
mScrimController.transitionTo(ScrimState.PULSING);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible with tint
// Pulse callback should have been invoked
assertScrimVisibility(false /* front */, true /* behind */);
assertScrimTint(mScrimBehind, true /* tinted */);
}
@Test
public void transitionToBouncer() {
mScrimController.transitionTo(ScrimState.BOUNCER);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint
assertScrimVisibility(true /* front */, true /* behind */);
assertScrimTint(mScrimBehind, false /* tinted */);
}
@Test
public void transitionToUnlocked() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be transparent
assertScrimVisibility(false /* front */, false /* behind */);
assertScrimTint(mScrimBehind, false /* tinted */);
assertScrimTint(mScrimInFront, false /* tinted */);
// Back scrim should be visible after start dragging
mScrimController.setPanelExpansion(0.5f);
assertScrimVisibility(false /* front */, true /* behind */);
}
@Test
public void transitionToUnlockedFromAod() {
// Simulate unlock with fingerprint
mScrimController.transitionTo(ScrimState.AOD);
mScrimController.finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.UNLOCKED);
// Immediately tinted after the transition starts
assertScrimTint(mScrimInFront, true /* tinted */);
assertScrimTint(mScrimBehind, true /* tinted */);
mScrimController.finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be transparent
// Neither scrims should be tinted anymore after the animation.
assertScrimVisibility(false /* front */, false /* behind */);
assertScrimTint(mScrimInFront, false /* tinted */);
assertScrimTint(mScrimBehind, false /* tinted */);
}
@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(mScrimInFront, true /* tinted */);
Assert.assertTrue("Scrim should be visible during transition.",
mScrimVisibile);
}
});
mScrimController.finishAnimationsImmediately();
}
@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() {
mScrimController.transitionTo(ScrimState.AOD);
verify(mWakeLock, times(1)).acquire();
verify(mWakeLock, never()).release();
mScrimController.finishAnimationsImmediately();
verify(mWakeLock, times(1)).release();
}
@Test
public void testCallbackInvokedOnSameStateTransition() {
mScrimController.transitionTo(ScrimState.UNLOCKED);
mScrimController.finishAnimationsImmediately();
ScrimController.Callback callback = mock(ScrimController.Callback.class);
mScrimController.transitionTo(ScrimState.UNLOCKED, callback);
verify(callback, times(1)).onFinished();
}
private void assertScrimTint(ScrimView scrimView, boolean tinted) {
final boolean viewIsTinted = scrimView.getTint() != Color.TRANSPARENT;
final String name = scrimView == mScrimInFront ? "front" : "back";
Assert.assertEquals("Tint test failed at state " + mScrimController.getState()
+" with scrim: " + name + " and tint: " + Integer.toHexString(scrimView.getTint()),
tinted, viewIsTinted);
}
private void assertScrimVisibility(boolean inFront, boolean behind) {
Assert.assertEquals("Unexpected front scrim visibility. Alpha is "
+ mScrimInFront.getViewAlpha(), inFront, mScrimInFront.getViewAlpha() > 0);
Assert.assertEquals("Unexpected back scrim visibility. Alpha is "
+ mScrimBehind.getViewAlpha(), behind, mScrimBehind.getViewAlpha() > 0);
Assert.assertEquals("Invalid visibility.", inFront || behind, mScrimVisibile);
}
/**
* Special version of ScrimController where animations have 0 duration for test purposes.
*/
private class SynchronousScrimController extends ScrimController {
private FakeHandler mHandler;
public SynchronousScrimController(LightBarController lightBarController,
ScrimView scrimBehind, ScrimView scrimInFront, View headsUpScrim,
Consumer<Boolean> scrimVisibleListener, DozeParameters dozeParameters) {
super(lightBarController, scrimBehind, scrimInFront, headsUpScrim,
scrimVisibleListener, dozeParameters);
mHandler = new FakeHandler(Looper.myLooper());
}
public void finishAnimationsImmediately() {
boolean[] animationFinished = {false};
setOnAnimationFinished(()-> animationFinished[0] = true);
// Execute code that will trigger animations.
onPreDraw();
// Force finish screen blanking.
endAnimation(mScrimInFront, TAG_KEY_ANIM_BLANK);
mHandler.dispatchQueuedMessages();
// Force finish all animations.
endAnimation(mScrimBehind, TAG_KEY_ANIM);
endAnimation(mScrimInFront, TAG_KEY_ANIM);
if (!animationFinished[0]) {
throw new IllegalStateException("Animation never finished");
}
}
private void endAnimation(ScrimView scrimView, int tag) {
Animator animator = (Animator) scrimView.getTag(tag);
if (animator != null) {
animator.end();
}
}
@Override
protected Handler getHandler() {
return mHandler;
}
@Override
protected WakeLock createWakeLock() {
return mWakeLock;
}
}
}