Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b85665b3 authored by William Xiao's avatar William Xiao Committed by Android (Google) Code Review
Browse files

Merge changes from topic "low_light_animations" into tm-qpr-dev

* changes:
  Implement updated low light animations
  Reverse dream overlay animations on exiting low light
parents def6a36f eba59517
Loading
Loading
Loading
Loading
+7 −2
Original line number Diff line number Diff line
@@ -72,6 +72,7 @@ public final class LowLightDreamManager {
    public static final int AMBIENT_LIGHT_MODE_LOW_LIGHT = 2;

    private final DreamManager mDreamManager;
    private final LowLightTransitionCoordinator mLowLightTransitionCoordinator;

    @Nullable
    private final ComponentName mLowLightDreamComponent;
@@ -81,8 +82,10 @@ public final class LowLightDreamManager {
    @Inject
    public LowLightDreamManager(
            DreamManager dreamManager,
            LowLightTransitionCoordinator lowLightTransitionCoordinator,
            @Named(LOW_LIGHT_DREAM_COMPONENT) @Nullable ComponentName lowLightDreamComponent) {
        mDreamManager = dreamManager;
        mLowLightTransitionCoordinator = lowLightTransitionCoordinator;
        mLowLightDreamComponent = lowLightDreamComponent;
    }

@@ -111,7 +114,9 @@ public final class LowLightDreamManager {

        mAmbientLightMode = ambientLightMode;

        mDreamManager.setSystemDreamComponent(mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT
                ? mLowLightDreamComponent : null);
        boolean shouldEnterLowLight = mAmbientLightMode == AMBIENT_LIGHT_MODE_LOW_LIGHT;
        mLowLightTransitionCoordinator.notifyBeforeLowLightTransition(shouldEnterLowLight,
                () -> mDreamManager.setSystemDreamComponent(
                        shouldEnterLowLight ? mLowLightDreamComponent : null));
    }
}
+111 −0
Original line number Diff line number Diff line
/*
 * 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.dream.lowlight;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.annotation.Nullable;

import javax.inject.Inject;
import javax.inject.Singleton;

/**
 * Helper class that allows listening and running animations before entering or exiting low light.
 */
@Singleton
public class LowLightTransitionCoordinator {
    /**
     * Listener that is notified before low light entry.
     */
    public interface LowLightEnterListener {
        /**
         * Callback that is notified before the device enters low light.
         *
         * @return an optional animator that will be waited upon before entering low light.
         */
        Animator onBeforeEnterLowLight();
    }

    /**
     * Listener that is notified before low light exit.
     */
    public interface LowLightExitListener {
        /**
         * Callback that is notified before the device exits low light.
         *
         * @return an optional animator that will be waited upon before exiting low light.
         */
        Animator onBeforeExitLowLight();
    }

    private LowLightEnterListener mLowLightEnterListener;
    private LowLightExitListener mLowLightExitListener;

    @Inject
    public LowLightTransitionCoordinator() {
    }

    /**
     * Sets the listener for the low light enter event.
     *
     * Only one listener can be set at a time. This method will overwrite any previously set
     * listener. Null can be used to unset the listener.
     */
    public void setLowLightEnterListener(@Nullable LowLightEnterListener lowLightEnterListener) {
        mLowLightEnterListener = lowLightEnterListener;
    }

    /**
     * Sets the listener for the low light exit event.
     *
     * Only one listener can be set at a time. This method will overwrite any previously set
     * listener. Null can be used to unset the listener.
     */
    public void setLowLightExitListener(@Nullable LowLightExitListener lowLightExitListener) {
        mLowLightExitListener = lowLightExitListener;
    }

    /**
     * Notifies listeners that the device is about to enter or exit low light.
     *
     * @param entering true if listeners should be notified before entering low light, false if this
     *                 is notifying before exiting.
     * @param callback callback that will be run after listeners complete.
     */
    void notifyBeforeLowLightTransition(boolean entering, Runnable callback) {
        Animator animator = null;

        if (entering && mLowLightEnterListener != null) {
            animator = mLowLightEnterListener.onBeforeEnterLowLight();
        } else if (!entering && mLowLightExitListener != null) {
            animator = mLowLightExitListener.onBeforeExitLowLight();
        }

        // If the listener returned an animator to indicate it was running an animation, run the
        // callback after the animation completes, otherwise call the callback directly.
        if (animator != null) {
            animator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animator) {
                    callback.run();
                }
            });
        } else {
            callback.run();
        }
    }
}
+25 −14
Original line number Diff line number Diff line
@@ -21,7 +21,10 @@ import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE
import static com.android.dream.lowlight.LowLightDreamManager.AMBIENT_LIGHT_MODE_UNKNOWN;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;

