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