• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2021 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.util
17 
18 import android.app.Activity
19 import android.content.Context
20 import android.graphics.Point
21 import android.hardware.display.DisplayManager
22 import android.util.Log
23 import android.view.Display
24 import android.view.DisplayInfo
25 import android.view.Surface.ROTATION_270
26 import android.view.Surface.ROTATION_90
27 import com.android.systemui.shared.recents.utilities.Utilities
28 import kotlin.math.min
29 
30 /**
31  * Utility class to provide methods to find and obtain information about displays via {@link
32  * DisplayManager}
33  */
34 class DisplayUtils(private val context: Context) {
35     companion object {
36         private const val TAG = "DisplayUtils"
37         private val ROTATION_HORIZONTAL_HINGE = setOf(ROTATION_90, ROTATION_270)
38         private const val TABLET_MIN_DPS = 600f // See Sysui's Utilities.TABLET_MIN_DPS
39     }
40 
41     private val displayManager: DisplayManager by lazy {
42         context.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
43     }
44 
45     fun hasMultiInternalDisplays(): Boolean {
46         return getInternalDisplays().size > 1
47     }
48 
49     /**
50      * Returns the internal {@link Display} with the largest area to be used to calculate wallpaper
51      * size and cropping.
52      */
53     fun getWallpaperDisplay(): Display {
54         val internalDisplays = getInternalDisplays()
55         return internalDisplays.maxWithOrNull { a, b -> getRealArea(a) - getRealArea(b) }
56             ?: internalDisplays[0]
57     }
58 
59     /**
60      * Checks if the device only has one display or unfolded screen in horizontal hinge orientation.
61      */
62     fun isSingleDisplayOrUnfoldedHorizontalHinge(activity: Activity): Boolean {
63         return !hasMultiInternalDisplays() || isUnfoldedHorizontalHinge(activity)
64     }
65 
66     /**
67      * Checks if the device is a foldable and it's unfolded and in horizontal hinge orientation
68      * (portrait).
69      */
70     fun isUnfoldedHorizontalHinge(activity: Activity): Boolean {
71         return activity.display.rotation in ROTATION_HORIZONTAL_HINGE &&
72             isOnWallpaperDisplay(activity) &&
73             hasMultiInternalDisplays()
74     }
75 
76     fun getMaxDisplaysDimension(): Point {
77         val dimen = Point()
78         getInternalDisplays().let { displays ->
79             dimen.x = displays.maxOf { getRealSize(it).x }
80             dimen.y = displays.maxOf { getRealSize(it).y }
81         }
82         return dimen
83     }
84 
85     /**
86      * Returns true if this device's screen (or largest screen in case of multiple screen devices)
87      * is considered a "Large screen"
88      */
89     fun isLargeScreenDevice(): Boolean {
90         // We need to use MaxDisplay's dimensions because if we're in embedded mode, our window
91         // will only be the size of the embedded Activity.
92         val maxDisplaysDimension = getRealSize(getWallpaperDisplay())
93         val smallestWidth = min(maxDisplaysDimension.x, maxDisplaysDimension.y)
94         return Utilities.dpiFromPx(
95             smallestWidth.toFloat(),
96             context.resources.configuration.densityDpi
97         ) >= TABLET_MIN_DPS
98     }
99 
100     /**
101      * Returns `true` if the current display is the wallpaper display on a multi-display device.
102      *
103      * On a multi-display device the wallpaper display is the largest display while on a single
104      * display device the only display is both the wallpaper display and the current display.
105      *
106      * For single display device, this is always true.
107      */
108     fun isOnWallpaperDisplay(activity: Activity): Boolean {
109         return activity.display.uniqueId == getWallpaperDisplay().uniqueId
110     }
111 
112     private fun getRealArea(display: Display): Int {
113         val displayInfo = DisplayInfo()
114         display.getDisplayInfo(displayInfo)
115         return displayInfo.logicalHeight * displayInfo.logicalWidth
116     }
117 
118     private fun getRealSize(display: Display): Point {
119         val displayInfo = DisplayInfo()
120         display.getDisplayInfo(displayInfo)
121         return Point(displayInfo.logicalWidth, displayInfo.logicalHeight)
122     }
123 
124     private fun getInternalDisplays(): List<Display> {
125         val allDisplays: Array<out Display> =
126             displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
127         if (allDisplays.isEmpty()) {
128             Log.e(TAG, "No displays found on context ${context.applicationContext}")
129             throw RuntimeException("No displays found!")
130         }
131         return allDisplays.filter { it.type == Display.TYPE_INTERNAL }
132     }
133 }
134