/* * Copyright (C) 2022 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.window; import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; import static android.view.Surface.ROTATION_0; import static android.view.View.SYSTEM_UI_FLAG_VISIBLE; import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; import android.annotation.NonNull; import android.app.ResourcesManager; import android.app.WindowConfiguration; import android.content.Context; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.IBinder; import android.os.RemoteException; import android.util.DisplayMetrics; import android.view.Display; import android.view.DisplayCutout; import android.view.DisplayInfo; import android.view.InsetsState; import android.view.WindowInsets; import android.view.WindowManager; import android.view.WindowManagerGlobal; import android.view.WindowMetrics; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.function.Supplier; /** * A controller to handle {@link android.view.WindowMetrics} related APIs, which are *
    *
  1. {@link WindowManager#getCurrentWindowMetrics()}
  2. *
  3. {@link WindowManager#getMaximumWindowMetrics()}
  4. *
  5. {@link WindowManager#getPossibleMaximumWindowMetrics(int)}
  6. *
* * @hide */ public final class WindowMetricsController { private final Context mContext; public WindowMetricsController(@NonNull Context context) { mContext = context; } /** @see WindowManager#getCurrentWindowMetrics() */ public WindowMetrics getCurrentWindowMetrics() { return getWindowMetricsInternal(false /* isMaximum */); } /** @see WindowManager#getMaximumWindowMetrics() */ public WindowMetrics getMaximumWindowMetrics() { return getWindowMetricsInternal(true /* isMaximum */); } /** * The core implementation to obtain {@link WindowMetrics} * * @param isMaximum {@code true} to obtain {@link WindowManager#getCurrentWindowMetrics()}. * {@code false} to obtain {@link WindowManager#getMaximumWindowMetrics()}. */ private WindowMetrics getWindowMetricsInternal(boolean isMaximum) { final Rect bounds; final float density; final boolean isScreenRound; final int activityType; synchronized (ResourcesManager.getInstance()) { final Configuration config = mContext.getResources().getConfiguration(); final WindowConfiguration winConfig = config.windowConfiguration; bounds = (isMaximum) ? winConfig.getMaxBounds() : winConfig.getBounds(); // Multiply default density scale because WindowMetrics provide the density value with // the scaling factor for the Density Independent Pixel unit, which is the same unit // as DisplayMetrics#density density = config.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; isScreenRound = config.isScreenRound(); activityType = winConfig.getActivityType(); } final IBinder token = Context.getToken(mContext); final Supplier insetsSupplier = () -> getWindowInsetsFromServerForDisplay( mContext.getDisplayId(), token, bounds, isScreenRound, activityType); return new WindowMetrics(new Rect(bounds), insetsSupplier, density); } /** * Retrieves WindowInsets for the given context and display, given the window bounds. * * @param displayId the ID of the logical display to calculate insets for * @param token the token of Activity or WindowContext * @param bounds the window bounds to calculate insets for * @param isScreenRound if the display identified by displayId is round * @param activityType the activity type of the window to calculate insets for * @return WindowInsets calculated for the given window bounds, on the given display */ private static WindowInsets getWindowInsetsFromServerForDisplay(int displayId, IBinder token, Rect bounds, boolean isScreenRound, int activityType) { try { final InsetsState insetsState = new InsetsState(); WindowManagerGlobal.getWindowManagerService().getWindowInsets( displayId, token, insetsState); final float overrideInvScale = CompatibilityInfo.getOverrideInvertedScale(); if (overrideInvScale != 1f) { insetsState.scale(overrideInvScale); } return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState */, isScreenRound, SOFT_INPUT_ADJUST_NOTHING, 0 /* flags */, SYSTEM_UI_FLAG_VISIBLE, WindowManager.LayoutParams.INVALID_WINDOW_TYPE, activityType, null /* idSideMap */); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** @see WindowManager#getPossibleMaximumWindowMetrics(int) */ @NonNull public Set getPossibleMaximumWindowMetrics(int displayId) { List possibleDisplayInfos; try { possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService() .getPossibleDisplayInfo(displayId); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Set maxMetrics = new HashSet<>(); WindowInsets windowInsets; DisplayInfo currentDisplayInfo; for (int i = 0; i < possibleDisplayInfos.size(); i++) { currentDisplayInfo = possibleDisplayInfos.get(i); // Calculate max bounds for natural rotation and state. Rect maxBounds = new Rect(0, 0, currentDisplayInfo.getNaturalWidth(), currentDisplayInfo.getNaturalHeight()); // Calculate insets for the natural max bounds. final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0; // Initialize insets based on Surface.ROTATION_0. Note any window-provided insets // will not be set. windowInsets = getWindowInsetsFromServerForDisplay( currentDisplayInfo.displayId, null /* token */, new Rect(0, 0, currentDisplayInfo.getNaturalWidth(), currentDisplayInfo.getNaturalHeight()), isScreenRound, ACTIVITY_TYPE_UNDEFINED); // Set the hardware-provided insets. Always with the ROTATION_0 result. DisplayCutout cutout = currentDisplayInfo.displayCutout; if (cutout != null && currentDisplayInfo.rotation != ROTATION_0) { cutout = cutout.getRotated( currentDisplayInfo.logicalWidth, currentDisplayInfo.logicalHeight, currentDisplayInfo.rotation, ROTATION_0); } windowInsets = new WindowInsets.Builder(windowInsets).setRoundedCorners( currentDisplayInfo.roundedCorners).setDisplayCutout(cutout).build(); // Multiply default density scale because WindowMetrics provide the density value with // the scaling factor for the Density Independent Pixel unit, which is the same unit // as DisplayMetrics#density final float density = currentDisplayInfo.logicalDensityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE; maxMetrics.add(new WindowMetrics(maxBounds, windowInsets, density)); } return maxMetrics; } }