@@ -43,45 +46,53 @@ public class LowLightDreamManagerTest {
    @Mock
    private DreamManager mDreamManager;

    @Mock
    private LowLightTransitionCoordinator mTransitionCoordinator;

    @Mock
    private ComponentName mDreamComponent;

    LowLightDreamManager mLowLightDreamManager;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        // Automatically run any provided Runnable to mTransitionCoordinator to simplify testing.
        doAnswer(invocation -> {
            ((Runnable) invocation.getArgument(1)).run();
            return null;
        }).when(mTransitionCoordinator).notifyBeforeLowLightTransition(anyBoolean(),
                any(Runnable.class));

        mLowLightDreamManager = new LowLightDreamManager(mDreamManager, mTransitionCoordinator,
                mDreamComponent);
    }

    @Test
    public void setAmbientLightMode_lowLight_setSystemDream() {
        final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
                mDreamComponent);

        lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
        mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);

        verify(mTransitionCoordinator).notifyBeforeLowLightTransition(eq(true), any());
        verify(mDreamManager).setSystemDreamComponent(mDreamComponent);
    }

    @Test
    public void setAmbientLightMode_regularLight_clearSystemDream() {
        final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
                mDreamComponent);

        lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);
        mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_REGULAR);

        verify(mTransitionCoordinator).notifyBeforeLowLightTransition(eq(false), any());
        verify(mDreamManager).setSystemDreamComponent(null);
    }

    @Test
    public void setAmbientLightMode_defaultUnknownMode_clearSystemDream() {
        final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
                mDreamComponent);

        // Set to low light first.
        lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
        mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);
        clearInvocations(mDreamManager);

        // Return to default unknown mode.
        lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_UNKNOWN);
        mLowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_UNKNOWN);

        verify(mDreamManager).setSystemDreamComponent(null);
    }
@@ -89,7 +100,7 @@ public class LowLightDreamManagerTest {
    @Test
    public void setAmbientLightMode_dreamComponentNotSet_doNothing() {
        final LowLightDreamManager lowLightDreamManager = new LowLightDreamManager(mDreamManager,
                null /*dream component*/);
                mTransitionCoordinator, null /*dream component*/);

        lowLightDreamManager.setAmbientLightMode(AMBIENT_LIGHT_MODE_LOW_LIGHT);

+113 −0
Original line number Diff line number Diff line
/*
 * 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.dream.lowlight;

import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;

import android.animation.Animator;
import android.testing.AndroidTestingRunner;

import androidx.test.filters.SmallTest;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

@SmallTest
@RunWith(AndroidTestingRunner.class)
public class LowLightTransitionCoordinatorTest {
    @Mock
    private LowLightTransitionCoordinator.LowLightEnterListener mEnterListener;

    @Mock
    private LowLightTransitionCoordinator.LowLightExitListener mExitListener;

    @Mock
    private Animator mAnimator;

    @Captor
    private ArgumentCaptor<Animator.AnimatorListener> mAnimatorListenerCaptor;

    @Mock
    private Runnable mRunnable;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void onEnterCalledOnListeners() {
        LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();

        coordinator.setLowLightEnterListener(mEnterListener);

        coordinator.notifyBeforeLowLightTransition(true, mRunnable);

        verify(mEnterListener).onBeforeEnterLowLight();
        verify(mRunnable).run();
    }

    @Test
    public void onExitCalledOnListeners() {
        LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();

        coordinator.setLowLightExitListener(mExitListener);

        coordinator.notifyBeforeLowLightTransition(false, mRunnable);

        verify(mExitListener).onBeforeExitLowLight();
        verify(mRunnable).run();
    }

    @Test
    public void listenerNotCalledAfterRemoval() {
        LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();

        coordinator.setLowLightEnterListener(mEnterListener);
        coordinator.setLowLightEnterListener(null);

        coordinator.notifyBeforeLowLightTransition(true, mRunnable);

        verifyZeroInteractions(mEnterListener);
        verify(mRunnable).run();
    }

    @Test
    public void runnableCalledAfterAnimationEnds() {
        when(mEnterListener.onBeforeEnterLowLight()).thenReturn(mAnimator);

        LowLightTransitionCoordinator coordinator = new LowLightTransitionCoordinator();
        coordinator.setLowLightEnterListener(mEnterListener);

        coordinator.notifyBeforeLowLightTransition(true, mRunnable);

        // Animator listener is added and the runnable is not run yet.
        verify(mAnimator).addListener(mAnimatorListenerCaptor.capture());
        verifyZeroInteractions(mRunnable);

        // Runnable is run once the animation ends.
        mAnimatorListenerCaptor.getValue().onAnimationEnd(null);
        verify(mRunnable).run();
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -774,7 +774,7 @@
    <!-- Duration in milliseconds of the dream in complications fade-in animation. -->
    <integer name="config_dreamOverlayInComplicationsDurationMs">250</integer>
    <!-- Duration in milliseconds of the y-translation animation when entering a dream -->
    <integer name="config_dreamOverlayInTranslationYDurationMs">917</integer>
    <integer name="config_dreamOverlayInTranslationYDurationMs">1167</integer>

    <!-- Delay in milliseconds before switching to the dock user and dreaming if a secondary user is
    active when the device is locked and docked. 0 indicates disabled. Default is 1 minute. -->
Loading