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