1 /* <lambda>null2 * Copyright (C) 2024 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.wallpaper.testing 17 18 import android.content.Context 19 import android.content.res.Configuration.ORIENTATION_LANDSCAPE 20 import android.content.res.Configuration.ORIENTATION_PORTRAIT 21 import android.content.res.Configuration.Orientation 22 import android.graphics.Point 23 import android.hardware.display.DisplayManager 24 import android.view.Display 25 import com.android.wallpaper.util.DisplaysProvider 26 import dagger.hilt.android.qualifiers.ApplicationContext 27 import javax.inject.Inject 28 import javax.inject.Singleton 29 import org.robolectric.shadows.ShadowDisplayManager 30 31 /** 32 * Uses ShadowDisplayManager to create fake displays for testing, due to the difficulty in directly 33 * creating Display instances. 34 * 35 * The limitations of the fake displays include: 36 * - The created display's type will not be internal, but will function the same when testing 37 * DisplayUtils. 38 * - The created displays will have the same uniqueId, and is not customizable through 39 * ShadowDisplayManager. 40 */ 41 @Singleton 42 class FakeDisplaysProvider 43 @Inject 44 constructor(@ApplicationContext private val appContext: Context) : DisplaysProvider { 45 private val displayManager = 46 appContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager 47 48 fun setDisplays(displayConfigs: List<FakeDisplayConfig>) { 49 ShadowDisplayManager.reset() 50 displayConfigs.forEachIndexed { index, config -> 51 if (index == 0) { 52 ShadowDisplayManager.changeDisplay( 53 Display.DEFAULT_DISPLAY, 54 configToQualifierString(config) 55 ) 56 config.naturallyPortrait?.let { 57 ShadowDisplayManager.setNaturallyPortrait(Display.DEFAULT_DISPLAY, it) 58 } 59 appContext.resources.configuration.densityDpi = config.dpi 60 } else { 61 val displayId = ShadowDisplayManager.addDisplay(configToQualifierString(config)) 62 config.naturallyPortrait?.let { 63 ShadowDisplayManager.setNaturallyPortrait(displayId, it) 64 } 65 } 66 } 67 } 68 69 fun configToQualifierString(config: FakeDisplayConfig): String { 70 val suffix = 71 if (config.orientation == ORIENTATION_LANDSCAPE) "-land" 72 else if (config.orientation == ORIENTATION_PORTRAIT) "-port" else "" 73 return "w${config.displaySize.x}dp-h${config.displaySize.y}dp$suffix" 74 } 75 76 override fun getInternalDisplays(): List<Display> { 77 val allDisplays: Array<out Display> = 78 displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) 79 return allDisplays.toList() 80 } 81 82 data class FakeDisplayConfig( 83 /** 84 * The display width and height in pixels. When returned from the Display, it is adjusted 85 * based on the orientation. 86 */ 87 val displaySize: Point, 88 /** Whether the device is naturally portrait, used to determine the degree of rotation. */ 89 val naturallyPortrait: Boolean? = null, 90 /** 91 * The current orientation of the device. The Display adjusts its size accordingly, with x 92 * as the larger dimension if landscape, and y as the larger dimension if portrait. 93 */ 94 @Orientation val orientation: Int? = null, 95 /** 96 * The DPI of a device, used to calculate screen size. This value needs to be set in 97 * appContext.resources.configuration.densityDpi to take effect. 98 */ 99 val dpi: Int = 1, 100 ) 101 102 companion object { 103 // Common display sizes used for testing 104 val HANDHELD = FakeDisplayConfig(Point(1440, 3120), true, ORIENTATION_PORTRAIT, 560) 105 val FOLDABLE_FOLDED = FakeDisplayConfig(Point(1080, 2092), true, ORIENTATION_PORTRAIT, 408) 106 val FOLDABLE_UNFOLDED_LAND = 107 FakeDisplayConfig(Point(2208, 1840), false, ORIENTATION_LANDSCAPE, 380) 108 val FOLDABLE_UNFOLDED_PORT = 109 FakeDisplayConfig(Point(2208, 1840), false, ORIENTATION_PORTRAIT, 380) 110 val TABLET_LAND = FakeDisplayConfig(Point(2560, 1600), false, ORIENTATION_LANDSCAPE, 276) 111 val TABLET_PORT = FakeDisplayConfig(Point(2560, 1600), false, ORIENTATION_PORTRAIT, 276) 112 } 113 } 114