• 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.Surface.ROTATION_270
25 import android.view.Surface.ROTATION_90
26 
27 /**
28  * Utility class to provide methods to find and obtain information about displays via {@link
29  * DisplayManager}
30  */
31 class DisplayUtils(private val context: Context) {
32     companion object {
33         private const val TAG = "DisplayUtils"
34         private val ROTATION_HORIZONTAL_HINGE = setOf(ROTATION_90, ROTATION_270)
35     }
36 
37     private val displayManager: DisplayManager by lazy {
38         context.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager
39     }
40 
41     fun hasMultiInternalDisplays(): Boolean {
42         return getInternalDisplays().size > 1
43     }
44 
45     /**
46      * Returns the internal {@link Display} with tthe largest area to be used to calculate wallpaper
47      * size and cropping.
48      */
49     fun getWallpaperDisplay(): Display {
50         val internalDisplays = getInternalDisplays()
51         return internalDisplays.maxWithOrNull { a, b -> getRealArea(a) - getRealArea(b) }
52             ?: internalDisplays[0]
53     }
54 
55     /**
56      * Checks if the device only has one display or unfolded screen in horizontal hinge orientation.
57      */
58     fun isSingleDisplayOrUnfoldedHorizontalHinge(activity: Activity): Boolean {
59         return !hasMultiInternalDisplays() || isUnfoldedHorizontalHinge(activity)
60     }
61 
62     /**
63      * Checks if the device is a foldable and it's unfolded and in horizontal hinge orientation
64      * (portrait).
65      */
66     fun isUnfoldedHorizontalHinge(activity: Activity): Boolean {
67         return activity.display.rotation in ROTATION_HORIZONTAL_HINGE &&
68             isOnWallpaperDisplay(activity) &&
69             hasMultiInternalDisplays()
70     }
71 
72     fun getMaxDisplaysDimension(): Point {
73         val dimen = Point()
74         getInternalDisplays().let { displays ->
75             dimen.x = displays.maxOf { getRealSize(it).x }
76             dimen.y = displays.maxOf { getRealSize(it).y }
77         }
78         return dimen
79     }
80 
81     /**
82      * Returns `true` if the current display is the wallpaper display on a multi-display device.
83      *
84      * On a multi-display device the wallpaper display is the largest display while on a single
85      * display device the only display is both the wallpaper display and the current display.
86      */
87     fun isOnWallpaperDisplay(activity: Activity): Boolean {
88         return activity.display.uniqueId == getWallpaperDisplay().uniqueId
89     }
90 
91     private fun getRealArea(display: Display): Int {
92         val p = Point()
93         display.getRealSize(p)
94         return p.x * p.y
95     }
96 
97     private fun getRealSize(display: Display): Point {
98         val p = Point()
99         display.getRealSize(p)
100         return p
101     }
102 
103     private fun getInternalDisplays(): List<Display> {
104         val allDisplays: Array<out Display> =
105             displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED)
106         if (allDisplays.isEmpty()) {
107             Log.e(TAG, "No displays found on context ${context.applicationContext}")
108             throw RuntimeException("No displays found!")
109         }
110         return allDisplays.filter { it.type == Display.TYPE_INTERNAL }
111     }
112 }
113