• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 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 package com.android.launcher3.util
17 
18 import android.content.Context
19 import android.content.res.Configuration
20 import android.content.res.Resources
21 import android.graphics.Point
22 import android.graphics.Rect
23 import android.hardware.display.DisplayManager
24 import android.util.ArrayMap
25 import android.util.DisplayMetrics
26 import android.view.Display
27 import android.view.Surface
28 import androidx.test.annotation.UiThreadTest
29 import androidx.test.core.app.ApplicationProvider
30 import androidx.test.filters.SmallTest
31 import com.android.launcher3.LauncherPrefs
32 import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
33 import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE
34 import com.android.launcher3.util.DisplayController.CHANGE_DENSITY
35 import com.android.launcher3.util.DisplayController.CHANGE_ROTATION
36 import com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING
37 import com.android.launcher3.util.DisplayController.DisplayInfoChangeListener
38 import com.android.launcher3.util.MainThreadInitializedObject.SandboxContext
39 import com.android.launcher3.util.window.CachedDisplayInfo
40 import com.android.launcher3.util.window.WindowManagerProxy
41 import kotlin.math.min
42 import org.junit.Before
43 import org.junit.Test
44 import org.junit.runner.RunWith
45 import org.mockito.kotlin.any
46 import org.mockito.kotlin.anyOrNull
47 import org.mockito.kotlin.doNothing
48 import org.mockito.kotlin.doReturn
49 import org.mockito.kotlin.eq
50 import org.mockito.kotlin.mock
51 import org.mockito.kotlin.verify
52 import org.mockito.kotlin.whenever
53 import org.mockito.stubbing.Answer
54 
55 /** Unit tests for {@link DisplayController} */
56 @SmallTest
57 @RunWith(LauncherMultivalentJUnit::class)
58 class DisplayControllerTest {
59 
60     private val appContext: Context = ApplicationProvider.getApplicationContext()
61 
62     private val context: SandboxContext = mock()
63     private val windowManagerProxy: WindowManagerProxy = mock()
64     private val launcherPrefs: LauncherPrefs = mock()
65     private val displayManager: DisplayManager = mock()
66     private val display: Display = mock()
67     private val resources: Resources = mock()
68     private val displayInfoChangeListener: DisplayInfoChangeListener = mock()
69 
70     private lateinit var displayController: DisplayController
71 
72     private val width = 2208
73     private val height = 1840
74     private val inset = 110
75     private val densityDpi = 420
76     private val density = densityDpi / DisplayMetrics.DENSITY_DEFAULT.toFloat()
77     private val bounds =
78         arrayOf(
79             WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_0),
80             WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_90),
81             WindowBounds(Rect(0, 0, width, height), Rect(0, inset, 0, 0), Surface.ROTATION_180),
82             WindowBounds(Rect(0, 0, height, width), Rect(0, inset, 0, 0), Surface.ROTATION_270)
83         )
84     private val configuration =
85         Configuration(appContext.resources.configuration).apply {
86             densityDpi = this@DisplayControllerTest.densityDpi
87             screenWidthDp = (bounds[0].bounds.width() / density).toInt()
88             screenHeightDp = (bounds[0].bounds.height() / density).toInt()
89             smallestScreenWidthDp = min(screenWidthDp, screenHeightDp)
90         }
91 
92     @Before
93     fun setUp() {
94         whenever(context.getObject(eq(WindowManagerProxy.INSTANCE))).thenReturn(windowManagerProxy)
95         whenever(context.getObject(eq(LauncherPrefs.INSTANCE))).thenReturn(launcherPrefs)
96         whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(false)
97         whenever(launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(true)
98 
99         // Mock WindowManagerProxy
100         val displayInfo = CachedDisplayInfo(Point(width, height), Surface.ROTATION_0)
101         whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
102         whenever(windowManagerProxy.estimateInternalDisplayBounds(any()))
103             .thenAnswer(
104                 Answer {
105                     // Always create a new copy of bounds
106                     val perDisplayBounds = ArrayMap<CachedDisplayInfo, List<WindowBounds>>()
107                     perDisplayBounds[displayInfo] = bounds.toList()
108                     return@Answer perDisplayBounds
109                 }
110             )
111         whenever(windowManagerProxy.getRealBounds(any(), any())).thenAnswer { i ->
112             bounds[i.getArgument<CachedDisplayInfo>(1).rotation]
113         }
114 
115         whenever(windowManagerProxy.getNavigationMode(any())).thenReturn(NavigationMode.NO_BUTTON)
116         // Mock context
117         whenever(context.createWindowContext(any(), any(), anyOrNull())).thenReturn(context)
118         whenever(context.getSystemService(eq(DisplayManager::class.java)))
119             .thenReturn(displayManager)
120         doNothing().whenever(context).registerComponentCallbacks(any())
121 
122         // Mock display
123         whenever(display.rotation).thenReturn(displayInfo.rotation)
124         whenever(context.display).thenReturn(display)
125         whenever(displayManager.getDisplay(any())).thenReturn(display)
126 
127         // Mock resources
128         doReturn(context).whenever(context).applicationContext
129         whenever(resources.configuration).thenReturn(configuration)
130         whenever(context.resources).thenReturn(resources)
131 
132         // Initialize DisplayController
133         displayController = DisplayController(context)
134         displayController.addChangeListener(displayInfoChangeListener)
135     }
136 
137     @Test
138     @UiThreadTest
139     fun testRotation() {
140         val displayInfo = CachedDisplayInfo(Point(height, width), Surface.ROTATION_90)
141         whenever(windowManagerProxy.getDisplayInfo(any())).thenReturn(displayInfo)
142         whenever(display.rotation).thenReturn(displayInfo.rotation)
143         val configuration =
144             Configuration(configuration).apply {
145                 screenWidthDp = configuration.screenHeightDp
146                 screenHeightDp = configuration.screenWidthDp
147             }
148         whenever(resources.configuration).thenReturn(configuration)
149 
150         displayController.onConfigurationChanged(configuration)
151 
152         verify(displayInfoChangeListener).onDisplayInfoChanged(any(), any(), eq(CHANGE_ROTATION))
153     }
154 
155     @Test
156     @UiThreadTest
157     fun testFontScale() {
158         val configuration = Configuration(configuration).apply { fontScale = 1.2f }
159         whenever(resources.configuration).thenReturn(configuration)
160 
161         displayController.onConfigurationChanged(configuration)
162 
163         verify(displayInfoChangeListener).onDisplayInfoChanged(any(), any(), eq(CHANGE_DENSITY))
164     }
165 
166     @Test
167     @UiThreadTest
168     fun testTaskbarPinning() {
169         whenever(launcherPrefs.get(TASKBAR_PINNING)).thenReturn(true)
170         displayController.handleInfoChange(display)
171         verify(displayInfoChangeListener)
172             .onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
173     }
174 
175     @Test
176     @UiThreadTest
177     fun testTaskbarPinningChangeInDesktopMode() {
178         whenever(launcherPrefs.get(TASKBAR_PINNING_IN_DESKTOP_MODE)).thenReturn(false)
179         displayController.handleInfoChange(display)
180         verify(displayInfoChangeListener)
181             .onDisplayInfoChanged(any(), any(), eq(CHANGE_TASKBAR_PINNING))
182     }
183 }
184