1 /* 2 * Copyright (C) 2013 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.core.hardware.display; 18 19 import android.annotation.SuppressLint; 20 import android.content.Context; 21 import android.hardware.display.DisplayManager; 22 import android.os.Build; 23 import android.view.Display; 24 25 import androidx.annotation.RestrictTo; 26 import androidx.annotation.VisibleForTesting; 27 28 import org.jspecify.annotations.NonNull; 29 import org.jspecify.annotations.Nullable; 30 31 import java.util.Objects; 32 33 /** 34 * Helper for accessing features in {@link android.hardware.display.DisplayManager}. 35 */ 36 public final class DisplayManagerCompat { 37 38 /** 39 * An internal category to get all the displays. This was added in SC_V2 and should only be 40 * used internally. This is not a stable API so it should not be make public. 41 */ 42 @VisibleForTesting 43 @RestrictTo(RestrictTo.Scope.LIBRARY) 44 static final String DISPLAY_CATEGORY_ALL = 45 "android.hardware.display.category.ALL_INCLUDING_DISABLED"; 46 47 /** 48 * An internal copy of the type from the platform. This was added in SDK 17 and should only be 49 * used internally. This is not a stable API so it should not be made public. 50 */ 51 @VisibleForTesting 52 @RestrictTo(RestrictTo.Scope.LIBRARY) 53 static final int DISPLAY_TYPE_INTERNAL = 1; 54 55 /** 56 * Display category: Presentation displays. 57 * <p> 58 * This category can be used to identify secondary displays that are suitable for 59 * use as presentation displays. 60 * </p> 61 * 62 * @see android.app.Presentation for information about presenting content 63 * on secondary displays. 64 * @see #getDisplays(String) 65 */ 66 public static final String DISPLAY_CATEGORY_PRESENTATION = 67 "android.hardware.display.category.PRESENTATION"; 68 69 /** 70 * Display category: Built in displays. 71 * <p> 72 * This category can be used to identify displays that are built in to the device. 73 * </p> 74 * @see #getDisplays(String) 75 */ 76 @ExperimentalDisplayApi 77 public static final String DISPLAY_CATEGORY_BUILT_IN_DISPLAYS = 78 "android.hardware.display.category.BUILT_IN_DISPLAYS"; 79 80 private final Context mContext; 81 DisplayManagerCompat(Context context)82 private DisplayManagerCompat(Context context) { 83 mContext = context; 84 } 85 86 /** 87 * Gets an instance of the display manager given the context. 88 */ getInstance(@onNull Context context)89 public static @NonNull DisplayManagerCompat getInstance(@NonNull Context context) { 90 return new DisplayManagerCompat(context); 91 } 92 93 /** 94 * Gets information about a logical display. 95 * 96 * The display metrics may be adjusted to provide compatibility 97 * for legacy applications. 98 * 99 * @param displayId The logical display id. 100 * @return The display object, or null if there is no valid display with the given id. 101 */ getDisplay(int displayId)102 public @Nullable Display getDisplay(int displayId) { 103 DisplayManager displayManager = 104 (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); 105 return displayManager.getDisplay(displayId); 106 } 107 108 /** 109 * Gets all currently valid logical displays. 110 * 111 * @return An array containing all displays. 112 */ getDisplays()113 public Display @NonNull [] getDisplays() { 114 return ((DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE)).getDisplays(); 115 } 116 117 /** 118 * Gets all currently valid logical displays of the specified category. 119 * <p> 120 * When there are multiple displays in a category the returned displays are sorted 121 * of preference. For example, if the requested category is 122 * {@link #DISPLAY_CATEGORY_PRESENTATION} and there are multiple presentation displays 123 * then the displays are sorted so that the first display in the returned array 124 * is the most preferred presentation display. The application may simply 125 * use the first display or allow the user to choose. 126 * </p> 127 * 128 * @param category The requested display category or null to return all displays. 129 * @return An array containing all displays sorted by order of preference. 130 * 131 * @see #DISPLAY_CATEGORY_PRESENTATION 132 */ getDisplays(@ullable String category)133 public Display @NonNull [] getDisplays(@Nullable String category) { 134 DisplayManager displayManager = (DisplayManager) mContext 135 .getSystemService(Context.DISPLAY_SERVICE); 136 if (DISPLAY_CATEGORY_BUILT_IN_DISPLAYS.equals(category)) { 137 return computeBuiltInDisplays(displayManager); 138 } else { 139 return ((DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE)) 140 .getDisplays(category); 141 } 142 } 143 144 /** 145 * Returns an array of built in displays, a built in display is one that is physically part 146 * of the device. 147 */ computeBuiltInDisplays(DisplayManager displayManager)148 private static Display[] computeBuiltInDisplays(DisplayManager displayManager) { 149 final Display[] allDisplays; 150 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S_V2) { 151 allDisplays = displayManager 152 .getDisplays(DISPLAY_CATEGORY_ALL); 153 154 } else { 155 allDisplays = displayManager.getDisplays(); 156 } 157 final int numberOfBuiltInDisplays = 158 numberOfDisplaysByType(DISPLAY_TYPE_INTERNAL, allDisplays); 159 final Display[] builtInDisplays = new Display[numberOfBuiltInDisplays]; 160 161 int builtInDisplayIndex = 0; 162 for (int i = 0; i < allDisplays.length; i++) { 163 Display display = allDisplays[i]; 164 if (DISPLAY_TYPE_INTERNAL == getTypeCompat(display)) { 165 builtInDisplays[builtInDisplayIndex] = display; 166 builtInDisplayIndex = builtInDisplayIndex + 1; 167 } 168 } 169 return builtInDisplays; 170 } 171 172 /** 173 * Returns the number of displays that have the matching type. 174 */ numberOfDisplaysByType(int type, Display[] displays)175 private static int numberOfDisplaysByType(int type, Display[] displays) { 176 int count = 0; 177 for (int i = 0; i < displays.length; i++) { 178 Display display = displays[i]; 179 if (type == getTypeCompat(display)) { 180 count = count + 1; 181 } 182 } 183 return count; 184 } 185 186 /** 187 * An internal method to get the type of the display using reflection. This is used to support 188 * backporting of getting a display of a specific type. The preferred way to expose displays is 189 * to have a category and have developers get them using the category. 190 */ 191 @SuppressLint("BanUncheckedReflection") 192 @VisibleForTesting 193 @RestrictTo(RestrictTo.Scope.LIBRARY) getTypeCompat(@onNull Display display)194 static int getTypeCompat(@NonNull Display display) { 195 try { 196 return (Integer) Objects.requireNonNull( 197 Display.class.getMethod("getType").invoke(display) 198 ); 199 } catch (NoSuchMethodException noSuchMethodException) { 200 return 0; 201 } catch (Exception exception) { 202 throw new RuntimeException(exception); 203 } 204 } 205 } 206