| /* |
| * 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.systemui.qs; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyBoolean; |
| import static org.mockito.ArgumentMatchers.anyFloat; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.reset; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.testing.AndroidTestingRunner; |
| import android.testing.TestableLooper.RunWithLooper; |
| |
| import androidx.test.filters.SmallTest; |
| |
| import com.android.internal.logging.MetricsLogger; |
| import com.android.internal.logging.UiEventLogger; |
| import com.android.internal.logging.nano.MetricsProto.MetricsEvent; |
| import com.android.internal.logging.testing.UiEventLoggerFake; |
| import com.android.systemui.R; |
| import com.android.systemui.SysuiTestCase; |
| import com.android.systemui.dump.DumpManager; |
| import com.android.systemui.media.MediaHost; |
| import com.android.systemui.plugins.qs.QSTileView; |
| import com.android.systemui.qs.customize.QSCustomizerController; |
| import com.android.systemui.qs.logging.QSLogger; |
| import com.android.systemui.qs.tileimpl.QSTileImpl; |
| import com.android.systemui.statusbar.FeatureFlags; |
| import com.android.systemui.util.animation.DisappearParameters; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| |
| import java.io.FileDescriptor; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.Collections; |
| |
| @RunWith(AndroidTestingRunner.class) |
| @RunWithLooper |
| @SmallTest |
| public class QSPanelControllerBaseTest extends SysuiTestCase { |
| |
| @Mock |
| private QSPanel mQSPanel; |
| @Mock |
| private QSTileHost mQSTileHost; |
| @Mock |
| private QSCustomizerController mQSCustomizerController; |
| @Mock |
| private QSTileRevealController.Factory mQSTileRevealControllerFactory; |
| @Mock |
| private QSTileRevealController mQSTileRevealController; |
| @Mock |
| private MediaHost mMediaHost; |
| @Mock |
| private MetricsLogger mMetricsLogger; |
| private UiEventLoggerFake mUiEventLogger = new UiEventLoggerFake(); |
| @Mock |
| private QSLogger mQSLogger; |
| private DumpManager mDumpManager = new DumpManager(); |
| @Mock |
| QSTileImpl mQSTile; |
| @Mock |
| QSTileView mQSTileView; |
| @Mock |
| PagedTileLayout mPagedTileLayout; |
| @Mock |
| FeatureFlags mFeatureFlags; |
| @Mock |
| Resources mResources; |
| @Mock |
| Configuration mConfiguration; |
| @Mock |
| Runnable mHorizontalLayoutListener; |
| |
| private QSPanelControllerBase<QSPanel> mController; |
| |
| /** Implementation needed to ensure we have a reflectively-available class name. */ |
| private class TestableQSPanelControllerBase extends QSPanelControllerBase<QSPanel> { |
| protected TestableQSPanelControllerBase(QSPanel view, QSTileHost host, |
| QSCustomizerController qsCustomizerController, MediaHost mediaHost, |
| MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger, |
| DumpManager dumpManager, FeatureFlags featureFlags) { |
| super(view, host, qsCustomizerController, true, mediaHost, metricsLogger, uiEventLogger, |
| qsLogger, dumpManager, featureFlags); |
| } |
| |
| @Override |
| protected QSTileRevealController createTileRevealController() { |
| return mQSTileRevealController; |
| } |
| } |
| |
| @Before |
| public void setup() throws Exception { |
| MockitoAnnotations.initMocks(this); |
| |
| when(mQSPanel.isAttachedToWindow()).thenReturn(true); |
| when(mQSPanel.getDumpableTag()).thenReturn("QSPanel"); |
| when(mQSPanel.openPanelEvent()).thenReturn(QSEvent.QS_PANEL_EXPANDED); |
| when(mQSPanel.closePanelEvent()).thenReturn(QSEvent.QS_PANEL_COLLAPSED); |
| when(mQSPanel.getOrCreateTileLayout()).thenReturn(mPagedTileLayout); |
| when(mQSPanel.getTileLayout()).thenReturn(mPagedTileLayout); |
| when(mQSTile.getTileSpec()).thenReturn("dnd"); |
| when(mQSTileHost.getTiles()).thenReturn(Collections.singleton(mQSTile)); |
| when(mQSTileHost.createTileView(any(), eq(mQSTile), anyBoolean())).thenReturn(mQSTileView); |
| when(mQSTileRevealControllerFactory.create(any(), any())) |
| .thenReturn(mQSTileRevealController); |
| when(mMediaHost.getDisappearParameters()).thenReturn(new DisappearParameters()); |
| when(mQSPanel.getResources()).thenReturn(mResources); |
| when(mResources.getConfiguration()).thenReturn(mConfiguration); |
| |
| mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost, |
| mQSCustomizerController, mMediaHost, |
| mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags); |
| |
| mController.init(); |
| reset(mQSTileRevealController); |
| } |
| |
| @Test |
| public void testSetRevealExpansion_preAttach() { |
| mController.onViewDetached(); |
| |
| QSPanelControllerBase<QSPanel> controller = new TestableQSPanelControllerBase(mQSPanel, |
| mQSTileHost, mQSCustomizerController, mMediaHost, |
| mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags) { |
| @Override |
| protected QSTileRevealController createTileRevealController() { |
| return mQSTileRevealController; |
| } |
| }; |
| |
| // Nothing happens until attached |
| controller.setRevealExpansion(0); |
| verify(mQSTileRevealController, never()).setExpansion(anyFloat()); |
| controller.setRevealExpansion(0.5f); |
| verify(mQSTileRevealController, never()).setExpansion(anyFloat()); |
| controller.setRevealExpansion(1); |
| verify(mQSTileRevealController, never()).setExpansion(anyFloat()); |
| |
| controller.init(); |
| verify(mQSTileRevealController).setExpansion(1); |
| } |
| |
| @Test |
| public void testSetRevealExpansion_postAttach() { |
| mController.setRevealExpansion(0); |
| verify(mQSTileRevealController).setExpansion(0); |
| mController.setRevealExpansion(0.5f); |
| verify(mQSTileRevealController).setExpansion(0.5f); |
| mController.setRevealExpansion(1); |
| verify(mQSTileRevealController).setExpansion(1); |
| } |
| |
| |
| @Test |
| public void testSetExpanded_Metrics() { |
| when(mQSPanel.isExpanded()).thenReturn(false); |
| mController.setExpanded(true); |
| verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(true)); |
| verify(mQSLogger).logPanelExpanded(true, mQSPanel.getDumpableTag()); |
| assertEquals(1, mUiEventLogger.numLogs()); |
| assertEquals(QSEvent.QS_PANEL_EXPANDED.getId(), mUiEventLogger.eventId(0)); |
| mUiEventLogger.getLogs().clear(); |
| |
| when(mQSPanel.isExpanded()).thenReturn(true); |
| mController.setExpanded(false); |
| verify(mMetricsLogger).visibility(eq(MetricsEvent.QS_PANEL), eq(false)); |
| verify(mQSLogger).logPanelExpanded(false, mQSPanel.getDumpableTag()); |
| assertEquals(1, mUiEventLogger.numLogs()); |
| assertEquals(QSEvent.QS_PANEL_COLLAPSED.getId(), mUiEventLogger.eventId(0)); |
| mUiEventLogger.getLogs().clear(); |
| |
| } |
| |
| @Test |
| public void testDump() { |
| String mockTileViewString = "Mock Tile View"; |
| String mockTileString = "Mock Tile"; |
| doAnswer(invocation -> { |
| PrintWriter pw = invocation.getArgument(1); |
| pw.println(mockTileString); |
| return null; |
| }).when(mQSTile).dump(any(FileDescriptor.class), any(PrintWriter.class), |
| any(String[].class)); |
| when(mQSTileView.toString()).thenReturn(mockTileViewString); |
| |
| StringWriter w = new StringWriter(); |
| PrintWriter pw = new PrintWriter(w); |
| mController.dump(mock(FileDescriptor.class), pw, new String[]{}); |
| String expected = "TestableQSPanelControllerBase:\n" |
| + " Tile records:\n" |
| + " " + mockTileString + "\n" |
| + " " + mockTileViewString + "\n"; |
| assertEquals(expected, w.getBuffer().toString()); |
| } |
| |
| @Test |
| public void setListening() { |
| mController.setListening(true); |
| verify(mQSLogger).logAllTilesChangeListening(true, "QSPanel", "dnd"); |
| verify(mPagedTileLayout).setListening(true, mUiEventLogger); |
| |
| mController.setListening(false); |
| verify(mQSLogger).logAllTilesChangeListening(false, "QSPanel", "dnd"); |
| verify(mPagedTileLayout).setListening(false, mUiEventLogger); |
| } |
| |
| |
| @Test |
| public void testShouldUzeHorizontalLayout_falseForSplitShade() { |
| mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE; |
| when(mMediaHost.getVisible()).thenReturn(true); |
| |
| when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(false); |
| when(mQSPanel.getDumpableTag()).thenReturn("QSPanelLandscape"); |
| mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost, |
| mQSCustomizerController, mMediaHost, |
| mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags); |
| mController.init(); |
| |
| assertThat(mController.shouldUseHorizontalLayout()).isTrue(); |
| |
| when(mFeatureFlags.isTwoColumnNotificationShadeEnabled()).thenReturn(true); |
| when(mResources.getBoolean(R.bool.config_use_split_notification_shade)).thenReturn(true); |
| when(mQSPanel.getDumpableTag()).thenReturn("QSPanelPortrait"); |
| mController = new TestableQSPanelControllerBase(mQSPanel, mQSTileHost, |
| mQSCustomizerController, mMediaHost, |
| mMetricsLogger, mUiEventLogger, mQSLogger, mDumpManager, mFeatureFlags); |
| mController.init(); |
| |
| assertThat(mController.shouldUseHorizontalLayout()).isFalse(); |
| } |
| |
| @Test |
| public void testChangeConfiguration_shouldUseHorizontalLayout() { |
| when(mMediaHost.getVisible()).thenReturn(true); |
| mController.setUsingHorizontalLayoutChangeListener(mHorizontalLayoutListener); |
| |
| // When device is rotated to landscape |
| mConfiguration.orientation = Configuration.ORIENTATION_LANDSCAPE; |
| mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration); |
| |
| // Then the layout changes |
| assertThat(mController.shouldUseHorizontalLayout()).isTrue(); |
| verify(mHorizontalLayoutListener).run(); // not invoked |
| |
| // When it is rotated back to portrait |
| mConfiguration.orientation = Configuration.ORIENTATION_PORTRAIT; |
| mController.mOnConfigurationChangedListener.onConfigurationChange(mConfiguration); |
| |
| // Then the layout changes back |
| assertThat(mController.shouldUseHorizontalLayout()).isFalse(); |
| verify(mHorizontalLayoutListener, times(2)).run(); |
| } |
| } |