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

Commit 9705b01b authored by Dave Mankoff's avatar Dave Mankoff
Browse files

Remove View Injection from KeyguardClockSwitch.

Adds a controller for the KeyguardClockSwitch, and
moves some of its logic over.

Fixes: 162500959
Test: atest SystemUITests
Change-Id: Ib606ec727e5d005a0668a96dc823a7d1d1a53c79
parent b9045b33
Loading
Loading
Loading
Loading
+10 −106
Original line number Diff line number Diff line
package com.android.keyguard;

import static com.android.systemui.util.InjectionInflationController.VIEW_CONTEXT;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.app.WallpaperManager;
import android.content.Context;
import android.graphics.Paint;
import android.graphics.Paint.Style;
@@ -24,16 +21,10 @@ import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextClock;

import androidx.annotation.VisibleForTesting;

import com.android.internal.colorextraction.ColorExtractor;
import com.android.internal.colorextraction.ColorExtractor.OnColorsChangedListener;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.util.wakelock.KeepAwakeAnimationListener;

@@ -42,37 +33,18 @@ import java.io.PrintWriter;
import java.util.Arrays;
import java.util.TimeZone;

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

/**
 * Switch to show plugin clock when plugin is connected, otherwise it will show default clock.
 */
public class KeyguardClockSwitch extends RelativeLayout {

    private static final String TAG = "KeyguardClockSwitch";
    private static final boolean CUSTOM_CLOCKS_ENABLED = true;

    /**
     * Animation fraction when text is transitioned to/from bold.
     */
    private static final float TO_BOLD_TRANSITION_FRACTION = 0.7f;

    /**
     * Controller used to track StatusBar state to know when to show the big_clock_container.
     */
    private final StatusBarStateController mStatusBarStateController;

    /**
     * Color extractor used to apply colors from wallpaper to custom clock faces.
     */
    private final SysuiColorExtractor mSysuiColorExtractor;

    /**
     * Manager used to know when to show a custom clock face.
     */
    private final ClockManager mClockManager;

