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

Commit 7dc31e36 authored by András Kurucz's avatar András Kurucz
Browse files

Remove LegacyLightsOutNotifController

This class has been replaced by LightsOutInteractor after the
rollout of the NotificationsLiveDataStoreRefactor flag.

Bug: 424001722
Test: check lights out mode
Flag: EXEMPT mechanical refactor
Change-Id: Iaaf3cfd802b112da5f0860fb4cb3bc603f433fb9
parent 69716fca
Loading
Loading
Loading
Loading
+0 −257
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;

import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

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

import android.testing.TestableLooper.RunWithLooper;
import android.view.Display;
import android.view.View;
import android.view.ViewPropertyAnimator;
import android.view.WindowInsets;
import android.view.WindowManager;

import androidx.lifecycle.Observer;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;

import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.collection.NotifLiveData;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;

import org.junit.Before;
import org.junit.Ignore;
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;

import java.util.Objects;

@SmallTest
@RunWith(AndroidJUnit4.class)
@RunWithLooper
@Ignore("b/424001722") // remove this test when LegacyLightsOutNotifController is gone
public class LegacyLightsOutNotifControllerTest extends SysuiTestCase {
    private static final int LIGHTS_ON = 0;
    private static final int LIGHTS_OUT = APPEARANCE_LOW_PROFILE_BARS;

    @Mock private NotifLiveData<Boolean> mHasActiveNotifs;
    @Mock private NotifLiveDataStore mNotifLiveDataStore;
    @Mock private CommandQueue mCommandQueue;
    @Mock private WindowManager mWindowManager;
    @Mock private Display mDisplay;

    @Captor private ArgumentCaptor<Observer<Boolean>> mObserverCaptor;
    @Captor private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksCaptor;

    private View mLightsOutView;
    private LegacyLightsOutNotifController mLightsOutNotifController;
    private int mDisplayId;
    private Observer<Boolean> mHaActiveNotifsObserver;
    private CommandQueue.Callbacks mCallbacks;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        mDisplayId = mContext.getDisplayId();
        mLightsOutView = new View(mContext);
        when(mWindowManager.getDefaultDisplay()).thenReturn(mDisplay);
        when(mDisplay.getDisplayId()).thenReturn(mDisplayId);
        when(mNotifLiveDataStore.getHasActiveNotifs()).thenReturn(mHasActiveNotifs);
        when(mHasActiveNotifs.getValue()).thenReturn(false);

        mLightsOutNotifController = new LegacyLightsOutNotifController(
                mLightsOutView,
                mWindowManager,
                mNotifLiveDataStore,
                mCommandQueue);
        mLightsOutNotifController.init();
        mLightsOutNotifController.onViewAttached();

        // Capture the entry listener object so we can simulate events in tests below
        verify(mHasActiveNotifs).addSyncObserver(mObserverCaptor.capture());
        mHaActiveNotifsObserver = Objects.requireNonNull(mObserverCaptor.getValue());

