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 com.android.wm.shell.transition;
18 
19 import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
20 
21 import android.graphics.Rect;
22 import android.util.ArrayMap;
23 import android.util.RotationUtils;
24 import android.view.SurfaceControl;
25 import android.window.TransitionInfo;
26 import android.window.WindowContainerToken;
27 
28 import androidx.annotation.NonNull;
29 
30 import com.android.wm.shell.shared.CounterRotator;
31 import com.android.wm.shell.shared.TransitionUtil;
32 
33 import java.util.List;
34 
35 /**
36  * The helper class that performs counter-rotate for all "going-away" window containers if they are
37  * still in the old rotation in a transition.
38  */
39 public class CounterRotatorHelper {
40     private final ArrayMap<WindowContainerToken, CounterRotator> mRotatorMap = new ArrayMap<>();
41     private final Rect mLastDisplayBounds = new Rect();
42     private int mLastRotationDelta;
43 
44     /** Puts the surface controls of closing changes to counter-rotated surfaces. */
handleClosingChanges(@onNull TransitionInfo info, @NonNull SurfaceControl.Transaction startTransaction, @NonNull TransitionInfo.Change displayRotationChange)45     public void handleClosingChanges(@NonNull TransitionInfo info,
46             @NonNull SurfaceControl.Transaction startTransaction,
47             @NonNull TransitionInfo.Change displayRotationChange) {
48         final int rotationDelta = RotationUtils.deltaRotation(
49                 displayRotationChange.getStartRotation(), displayRotationChange.getEndRotation());
50         final Rect displayBounds = displayRotationChange.getEndAbsBounds();
51         final int displayW = displayBounds.width();
52         final int displayH = displayBounds.height();
53         mLastRotationDelta = rotationDelta;
54         mLastDisplayBounds.set(displayBounds);
55 
56         final List<TransitionInfo.Change> changes = info.getChanges();
57         final int numChanges = changes.size();
58         for (int i = numChanges - 1; i >= 0; --i) {
59             final TransitionInfo.Change change = changes.get(i);
60             final WindowContainerToken parent = change.getParent();
61             if (!TransitionUtil.isClosingType(change.getMode())
62                     || !TransitionInfo.isIndependent(change, info) || parent == null) {
63                 continue;
64             }
65 
66             CounterRotator crot = mRotatorMap.get(parent);
67             if (crot == null) {
68                 crot = new CounterRotator();
69                 crot.setup(startTransaction, info.getChange(parent).getLeash(), rotationDelta,
70                         displayW, displayH);
71                 final SurfaceControl rotatorSc = crot.getSurface();
72                 if (rotatorSc != null) {
73                     // Wallpaper should be placed at the bottom.
74                     final int layer = (change.getFlags() & FLAG_IS_WALLPAPER) == 0
75                             ? numChanges - i
76                             : -1;
77                     startTransaction.setLayer(rotatorSc, layer);
78                 }
79                 mRotatorMap.put(parent, crot);
80             }
81             crot.addChild(startTransaction, change.getLeash());
82         }
83     }
84 
85     /**
86      * Returns the rotated end bounds if the change is put in previous rotation. Otherwise the
87      * original end bounds are returned.
88      */
89     @NonNull
getEndBoundsInStartRotation(@onNull TransitionInfo.Change change)90     public Rect getEndBoundsInStartRotation(@NonNull TransitionInfo.Change change) {
91         if (mLastRotationDelta == 0) return change.getEndAbsBounds();
92         final Rect rotatedBounds = new Rect(change.getEndAbsBounds());
93         RotationUtils.rotateBounds(rotatedBounds, mLastDisplayBounds, mLastRotationDelta);
94         return rotatedBounds;
95     }
96 
97     /**
98      * Removes the counter rotation surface in the finish transaction. No need to reparent the
99      * children as the finish transaction should have already taken care of that.
100      *
101      * This can only be called after startTransaction for {@link #handleClosingChanges} is applied.
102      */
cleanUp(@onNull SurfaceControl.Transaction finishTransaction)103     public void cleanUp(@NonNull SurfaceControl.Transaction finishTransaction) {
104         for (int i = mRotatorMap.size() - 1; i >= 0; --i) {
105             mRotatorMap.valueAt(i).cleanUp(finishTransaction);
106         }
107         mRotatorMap.clear();
108         mLastRotationDelta = 0;
109     }
110 }
111