1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.qs 18 19 import android.content.res.Configuration 20 import android.testing.AndroidTestingRunner 21 import androidx.test.filters.SmallTest 22 import com.android.internal.logging.MetricsLogger 23 import com.android.internal.logging.testing.UiEventLoggerFake 24 import com.android.systemui.SysuiTestCase 25 import com.android.systemui.dump.DumpManager 26 import com.android.systemui.media.controls.ui.MediaHost 27 import com.android.systemui.media.controls.ui.MediaHostState 28 import com.android.systemui.plugins.qs.QSTile 29 import com.android.systemui.plugins.qs.QSTileView 30 import com.android.systemui.qs.customize.QSCustomizerController 31 import com.android.systemui.qs.logging.QSLogger 32 import com.android.systemui.util.leak.RotationUtils 33 import org.junit.After 34 import org.junit.Before 35 import org.junit.Test 36 import org.junit.runner.RunWith 37 import org.mockito.ArgumentCaptor 38 import org.mockito.ArgumentMatchers.anyBoolean 39 import org.mockito.Captor 40 import org.mockito.Mock 41 import org.mockito.Mockito.any 42 import org.mockito.Mockito.reset 43 import org.mockito.Mockito.times 44 import org.mockito.Mockito.verify 45 import org.mockito.Mockito.`when` as whenever 46 import org.mockito.MockitoAnnotations 47 48 @SmallTest 49 @RunWith(AndroidTestingRunner::class) 50 class QuickQSPanelControllerTest : SysuiTestCase() { 51 52 @Mock private lateinit var quickQSPanel: QuickQSPanel 53 @Mock private lateinit var qsHost: QSHost 54 @Mock private lateinit var qsCustomizerController: QSCustomizerController 55 @Mock private lateinit var mediaHost: MediaHost 56 @Mock private lateinit var metricsLogger: MetricsLogger 57 @Mock private lateinit var qsLogger: QSLogger 58 @Mock private lateinit var tile: QSTile 59 @Mock private lateinit var tileLayout: TileLayout 60 @Mock private lateinit var tileView: QSTileView 61 @Captor private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener> 62 63 private val uiEventLogger = UiEventLoggerFake() 64 private val dumpManager = DumpManager() 65 66 private var usingCollapsedLandscapeMedia = true 67 68 private lateinit var controller: TestQuickQSPanelController 69 70 @Before 71 fun setUp() { 72 MockitoAnnotations.initMocks(this) 73 74 whenever(quickQSPanel.tileLayout).thenReturn(tileLayout) 75 whenever(quickQSPanel.isAttachedToWindow).thenReturn(true) 76 whenever(quickQSPanel.dumpableTag).thenReturn("") 77 whenever(quickQSPanel.resources).thenReturn(mContext.resources) 78 whenever(qsHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView) 79 80 controller = 81 TestQuickQSPanelController( 82 quickQSPanel, 83 qsHost, 84 qsCustomizerController, 85 /* usingMediaPlayer = */ false, 86 mediaHost, 87 { usingCollapsedLandscapeMedia }, 88 metricsLogger, 89 uiEventLogger, 90 qsLogger, 91 dumpManager) 92 93 controller.init() 94 } 95 96 @After 97 fun tearDown() { 98 controller.onViewDetached() 99 } 100 101 @Test 102 fun testTileSublistWithFewerTiles_noCrash() { 103 whenever(quickQSPanel.numQuickTiles).thenReturn(3) 104 105 whenever(qsHost.tiles).thenReturn(listOf(tile, tile)) 106 107 controller.setTiles() 108 } 109 110 @Test 111 fun testTileSublistWithTooManyTiles() { 112 val limit = 3 113 whenever(quickQSPanel.numQuickTiles).thenReturn(limit) 114 whenever(qsHost.tiles).thenReturn(listOf(tile, tile, tile, tile)) 115 116 controller.setTiles() 117 118 verify(quickQSPanel, times(limit)).addTile(any()) 119 } 120 121 @Test 122 fun mediaExpansion_afterConfigChange_inLandscape_collapsedInLandscapeTrue_updatesToCollapsed() { 123 verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture()) 124 125 // verify that media starts in the expanded state by default 126 verify(mediaHost).expansion = MediaHostState.EXPANDED 127 128 // Rotate device, verify media size updated to collapsed 129 usingCollapsedLandscapeMedia = true 130 controller.setRotation(RotationUtils.ROTATION_LANDSCAPE) 131 captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) } 132 133 verify(mediaHost).expansion = MediaHostState.COLLAPSED 134 } 135 136 @Test 137 fun mediaExpansion_afterConfigChange_landscape_collapsedInLandscapeFalse_remainsExpanded() { 138 verify(quickQSPanel).addOnConfigurationChangedListener(captor.capture()) 139 reset(mediaHost) 140 141 usingCollapsedLandscapeMedia = false 142 controller.setRotation(RotationUtils.ROTATION_LANDSCAPE) 143 captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) } 144 145 verify(mediaHost).expansion = MediaHostState.EXPANDED 146 } 147 148 class TestQuickQSPanelController( 149 view: QuickQSPanel, 150 qsHost: QSHost, 151 qsCustomizerController: QSCustomizerController, 152 usingMediaPlayer: Boolean, 153 mediaHost: MediaHost, 154 usingCollapsedLandscapeMedia: () -> Boolean, 155 metricsLogger: MetricsLogger, 156 uiEventLogger: UiEventLoggerFake, 157 qsLogger: QSLogger, 158 dumpManager: DumpManager 159 ) : 160 QuickQSPanelController( 161 view, 162 qsHost, 163 qsCustomizerController, 164 usingMediaPlayer, 165 mediaHost, 166 usingCollapsedLandscapeMedia, 167 metricsLogger, 168 uiEventLogger, 169 qsLogger, 170 dumpManager) { 171 172 private var rotation = RotationUtils.ROTATION_NONE 173 174 @Override override fun getRotation(): Int = rotation 175 176 fun setRotation(newRotation: Int) { 177 rotation = newRotation 178 } 179 } 180 } 181