1 /*
2  * Copyright (C) 2021 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 androidx.window.util;
18 
19 import static android.view.Surface.ROTATION_270;
20 import static android.view.Surface.ROTATION_90;
21 
22 import android.annotation.SuppressLint;
23 import android.app.WindowConfiguration;
24 import android.content.Context;
25 import android.graphics.Rect;
26 import android.hardware.display.DisplayManagerGlobal;
27 import android.util.RotationUtils;
28 import android.view.DisplayInfo;
29 import android.view.Surface;
30 import android.view.WindowManager;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.UiContext;
34 import androidx.annotation.VisibleForTesting;
35 
36 /**
37  * Util class for both Sidecar and Extensions.
38  */
39 public final class ExtensionHelper {
40 
ExtensionHelper()41     private ExtensionHelper() {
42         // Util class, no instances should be created.
43     }
44 
45     /**
46      * Rotates the input rectangle specified in default display orientation to the current display
47      * rotation.
48      *
49      * @param displayId the display id.
50      * @param rotation the target rotation relative to the default display orientation.
51      * @param inOutRect the input/output Rect as specified in the default display orientation.
52      */
rotateRectToDisplayRotation( int displayId, @Surface.Rotation int rotation, @NonNull Rect inOutRect)53     public static void rotateRectToDisplayRotation(
54             int displayId, @Surface.Rotation int rotation, @NonNull Rect inOutRect) {
55         final DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
56         final DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
57 
58         rotateRectToDisplayRotation(displayInfo, rotation, inOutRect);
59     }
60 
61     // We suppress the Lint error CheckResult for Rect#intersect because in case the displayInfo and
62     // folding features are out of sync, e.g. when a foldable devices is unfolding, it is acceptable
63     // to provide the original folding feature Rect even if they don't intersect.
64     @SuppressLint("RectIntersectReturnValueIgnored")
65     @VisibleForTesting
rotateRectToDisplayRotation(@onNull DisplayInfo displayInfo, @Surface.Rotation int rotation, @NonNull Rect inOutRect)66     static void rotateRectToDisplayRotation(@NonNull DisplayInfo displayInfo,
67             @Surface.Rotation int rotation, @NonNull Rect inOutRect) {
68         // The inOutRect is specified in the default display orientation, so here we need to get
69         // the display width and height in the default orientation to perform the intersection and
70         // rotation.
71         final boolean isSideRotation =
72                 displayInfo.rotation == ROTATION_90 || displayInfo.rotation == ROTATION_270;
73         final int baseDisplayWidth =
74                 isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth;
75         final int baseDisplayHeight =
76                 isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight;
77 
78         inOutRect.intersect(0, 0, baseDisplayWidth, baseDisplayHeight);
79 
80         RotationUtils.rotateBounds(inOutRect, baseDisplayWidth, baseDisplayHeight, rotation);
81     }
82 
83     /** Transforms rectangle from absolute coordinate space to the window coordinate space. */
transformToWindowSpaceRect(@onNull @iContext Context context, Rect inOutRect)84     public static void transformToWindowSpaceRect(@NonNull @UiContext Context context,
85             Rect inOutRect) {
86         transformToWindowSpaceRect(getWindowBounds(context), inOutRect);
87     }
88 
89     /** @see ExtensionHelper#transformToWindowSpaceRect(Context, Rect) */
transformToWindowSpaceRect(@onNull WindowConfiguration windowConfiguration, Rect inOutRect)90     public static void transformToWindowSpaceRect(@NonNull WindowConfiguration windowConfiguration,
91             Rect inOutRect) {
92         transformToWindowSpaceRect(windowConfiguration.getBounds(), inOutRect);
93     }
94 
transformToWindowSpaceRect(@onNull Rect bounds, @NonNull Rect inOutRect)95     private static void transformToWindowSpaceRect(@NonNull Rect bounds, @NonNull Rect inOutRect) {
96         if (!inOutRect.intersect(bounds)) {
97             inOutRect.setEmpty();
98             return;
99         }
100         inOutRect.offset(-bounds.left, -bounds.top);
101     }
102 
103     /**
104      * Gets the current window bounds in absolute coordinates.
105      */
106     @NonNull
getWindowBounds(@onNull @iContext Context context)107     private static Rect getWindowBounds(@NonNull @UiContext Context context) {
108         return context.getSystemService(WindowManager.class).getCurrentWindowMetrics().getBounds();
109     }
110 
111     /**
112      * Checks if both dimensions of the given rect are zero at the same time.
113      */
isZero(@onNull Rect rect)114     public static boolean isZero(@NonNull Rect rect) {
115         return rect.height() == 0 && rect.width() == 0;
116     }
117 }
118