    /**
     * Layout transition that scales the default clock face.
     */
@@ -130,42 +102,8 @@ public class KeyguardClockSwitch extends RelativeLayout {
    private boolean mSupportsDarkText;
    private int[] mColorPalette;

    /**
     * Track the state of the status bar to know when to hide the big_clock_container.
     */
    private int mStatusBarState;

    private final StatusBarStateController.StateListener mStateListener =
            new StatusBarStateController.StateListener() {
                @Override
                public void onStateChanged(int newState) {
                    mStatusBarState = newState;
                    updateBigClockVisibility();
                }
            };

    private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;

    /**
     * Listener for changes to the color palette.
     *
     * The color palette changes when the wallpaper is changed.
     */
    private final OnColorsChangedListener mColorsListener = (extractor, which) -> {
        if ((which & WallpaperManager.FLAG_LOCK) != 0) {
            updateColors();
        }
    };

    @Inject
    public KeyguardClockSwitch(@Named(VIEW_CONTEXT) Context context, AttributeSet attrs,
            StatusBarStateController statusBarStateController, SysuiColorExtractor colorExtractor,
            ClockManager clockManager) {
    public KeyguardClockSwitch(Context context, AttributeSet attrs) {
        super(context, attrs);
        mStatusBarStateController = statusBarStateController;
        mStatusBarState = mStatusBarStateController.getState();
        mSysuiColorExtractor = colorExtractor;
        mClockManager = clockManager;

        mClockTransition = new ClockVisibilityTransition().setCutoff(
                1 - TO_BOLD_TRANSITION_FRACTION);
@@ -197,29 +135,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
        mKeyguardStatusArea = findViewById(R.id.keyguard_status_area);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (CUSTOM_CLOCKS_ENABLED) {
            mClockManager.addOnClockChangedListener(mClockChangedListener);
        }
        mStatusBarStateController.addCallback(mStateListener);
        mSysuiColorExtractor.addOnColorsChangedListener(mColorsListener);
        updateColors();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (CUSTOM_CLOCKS_ENABLED) {
            mClockManager.removeOnClockChangedListener(mClockChangedListener);
        }
        mStatusBarStateController.removeCallback(mStateListener);
        mSysuiColorExtractor.removeOnColorsChangedListener(mColorsListener);
        setClockPlugin(null);
    }

    private void setClockPlugin(ClockPlugin plugin) {
    void setClockPlugin(ClockPlugin plugin, int statusBarState) {
        // Disconnect from existing plugin.
        if (mClockPlugin != null) {
            View smallClockView = mClockPlugin.getView();
@@ -228,7 +144,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
            }
            if (mBigClockContainer != null) {
                mBigClockContainer.removeAllViews();
                updateBigClockVisibility();
                updateBigClockVisibility(statusBarState);
            }
            mClockPlugin.onDestroyView();
            mClockPlugin = null;
@@ -256,7 +172,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
        View bigClockView = plugin.getBigClockView();
        if (bigClockView != null && mBigClockContainer != null) {
            mBigClockContainer.addView(bigClockView);
            updateBigClockVisibility();
            updateBigClockVisibility(statusBarState);
        }
        // Hide default clock.
        if (!plugin.shouldShowStatusArea()) {
@@ -275,7 +191,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
    /**
     * Set container for big clock face appearing behind NSSL and KeyguardStatusView.
     */
    public void setBigClockContainer(ViewGroup container) {
    public void setBigClockContainer(ViewGroup container, int statusBarState) {
        if (mClockPlugin != null && container != null) {
            View bigClockView = mClockPlugin.getBigClockView();
            if (bigClockView != null) {
@@ -283,7 +199,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
            }
        }
        mBigClockContainer = container;
        updateBigClockVisibility();
        updateBigClockVisibility(statusBarState);
    }

    /**
@@ -407,9 +323,7 @@ public class KeyguardClockSwitch extends RelativeLayout {
        }
    }

    private void updateColors() {
        ColorExtractor.GradientColors colors = mSysuiColorExtractor.getColors(
                WallpaperManager.FLAG_LOCK);
    void updateColors(ColorExtractor.GradientColors colors) {
        mSupportsDarkText = colors.supportsDarkText();
        mColorPalette = colors.getColorPalette();
        if (mClockPlugin != null) {
@@ -417,12 +331,12 @@ public class KeyguardClockSwitch extends RelativeLayout {
        }
    }

    private void updateBigClockVisibility() {
    void updateBigClockVisibility(int statusBarState) {
        if (mBigClockContainer == null) {
            return;
        }
        final boolean inDisplayState = mStatusBarState == StatusBarState.KEYGUARD
                || mStatusBarState == StatusBarState.SHADE_LOCKED;
        final boolean inDisplayState = statusBarState == StatusBarState.KEYGUARD
                || statusBarState == StatusBarState.SHADE_LOCKED;
        final int visibility = !mShowingHeader && inDisplayState
                    && mBigClockContainer.getChildCount() != 0 ? View.VISIBLE : View.GONE;
        if (mBigClockContainer.getVisibility() != visibility) {
@@ -506,16 +420,6 @@ public class KeyguardClockSwitch extends RelativeLayout {
        }
    }

    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    ClockManager.ClockChangedListener getClockChangedListener() {
        return mClockChangedListener;
    }

    @VisibleForTesting(otherwise = VisibleForTesting.NONE)
    StatusBarStateController.StateListener getStateListener() {
        return mStateListener;
    }

    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        pw.println("KeyguardClockSwitch:");
        pw.println("  mClockPlugin: " + mClockPlugin);
+119 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.keyguard;

import android.app.WallpaperManager;
import android.view.View;
import android.view.ViewGroup;

import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;

import javax.inject.Inject;

/**
 * Injectable controller for {@link KeyguardClockSwitch}.
 */
public class KeyguardClockSwitchController {
    private static final boolean CUSTOM_CLOCKS_ENABLED = true;

    private final StatusBarStateController mStatusBarStateController;
    private final SysuiColorExtractor mColorExtractor;
    private final ClockManager mClockManager;
    private KeyguardClockSwitch mView;

    private final StatusBarStateController.StateListener mStateListener =
            new StatusBarStateController.StateListener() {
                @Override
                public void onStateChanged(int newState) {
                    mView.updateBigClockVisibility(newState);
                }
            };

    /**
     * Listener for changes to the color palette.
     *
     * The color palette changes when the wallpaper is changed.
     */
    private final ColorExtractor.OnColorsChangedListener mColorsListener = (extractor, which) -> {
        if ((which & WallpaperManager.FLAG_LOCK) != 0) {
            mView.updateColors(getGradientColors());
        }
    };

    private ClockManager.ClockChangedListener mClockChangedListener = this::setClockPlugin;

    private final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
            new View.OnAttachStateChangeListener() {
        @Override
        public void onViewAttachedToWindow(View v) {
            if (CUSTOM_CLOCKS_ENABLED) {
                mClockManager.addOnClockChangedListener(mClockChangedListener);
            }
            mStatusBarStateController.addCallback(mStateListener);
            mColorExtractor.addOnColorsChangedListener(mColorsListener);
            mView.updateColors(getGradientColors());
        }

        @Override
        public void onViewDetachedFromWindow(View v) {
            if (CUSTOM_CLOCKS_ENABLED) {
                mClockManager.removeOnClockChangedListener(mClockChangedListener);
            }
            mStatusBarStateController.removeCallback(mStateListener);
            mColorExtractor.removeOnColorsChangedListener(mColorsListener);
            mView.setClockPlugin(null, mStatusBarStateController.getState());
        }
    };

    @Inject
    public KeyguardClockSwitchController(StatusBarStateController statusBarStateController,
            SysuiColorExtractor colorExtractor, ClockManager clockManager) {
        mStatusBarStateController = statusBarStateController;
        mColorExtractor = colorExtractor;
        mClockManager = clockManager;
    }

    /**
     * Attach the controller to the view it relates to.
     */
    public void attach(KeyguardClockSwitch view) {
        mView = view;
        if (mView.isAttachedToWindow()) {
            mOnAttachStateChangeListener.onViewAttachedToWindow(mView);
        }
        mView.addOnAttachStateChangeListener(mOnAttachStateChangeListener);
    }

    /**
     * Set container for big clock face appearing behind NSSL and KeyguardStatusView.
     */
    public void setBigClockContainer(ViewGroup bigClockContainer) {
        mView.setBigClockContainer(bigClockContainer, mStatusBarStateController.getState());
    }

    private void setClockPlugin(ClockPlugin plugin) {
        mView.setClockPlugin(plugin, mStatusBarStateController.getState());
    }

    private ColorExtractor.GradientColors getGradientColors() {
        return mColorExtractor.getColors(WallpaperManager.FLAG_LOCK);
    }
}
+14 −6
Original line number Diff line number Diff line
@@ -62,7 +62,7 @@ import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardClockSwitchController;
import com.android.keyguard.KeyguardStatusView;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -124,6 +124,7 @@ import java.util.function.Consumer;
import java.util.function.Function;

import javax.inject.Inject;
import javax.inject.Provider;

@StatusBarComponent.StatusBarScope
public class NotificationPanelViewController extends PanelViewController {
@@ -252,6 +253,7 @@ public class NotificationPanelViewController extends PanelViewController {
    private final ConversationNotificationManager mConversationNotificationManager;
    private final MediaHierarchyManager mMediaHierarchyManager;
    private final StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
    private final Provider<KeyguardClockSwitchController> mKeyguardClockSwitchControllerProvider;

    private KeyguardAffordanceHelper mAffordanceHelper;
    private KeyguardUserSwitcher mKeyguardUserSwitcher;
@@ -495,7 +497,8 @@ public class NotificationPanelViewController extends PanelViewController {
            ConversationNotificationManager conversationNotificationManager,
            MediaHierarchyManager mediaHierarchyManager,
            BiometricUnlockController biometricUnlockController,
            StatusBarKeyguardViewManager statusBarKeyguardViewManager) {
            StatusBarKeyguardViewManager statusBarKeyguardViewManager,
            Provider<KeyguardClockSwitchController> keyguardClockSwitchControllerProvider) {
        super(view, falsingManager, dozeLog, keyguardStateController,
                (SysuiStatusBarStateController) statusBarStateController, vibratorHelper,
                latencyTracker, flingAnimationUtilsBuilder, statusBarTouchableRegionManager);
@@ -507,6 +510,7 @@ public class NotificationPanelViewController extends PanelViewController {
        mFlingAnimationUtilsBuilder = flingAnimationUtilsBuilder;
        mMediaHierarchyManager = mediaHierarchyManager;
        mStatusBarKeyguardViewManager = statusBarKeyguardViewManager;
        mKeyguardClockSwitchControllerProvider = keyguardClockSwitchControllerProvider;
        mView.setWillNotDraw(!DEBUG);
        mInjectionInflationController = injectionInflationController;
        mFalsingManager = falsingManager;
@@ -579,9 +583,11 @@ public class NotificationPanelViewController extends PanelViewController {
        mKeyguardStatusBar = mView.findViewById(R.id.keyguard_header);
        mKeyguardStatusView = mView.findViewById(R.id.keyguard_status_view);

        KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
        KeyguardClockSwitchController keyguardClockSwitchController =
                mKeyguardClockSwitchControllerProvider.get();
        keyguardClockSwitchController.attach(mView.findViewById(R.id.keyguard_clock_container));
        mBigClockContainer = mView.findViewById(R.id.big_clock_container);
        keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
        keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);

        mNotificationContainerParent = mView.findViewById(R.id.notification_container_parent);
        mNotificationStackScroller = mView.findViewById(R.id.notification_stack_scroller);
@@ -703,8 +709,10 @@ public class NotificationPanelViewController extends PanelViewController {

        // Re-associate the clock container with the keyguard clock switch.
        mBigClockContainer.removeAllViews();
        KeyguardClockSwitch keyguardClockSwitch = mView.findViewById(R.id.keyguard_clock_container);
        keyguardClockSwitch.setBigClockContainer(mBigClockContainer);
        KeyguardClockSwitchController keyguardClockSwitchController =
                mKeyguardClockSwitchControllerProvider.get();
        keyguardClockSwitchController.attach(mView.findViewById(R.id.keyguard_clock_container));
        keyguardClockSwitchController.setBigClockContainer(mBigClockContainer);

        // Update keyguard bottom area
        index = mView.indexOfChild(mKeyguardBottomArea);
+0 −6
Original line number Diff line number Diff line
@@ -23,7 +23,6 @@ import android.view.InflateException;
import android.view.LayoutInflater;
import android.view.View;

import com.android.keyguard.KeyguardClockSwitch;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardSliceView;
import com.android.systemui.dagger.SystemUIRootComponent;
@@ -131,11 +130,6 @@ public class InjectionInflationController {
         */
        NotificationShelf creatNotificationShelf();

        /**
         * Creates the KeyguardClockSwitch.
         */
        KeyguardClockSwitch createKeyguardClockSwitch();

        /**
         * Creates the KeyguardSliceView.
         */
+162 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.keyguard;


import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
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.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import com.android.internal.colorextraction.ColorExtractor;
import com.android.keyguard.clock.ClockManager;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.plugins.ClockPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.StatusBarState;

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

@SmallTest
@RunWith(AndroidTestingRunner.class)
public class KeyguardClockSwitchControllerTest extends SysuiTestCase {

    @Mock
    private StatusBarStateController mStatusBarStateController;
    @Mock
    private SysuiColorExtractor mColorExtractor;
    @Mock
    private ClockManager mClockManager;
    @Mock
    private KeyguardClockSwitch mView;
    @Mock
    private ClockPlugin mClockPlugin;
    @Mock
    ColorExtractor.GradientColors mGradientColors;

    private KeyguardClockSwitchController mController;

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

        mController = new KeyguardClockSwitchController(
                mStatusBarStateController, mColorExtractor, mClockManager);

        when(mView.isAttachedToWindow()).thenReturn(true);
        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
        when(mColorExtractor.getColors(anyInt())).thenReturn(mGradientColors);
    }

    @Test
    public void testAttach_viewAlreadyAttached() {
        mController.attach(mView);

        verifyAttachment(times(1));
    }

    @Test
    public void testAttach_viewNotYetAttached() {
        ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
                ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);

        when(mView.isAttachedToWindow()).thenReturn(false);
        mController.attach(mView);
        verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());

        verifyAttachment(never());

        listenerArgumentCaptor.getValue().onViewAttachedToWindow(mView);

        verifyAttachment(times(1));
    }


    @Test
    public void testAttach_viewDetached() {
        ArgumentCaptor<View.OnAttachStateChangeListener> listenerArgumentCaptor =
                ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
        mController.attach(mView);
        verify(mView).addOnAttachStateChangeListener(listenerArgumentCaptor.capture());

        verifyAttachment(times(1));

        listenerArgumentCaptor.getValue().onViewDetachedFromWindow(mView);

        verify(mStatusBarStateController).removeCallback(
                any(StatusBarStateController.StateListener.class));
        verify(mColorExtractor).removeOnColorsChangedListener(
                any(ColorExtractor.OnColorsChangedListener.class));
    }

    @Test
    public void testBigClockPassesStatusBarState() {
        ViewGroup testView = new FrameLayout(mContext);

        mController.attach(mView);
        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE);
        mController.setBigClockContainer(testView);
        verify(mView).setBigClockContainer(testView, StatusBarState.SHADE);


        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.KEYGUARD);
        mController.setBigClockContainer(testView);
        verify(mView).setBigClockContainer(testView, StatusBarState.KEYGUARD);


        when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED);
        mController.setBigClockContainer(testView);
        verify(mView).setBigClockContainer(testView, StatusBarState.SHADE_LOCKED);
    }

    @Test
    public void testPluginPassesStatusBarState() {
        ArgumentCaptor<ClockManager.ClockChangedListener> listenerArgumentCaptor =
                ArgumentCaptor.forClass(ClockManager.ClockChangedListener.class);

        mController.attach(mView);
        verify(mClockManager).addOnClockChangedListener(listenerArgumentCaptor.capture());

        listenerArgumentCaptor.getValue().onClockChanged(mClockPlugin);
        verify(mView).setClockPlugin(mClockPlugin, StatusBarState.SHADE);
    }

    private void verifyAttachment(VerificationMode times) {
        verify(mClockManager, times).addOnClockChangedListener(
                any(ClockManager.ClockChangedListener.class));
        verify(mStatusBarStateController, times).addCallback(
                any(StatusBarStateController.StateListener.class));
        verify(mColorExtractor, times).addOnColorsChangedListener(
                any(ColorExtractor.OnColorsChangedListener.class));
        verify(mView, times).updateColors(mGradientColors);
    }
}
Loading