1 /* 2 * Copyright (C) 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 android.window; 18 19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 20 import static android.view.Surface.ROTATION_0; 21 import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; 22 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; 23 24 import android.annotation.NonNull; 25 import android.app.ResourcesManager; 26 import android.app.WindowConfiguration; 27 import android.content.Context; 28 import android.content.res.CompatibilityInfo; 29 import android.content.res.Configuration; 30 import android.graphics.Rect; 31 import android.os.IBinder; 32 import android.os.RemoteException; 33 import android.util.DisplayMetrics; 34 import android.view.Display; 35 import android.view.DisplayCutout; 36 import android.view.DisplayInfo; 37 import android.view.InsetsState; 38 import android.view.WindowInsets; 39 import android.view.WindowManager; 40 import android.view.WindowManagerGlobal; 41 import android.view.WindowMetrics; 42 43 import java.util.HashSet; 44 import java.util.List; 45 import java.util.Set; 46 import java.util.function.Supplier; 47 48 /** 49 * A controller to handle {@link android.view.WindowMetrics} related APIs, which are 50 * <ol> 51 * <li>{@link WindowManager#getCurrentWindowMetrics()}</li> 52 * <li>{@link WindowManager#getMaximumWindowMetrics()}</li> 53 * <li>{@link WindowManager#getPossibleMaximumWindowMetrics(int)}</li> 54 * </ol> 55 * 56 * @hide 57 */ 58 public final class WindowMetricsController { 59 private final Context mContext; 60 WindowMetricsController(@onNull Context context)61 public WindowMetricsController(@NonNull Context context) { 62 mContext = context; 63 } 64 65 /** @see WindowManager#getCurrentWindowMetrics() */ getCurrentWindowMetrics()66 public WindowMetrics getCurrentWindowMetrics() { 67 return getWindowMetricsInternal(false /* isMaximum */); 68 } 69 70 /** @see WindowManager#getMaximumWindowMetrics() */ getMaximumWindowMetrics()71 public WindowMetrics getMaximumWindowMetrics() { 72 return getWindowMetricsInternal(true /* isMaximum */); 73 } 74 75 /** 76 * The core implementation to obtain {@link WindowMetrics} 77 * 78 * @param isMaximum {@code true} to obtain {@link WindowManager#getCurrentWindowMetrics()}. 79 * {@code false} to obtain {@link WindowManager#getMaximumWindowMetrics()}. 80 */ getWindowMetricsInternal(boolean isMaximum)81 private WindowMetrics getWindowMetricsInternal(boolean isMaximum) { 82 final Rect bounds; 83 final float density; 84 final boolean isScreenRound; 85 final int activityType; 86 synchronized (ResourcesManager.getInstance()) { 87 final Configuration config = mContext.getResources().getConfiguration(); 88 final WindowConfiguration winConfig = config.windowConfiguration; 89 bounds = (isMaximum) ? winConfig.getMaxBounds() : winConfig.getBounds(); 90 // Multiply default density scale because WindowMetrics provide the density value with 91 // the scaling factor for the Density Independent Pixel unit, which is the same unit 92 // as DisplayMetrics#density 93 density = config.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; 94 isScreenRound = config.isScreenRound(); 95 activityType = winConfig.getActivityType(); 96 } 97 final IBinder token = Context.getToken(mContext); 98 final Supplier<WindowInsets> insetsSupplier = () -> getWindowInsetsFromServerForDisplay( 99 mContext.getDisplayId(), token, bounds, isScreenRound, activityType); 100 return new WindowMetrics(new Rect(bounds), insetsSupplier, density); 101 } 102 103 /** 104 * Retrieves WindowInsets for the given context and display, given the window bounds. 105 * 106 * @param displayId the ID of the logical display to calculate insets for 107 * @param token the token of Activity or WindowContext 108 * @param bounds the window bounds to calculate insets for 109 * @param isScreenRound if the display identified by displayId is round 110 * @param activityType the activity type of the window to calculate insets for 111 * @return WindowInsets calculated for the given window bounds, on the given display 112 */ getWindowInsetsFromServerForDisplay(int displayId, IBinder token, Rect bounds, boolean isScreenRound, int activityType)113 private static WindowInsets getWindowInsetsFromServerForDisplay(int displayId, IBinder token, 114 Rect bounds, boolean isScreenRound, int activityType) { 115 try { 116 final InsetsState insetsState = new InsetsState(); 117 WindowManagerGlobal.getWindowManagerService().getWindowInsets( 118 displayId, token, insetsState); 119 final float overrideInvScale = CompatibilityInfo.getOverrideInvertedScale(); 120 if (overrideInvScale != 1f) { 121 insetsState.scale(overrideInvScale); 122 } 123 return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState */, 124 isScreenRound, SOFT_INPUT_ADJUST_NOTHING, 0 /* flags */, SYSTEM_UI_FLAG_VISIBLE, 125 WindowManager.LayoutParams.INVALID_WINDOW_TYPE, activityType, 126 null /* idSideMap */); 127 } catch (RemoteException e) { 128 throw e.rethrowFromSystemServer(); 129 } 130 } 131 132 /** @see WindowManager#getPossibleMaximumWindowMetrics(int) */ 133 @NonNull getPossibleMaximumWindowMetrics(int displayId)134 public Set<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) { 135 List<DisplayInfo> possibleDisplayInfos; 136 try { 137 possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService() 138 .getPossibleDisplayInfo(displayId); 139 } catch (RemoteException e) { 140 throw e.rethrowFromSystemServer(); 141 } 142 143 Set<WindowMetrics> maxMetrics = new HashSet<>(); 144 WindowInsets windowInsets; 145 DisplayInfo currentDisplayInfo; 146 for (int i = 0; i < possibleDisplayInfos.size(); i++) { 147 currentDisplayInfo = possibleDisplayInfos.get(i); 148 149 // Calculate max bounds for natural rotation and state. 150 Rect maxBounds = new Rect(0, 0, currentDisplayInfo.getNaturalWidth(), 151 currentDisplayInfo.getNaturalHeight()); 152 153 // Calculate insets for the natural max bounds. 154 final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0; 155 // Initialize insets based on Surface.ROTATION_0. Note any window-provided insets 156 // will not be set. 157 windowInsets = getWindowInsetsFromServerForDisplay( 158 currentDisplayInfo.displayId, null /* token */, 159 new Rect(0, 0, currentDisplayInfo.getNaturalWidth(), 160 currentDisplayInfo.getNaturalHeight()), isScreenRound, 161 ACTIVITY_TYPE_UNDEFINED); 162 // Set the hardware-provided insets. Always with the ROTATION_0 result. 163 DisplayCutout cutout = currentDisplayInfo.displayCutout; 164 if (cutout != null && currentDisplayInfo.rotation != ROTATION_0) { 165 cutout = cutout.getRotated( 166 currentDisplayInfo.logicalWidth, currentDisplayInfo.logicalHeight, 167 currentDisplayInfo.rotation, ROTATION_0); 168 } 169 windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners( 170 currentDisplayInfo.roundedCorners).setDisplayCutout(cutout).build(); 171 172 // Multiply default density scale because WindowMetrics provide the density value with 173 // the scaling factor for the Density Independent Pixel unit, which is the same unit 174 // as DisplayMetrics#density 175 final float density = currentDisplayInfo.logicalDensityDpi 176 * DisplayMetrics.DENSITY_DEFAULT_SCALE; 177 maxMetrics.add(new WindowMetrics(maxBounds, windowInsets, density)); 178 } 179 return maxMetrics; 180 } 181 } 182