182 lines
8.1 KiB
Java
182 lines
8.1 KiB
Java
/*
|
|
* 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
|
|
* <ol>
|
|
* <li>{@link WindowManager#getCurrentWindowMetrics()}</li>
|
|
* <li>{@link WindowManager#getMaximumWindowMetrics()}</li>
|
|
* <li>{@link WindowManager#getPossibleMaximumWindowMetrics(int)}</li>
|
|
* </ol>
|
|
*
|
|
* @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<WindowInsets> 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<WindowMetrics> getPossibleMaximumWindowMetrics(int displayId) {
|
|
List<DisplayInfo> possibleDisplayInfos;
|
|
try {
|
|
possibleDisplayInfos = WindowManagerGlobal.getWindowManagerService()
|
|
.getPossibleDisplayInfo(displayId);
|
|
} catch (RemoteException e) {
|
|
throw e.rethrowFromSystemServer();
|
|
}
|
|
|
|
Set<WindowMetrics> 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;
|
|
}
|
|
}
|