        // Capture the callback object so we can simulate callback events in tests below
        verify(mCommandQueue).addCallback(mCallbacksCaptor.capture());
        mCallbacks = Objects.requireNonNull(mCallbacksCaptor.getValue());
    }

    @Test
    public void testAreLightsOut_lightsOut() {
        mCallbacks.onSystemBarAttributesChanged(
                mDisplayId /* display id */,
                LIGHTS_OUT /* appearance */,
                null /* appearanceRegions */,
                false /* navbarColorManagedByIme */,
                BEHAVIOR_DEFAULT,
                WindowInsets.Type.defaultVisible(),
                null /* packageName */,
                null /* letterboxDetails */);
        assertTrue(mLightsOutNotifController.areLightsOut());
    }

    @Test
    public void testAreLightsOut_lightsOn() {
        mCallbacks.onSystemBarAttributesChanged(
                mDisplayId /* display id */,
                LIGHTS_ON /* appearance */,
                null /* appearanceRegions */,
                false /* navbarColorManagedByIme */,
                BEHAVIOR_DEFAULT,
                WindowInsets.Type.defaultVisible(),
                null /* packageName */,
                null /* letterboxDetails */);
        assertFalse(mLightsOutNotifController.areLightsOut());
    }

    @Test
    public void testIsShowingDot_visible() {
        mLightsOutView.setVisibility(View.VISIBLE);
        mLightsOutView.setAlpha(1.0f);
        assertTrue(mLightsOutNotifController.isShowingDot());
    }

    @Test
    public void testIsShowingDot_gone() {
        mLightsOutView.setVisibility(View.GONE);
        mLightsOutView.setAlpha(0f);
        assertFalse(mLightsOutNotifController.isShowingDot());
    }

    @Test
    public void testLightsOut_withNotifs_onSystemBarAttributesChanged() {
        // GIVEN active visible notifications
        when(mHasActiveNotifs.getValue()).thenReturn(true);

        // WHEN lights out
        mCallbacks.onSystemBarAttributesChanged(
                mDisplayId /* display id */,
                LIGHTS_OUT /* appearance */,
                null /* appearanceRegions */,
                false /* navbarColorManagedByIme */,
                BEHAVIOR_DEFAULT,
                WindowInsets.Type.defaultVisible(),
                null /* packageName */,
                null /* letterboxDetails */);

        // THEN we should show dot
        assertTrue(mLightsOutNotifController.shouldShowDot());
        assertIsShowingDot(true);
    }

    @Test
    public void testLightsOut_withoutNotifs_onSystemBarAttributesChanged() {
        // GIVEN no active visible notifications
        when(mHasActiveNotifs.getValue()).thenReturn(false);

        // WHEN lights out
        mCallbacks.onSystemBarAttributesChanged(
                mDisplayId /* display id */,
                LIGHTS_OUT /* appearance */,
                null /* appearanceRegions */,
                false /* navbarColorManagedByIme */,
                BEHAVIOR_DEFAULT,
                WindowInsets.Type.defaultVisible(),
                null /* packageName */,
                null /* letterboxDetails */);

        // THEN we shouldn't show the dot
        assertFalse(mLightsOutNotifController.shouldShowDot());
        assertIsShowingDot(false);
    }

    @Test
    public void testLightsOn_afterLightsOut_onSystemBarAttributesChanged() {
        // GIVEN active visible notifications
        when(mHasActiveNotifs.getValue()).thenReturn(true);

        // WHEN lights on
        mCallbacks.onSystemBarAttributesChanged(
                mDisplayId /* display id */,
                LIGHTS_ON /* appearance */,
                null /* appearanceRegions */,
                false /* navbarColorManagedByIme */,
                BEHAVIOR_DEFAULT,
                WindowInsets.Type.defaultVisible(),
                null /* packageName */,
                null /* letterboxDetails */);

        // THEN we shouldn't show the dot
        assertFalse(mLightsOutNotifController.shouldShowDot());
        assertIsShowingDot(false);
    }

    @Test
    public void testEntryAdded() {
        // GIVEN no visible notifications and lights out
        when(mHasActiveNotifs.getValue()).thenReturn(false);
        mLightsOutNotifController.mAppearance = LIGHTS_OUT;
        mLightsOutNotifController.updateLightsOutView();
        assertIsShowingDot(false);

        // WHEN an active notification is added
        when(mHasActiveNotifs.getValue()).thenReturn(true);
        assertTrue(mLightsOutNotifController.shouldShowDot());
        mHaActiveNotifsObserver.onChanged(true);

        // THEN we should see the dot view
        assertIsShowingDot(true);
    }

    @Test
    public void testEntryRemoved() {
        // GIVEN a visible notification and lights out
        when(mHasActiveNotifs.getValue()).thenReturn(true);
        mLightsOutNotifController.mAppearance = LIGHTS_OUT;
        mLightsOutNotifController.updateLightsOutView();
        assertIsShowingDot(true);

        // WHEN all active notifications are removed
        when(mHasActiveNotifs.getValue()).thenReturn(false);
        assertFalse(mLightsOutNotifController.shouldShowDot());
        mHaActiveNotifsObserver.onChanged(false);

        // THEN we shouldn't see the dot view
        assertIsShowingDot(false);
    }

    private void assertIsShowingDot(boolean isShowing) {
        // cancel the animation so we can check the end state
        final ViewPropertyAnimator animation = mLightsOutView.animate();
        if (animation != null) {
            animation.cancel();
        }

        if (isShowing) {
            assertTrue(mLightsOutNotifController.isShowingDot());
        } else {
            assertFalse(mLightsOutNotifController.isShowingDot());
        }
    }
}
+2 −3
Original line number Diff line number Diff line
@@ -35,9 +35,8 @@ enum class StatusBarMode {
    SEMI_TRANSPARENT,
    /**
     * A mode where notification icons in the status bar are hidden and replaced by a dot (this mode
     * can be requested by apps). See
     * [com.android.systemui.statusbar.phone.LegacyLightsOutNotifController] and
     * [com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor].
     * can be requested by apps).
     * @see [com.android.systemui.statusbar.phone.domain.interactor.LightsOutInteractor].
     */
    LIGHTS_OUT,
    /** Similar to [LIGHTS_OUT], but also with a transparent background for the status bar. */
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import androidx.lifecycle.Observer
 *
 * This interface is read-only.
 */
// TODO(b/34027044) remove NotifLiveDataStore
interface NotifLiveDataStore {
    val hasActiveNotifs: NotifLiveData<Boolean>
    val activeNotifCount: NotifLiveData<Int>
+0 −162
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.view.WindowInsetsController.APPEARANCE_LOW_PROFILE_BARS;

import static com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarModule.LIGHTS_OUT_NOTIF_VIEW;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.view.View;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
import android.view.WindowManager;
import android.view.animation.AccelerateInterpolator;

import androidx.lifecycle.Observer;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.statusbar.LetterboxDetails;
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor;
import com.android.systemui.statusbar.phone.fragment.dagger.HomeStatusBarScope;
import com.android.systemui.util.ViewController;

import javax.inject.Inject;
import javax.inject.Named;

/**
 * Apps can request a low profile mode {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}
 * where status bar and navigation icons dim. In this mode, a notification dot appears
 * where the notification icons would appear if they would be shown outside of this mode.
 *
 * This controller shows and hides the notification dot in the status bar to indicate
 * whether there are notifications when the device is in {@link View#SYSTEM_UI_FLAG_LOW_PROFILE}.
 */
@HomeStatusBarScope
public class LegacyLightsOutNotifController extends ViewController<View> {
    private final CommandQueue mCommandQueue;
    private final NotifLiveDataStore mNotifDataStore;
    private final WindowManager mWindowManager;
    private final Observer<Boolean> mObserver = hasNotifs -> updateLightsOutView();

    /** @see android.view.WindowInsetsController#setSystemBarsAppearance(int, int) */
    @VisibleForTesting @Appearance int mAppearance;

    private int mDisplayId;

    @Inject
    LegacyLightsOutNotifController(
            @Named(LIGHTS_OUT_NOTIF_VIEW) View lightsOutNotifView,
            WindowManager windowManager,
            NotifLiveDataStore notifDataStore,
            CommandQueue commandQueue) {
        super(lightsOutNotifView);
        mWindowManager = windowManager;
        mNotifDataStore = notifDataStore;
        mCommandQueue = commandQueue;
    }

    @Override
    protected void onInit() {
        super.onInit();
        NotificationsLiveDataStoreRefactor.assertInLegacyMode();
    }

    @Override
    protected void onViewDetached() {
        mNotifDataStore.getHasActiveNotifs().removeObserver(mObserver);
        mCommandQueue.removeCallback(mCallback);
    }

    @Override
    protected void onViewAttached() {
        mView.setVisibility(View.GONE);
        mView.setAlpha(0f);

        mDisplayId = mWindowManager.getDefaultDisplay().getDisplayId();
        mNotifDataStore.getHasActiveNotifs().addSyncObserver(mObserver);
        mCommandQueue.addCallback(mCallback);

        updateLightsOutView();
    }

    private boolean hasActiveNotifications() {
        return mNotifDataStore.getHasActiveNotifs().getValue();
    }

    @VisibleForTesting
    void updateLightsOutView() {
        final boolean showDot = shouldShowDot();
        if (showDot != isShowingDot()) {
            if (showDot) {
                mView.setAlpha(0f);
                mView.setVisibility(View.VISIBLE);
            }

            mView.animate()
                    .alpha(showDot ? 1 : 0)
                    .setDuration(showDot ? 750 : 250)
                    .setInterpolator(new AccelerateInterpolator(2.0f))
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator a) {
                            mView.setAlpha(showDot ? 1 : 0);
                            mView.setVisibility(showDot ? View.VISIBLE : View.GONE);
                            // Unset the listener, otherwise this may persist for
                            // another view property animation
                            mView.animate().setListener(null);
                        }
                    })
                    .start();
        }
    }

    @VisibleForTesting
    boolean isShowingDot() {
        return mView.getVisibility() == View.VISIBLE
                && mView.getAlpha() == 1.0f;
    }

    @VisibleForTesting
    boolean shouldShowDot() {
        return hasActiveNotifications() && areLightsOut();
    }

    @VisibleForTesting
    boolean areLightsOut() {
        return 0 != (mAppearance & APPEARANCE_LOW_PROFILE_BARS);
    }

    private final CommandQueue.Callbacks mCallback = new CommandQueue.Callbacks() {
        @Override
        public void onSystemBarAttributesChanged(int displayId, @Appearance int appearance,
                AppearanceRegion[] appearanceRegions, boolean navbarColorManagedByIme,
                @Behavior int behavior, @InsetsType int requestedVisibleTypes,
                String packageName, LetterboxDetails[] letterboxDetails) {
            if (displayId != mDisplayId) {
                return;
            }
            mAppearance = appearance;
            updateLightsOutView();
        }
    };
}
+0 −5
Original line number Diff line number Diff line
@@ -24,7 +24,6 @@ import com.android.systemui.statusbar.core.NewStatusBarIcons;
import com.android.systemui.statusbar.data.repository.StatusBarConfigurationController;
import com.android.systemui.statusbar.layout.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.HeadsUpAppearanceController;
import com.android.systemui.statusbar.phone.LegacyLightsOutNotifController;
import com.android.systemui.statusbar.phone.PhoneStatusBarTransitions;
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
@@ -114,10 +113,6 @@ public interface HomeStatusBarComponent {
    @HomeStatusBarScope
    HeadsUpAppearanceController getHeadsUpAppearanceController();

    /** */
    @HomeStatusBarScope
    LegacyLightsOutNotifController getLegacyLightsOutNotifController();

    /** */
    @HomeStatusBarScope
    StatusBarDemoMode getStatusBarDemoMode();