1 /*
2  * Copyright (C) 2020 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.sidecar;
18 
19 import static android.view.Display.INVALID_DISPLAY;
20 import static android.view.Surface.ROTATION_0;
21 import static android.view.Surface.ROTATION_180;
22 import static android.view.Surface.ROTATION_270;
23 import static android.view.Surface.ROTATION_90;
24 
25 import android.app.Activity;
26 import android.app.ActivityThread;
27 import android.graphics.Rect;
28 import android.hardware.display.DisplayManagerGlobal;
29 import android.os.IBinder;
30 import android.view.DisplayInfo;
31 import android.view.Surface;
32 
33 import androidx.annotation.Nullable;
34 
35 class SidecarHelper {
36     /**
37      * Rotate the input rectangle specified in default display orientation to the current display
38      * rotation.
39      */
rotateRectToDisplayRotation(Rect inOutRect, int displayId)40     static void rotateRectToDisplayRotation(Rect inOutRect, int displayId) {
41         DisplayManagerGlobal dmGlobal = DisplayManagerGlobal.getInstance();
42         DisplayInfo displayInfo = dmGlobal.getDisplayInfo(displayId);
43         int rotation = displayInfo.rotation;
44 
45         boolean isSideRotation = rotation == ROTATION_90 || rotation == ROTATION_270;
46         int displayWidth = isSideRotation ? displayInfo.logicalHeight : displayInfo.logicalWidth;
47         int displayHeight = isSideRotation ? displayInfo.logicalWidth : displayInfo.logicalHeight;
48 
49         inOutRect.intersect(0, 0, displayWidth, displayHeight);
50 
51         rotateBounds(inOutRect, displayWidth, displayHeight, rotation);
52     }
53 
54     /**
55      * Rotate the input rectangle within parent bounds for a given delta.
56      */
rotateBounds(Rect inOutRect, int parentWidth, int parentHeight, @Surface.Rotation int delta)57     private static void rotateBounds(Rect inOutRect, int parentWidth, int parentHeight,
58             @Surface.Rotation int delta) {
59         int origLeft = inOutRect.left;
60         switch (delta) {
61             case ROTATION_0:
62                 return;
63             case ROTATION_90:
64                 inOutRect.left = inOutRect.top;
65                 inOutRect.top = parentWidth - inOutRect.right;
66                 inOutRect.right = inOutRect.bottom;
67                 inOutRect.bottom = parentWidth - origLeft;
68                 return;
69             case ROTATION_180:
70                 inOutRect.left = parentWidth - inOutRect.right;
71                 inOutRect.right = parentWidth - origLeft;
72                 return;
73             case ROTATION_270:
74                 inOutRect.left = parentHeight - inOutRect.bottom;
75                 inOutRect.bottom = inOutRect.right;
76                 inOutRect.right = parentHeight - inOutRect.top;
77                 inOutRect.top = origLeft;
78                 return;
79         }
80     }
81 
82     /** Transform rectangle from absolute coordinate space to the window coordinate space. */
transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken)83     static void transformToWindowSpaceRect(Rect inOutRect, IBinder windowToken) {
84         Rect windowRect = getWindowBounds(windowToken);
85         if (windowRect == null) {
86             inOutRect.setEmpty();
87             return;
88         }
89         if (!Rect.intersects(inOutRect, windowRect)) {
90             inOutRect.setEmpty();
91             return;
92         }
93         inOutRect.intersect(windowRect);
94         inOutRect.offset(-windowRect.left, -windowRect.top);
95     }
96 
97     /**
98      * Get the current window bounds in absolute coordinates.
99      * NOTE: Only works with Activity windows.
100      */
101     @Nullable
getWindowBounds(IBinder windowToken)102     private static Rect getWindowBounds(IBinder windowToken) {
103         Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
104         return activity != null
105                 ? activity.getWindowManager().getCurrentWindowMetrics().getBounds()
106                 : null;
107     }
108 
109     /**
110      * Check if this window is an Activity window that is in multi-window mode.
111      */
isInMultiWindow(IBinder windowToken)112     static boolean isInMultiWindow(IBinder windowToken) {
113         Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
114         return activity != null && activity.isInMultiWindowMode();
115     }
116 
117     /**
118      * Get the id of the parent display for the window.
119      * NOTE: Only works with Activity windows.
120      */
getWindowDisplay(IBinder windowToken)121     static int getWindowDisplay(IBinder windowToken) {
122         Activity activity = ActivityThread.currentActivityThread().getActivity(windowToken);
123         return activity != null
124                 ? activity.getWindowManager().getDefaultDisplay().getDisplayId() : INVALID_DISPLAY;
125     }
126 }
127