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