1 /* 2 * Copyright 2022 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 androidx.camera.camera2.pipe.integration.impl 18 19 import android.content.Context 20 import android.graphics.Point 21 import android.hardware.display.DisplayManager 22 import android.hardware.display.DisplayManager.DisplayListener 23 import android.os.Handler 24 import android.os.Looper 25 import android.util.Size 26 import android.view.Display 27 import androidx.camera.camera2.pipe.integration.compat.workaround.DisplaySizeCorrector 28 import androidx.camera.camera2.pipe.integration.compat.workaround.MaxPreviewSize 29 import androidx.camera.core.internal.utils.SizeUtil 30 import javax.inject.Inject 31 import javax.inject.Singleton 32 33 @Suppress("DEPRECATION") // getRealSize 34 @Singleton 35 public class DisplayInfoManager @Inject constructor(context: Context) { 36 private val maxPreviewSize = MaxPreviewSize() 37 private val displaySizeCorrector = DisplaySizeCorrector() 38 39 public companion object { 40 private val MAX_PREVIEW_SIZE = Size(1920, 1080) 41 /** This is the smallest size from a device which had issue reported to CameraX. */ 42 private val ABNORMAL_DISPLAY_SIZE_THRESHOLD: Size = Size(320, 240) 43 /** 44 * The fallback display size for the case that the retrieved display size is abnormally 45 * small and no correct display size can be retrieved from DisplaySizeCorrector. 46 */ 47 private val FALLBACK_DISPLAY_SIZE: Size = Size(640, 480) 48 private var lazyMaxDisplay: Display? = null 49 private var lazyPreviewSize: Size? = null 50 invalidateLazyFieldsnull51 internal fun invalidateLazyFields() { 52 lazyMaxDisplay = null 53 lazyPreviewSize = null 54 } 55 <lambda>null56 internal val displayListener by lazy { 57 object : DisplayListener { 58 override fun onDisplayAdded(displayId: Int) { 59 invalidateLazyFields() 60 } 61 62 override fun onDisplayRemoved(displayId: Int) { 63 invalidateLazyFields() 64 } 65 66 override fun onDisplayChanged(displayId: Int) { 67 invalidateLazyFields() 68 } 69 } 70 } 71 } 72 <lambda>null73 private val displayManager: DisplayManager by lazy { 74 (context.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager).also { 75 it.registerDisplayListener(displayListener, Handler(Looper.getMainLooper())) 76 } 77 } 78 79 public val defaultDisplay: Display 80 get() = getMaxSizeDisplay() 81 82 private var previewSize: Size? = null 83 84 /** Update the preview size according to current display size. */ refreshnull85 public fun refresh() { 86 previewSize = calculatePreviewSize() 87 } 88 89 /** 90 * PREVIEW refers to the best size match to the device's screen resolution, or to 1080p 91 * (1920x1080), whichever is smaller. 92 */ getPreviewSizenull93 public fun getPreviewSize(): Size { 94 // Use cached value to speed up since this would be called multiple times. 95 if (previewSize != null) { 96 return previewSize as Size 97 } 98 previewSize = calculatePreviewSize() 99 return previewSize as Size 100 } 101 getMaxSizeDisplaynull102 private fun getMaxSizeDisplay(): Display { 103 lazyMaxDisplay?.let { 104 return it 105 } 106 107 val displays = displayManager.displays 108 109 var maxDisplayWhenStateNotOff: Display? = null 110 var maxDisplaySizeWhenStateNotOff = -1 111 112 var maxDisplay: Display? = null 113 var maxDisplaySize = -1 114 115 for (display: Display in displays) { 116 val displaySize = Point() 117 // TODO(b/230400472): Use WindowManager#getCurrentWindowMetrics(). Display#getRealSize() 118 // is deprecated since API level 31. 119 display.getRealSize(displaySize) 120 121 if (displaySize.x * displaySize.y > maxDisplaySize) { 122 maxDisplaySize = displaySize.x * displaySize.y 123 maxDisplay = display 124 } 125 if (display.state != Display.STATE_OFF) { 126 if (displaySize.x * displaySize.y > maxDisplaySizeWhenStateNotOff) { 127 maxDisplaySizeWhenStateNotOff = displaySize.x * displaySize.y 128 maxDisplayWhenStateNotOff = display 129 } 130 } 131 } 132 133 lazyMaxDisplay = maxDisplayWhenStateNotOff ?: maxDisplay 134 135 return checkNotNull(lazyMaxDisplay) { "No displays found from ${displayManager.displays}!" } 136 } 137 138 /** Calculates the device's screen resolution, or MAX_PREVIEW_SIZE, whichever is smaller. */ calculatePreviewSizenull139 private fun calculatePreviewSize(): Size { 140 lazyPreviewSize?.let { 141 return it 142 } 143 144 var displayViewSize = getCorrectedDisplaySize() 145 if (SizeUtil.isSmallerByArea(MAX_PREVIEW_SIZE, displayViewSize)) { 146 displayViewSize = MAX_PREVIEW_SIZE 147 } 148 return maxPreviewSize.getMaxPreviewResolution(displayViewSize).also { lazyPreviewSize = it } 149 } 150 getCorrectedDisplaySizenull151 private fun getCorrectedDisplaySize(): Size { 152 val displaySize = Point() 153 defaultDisplay.getRealSize(displaySize) 154 var displayViewSize = Size(displaySize.x, displaySize.y) 155 156 // Checks whether the display size is abnormally small. 157 if (SizeUtil.isSmallerByArea(displayViewSize, ABNORMAL_DISPLAY_SIZE_THRESHOLD)) { 158 // Gets the display size from DisplaySizeCorrector if the display size retrieved from 159 // DisplayManager is abnormally small. Falls back the display size to 640x480 if 160 // DisplaySizeCorrector doesn't contain the device's display size info. 161 displayViewSize = displaySizeCorrector.displaySize ?: FALLBACK_DISPLAY_SIZE 162 } 163 164 // Flips the size to landscape orientation 165 if (displayViewSize.height > displayViewSize.width) { 166 displayViewSize = 167 Size(/* width= */ displayViewSize.height, /* height= */ displayViewSize.width) 168 } 169 170 return displayViewSize 171 } 172 } 173