• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.common.split;
18 
19 import static android.view.WindowManager.DOCKED_BOTTOM;
20 import static android.view.WindowManager.DOCKED_INVALID;
21 import static android.view.WindowManager.DOCKED_LEFT;
22 import static android.view.WindowManager.DOCKED_RIGHT;
23 import static android.view.WindowManager.DOCKED_TOP;
24 
25 import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_ALIGN_CENTER;
26 import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_DISMISSING;
27 import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_FLEX;
28 import static com.android.wm.shell.common.split.SplitLayout.PARALLAX_NONE;
29 
30 import android.graphics.Point;
31 import android.graphics.Rect;
32 import android.view.SurfaceControl;
33 
34 /**
35  * This class governs how and when parallax and dimming effects are applied to task surfaces,
36  * usually when the divider is being moved around by the user (or during an animation).
37  */
38 class ResizingEffectPolicy {
39     /** The default amount to dim an app that is partially offscreen. */
40     public static float DEFAULT_OFFSCREEN_DIM = 0.32f;
41 
42     private final SplitLayout mSplitLayout;
43     /** The parallax algorithm we are currently using. */
44     private final int mParallaxType;
45     /**
46      * A convenience class, corresponding to {@link #mParallaxType}, that performs all the
47      * calculations for parallax and dimming values.
48      */
49     private final ParallaxSpec mParallaxSpec;
50 
51     int mShrinkSide = DOCKED_INVALID;
52 
53     // The current dismissing side.
54     int mDimmingSide = DOCKED_INVALID;
55 
56     /**
57      * A {@link Point} that stores a single x and y value, representing the parallax translation
58      * we use on the app that the divider is moving toward. The app is either shrinking in size or
59      * getting pushed off the screen.
60      */
61     final Point mRetreatingSideParallax = new Point();
62     /**
63      * A {@link Point} that stores a single x and y value, representing the parallax translation
64      * we use on the app that the divider is moving away from. The app is either growing in size or
65      * getting pulled onto the screen.
66      */
67     final Point mAdvancingSideParallax = new Point();
68 
69     // The dimming value to hint the dismissing side and progress.
70     float mDimValue = 0.0f;
71 
72     /**
73      * Content bounds for the app that the divider is moving toward. This is the content that is
74      * currently drawn at the start of the divider movement. It stays unchanged throughout the
75      * divider's movement.
76      */
77     final Rect mRetreatingContent = new Rect();
78     /**
79      * Surface bounds for the app that the divider is moving toward. This is the "canvas" on
80      * which an app could potentially be drawn. It changes on every frame as the divider moves
81      * around.
82      */
83     final Rect mRetreatingSurface = new Rect();
84     /**
85      * Content bounds for the app that the divider is moving away from. This is the content that
86      * is currently drawn at the start of the divider movement. It stays unchanged throughout
87      * the divider's movement.
88      */
89     final Rect mAdvancingContent = new Rect();
90     /**
91      * Surface bounds for the app that the divider is moving away from. This is the "canvas" on
92      * which an app could potentially be drawn. It changes on every frame as the divider moves
93      * around.
94      */
95     final Rect mAdvancingSurface = new Rect();
96 
97     final Rect mTempRect = new Rect();
98     final Rect mTempRect2 = new Rect();
99 
ResizingEffectPolicy(int parallaxType, SplitLayout splitLayout)100     ResizingEffectPolicy(int parallaxType, SplitLayout splitLayout) {
101         mParallaxType = parallaxType;
102         mSplitLayout = splitLayout;
103         switch (mParallaxType) {
104             case PARALLAX_DISMISSING:
105                 mParallaxSpec = new DismissingParallaxSpec();
106                 break;
107             case PARALLAX_ALIGN_CENTER:
108                 mParallaxSpec = new CenterParallaxSpec();
109                 break;
110             case PARALLAX_FLEX:
111                 mParallaxSpec = new FlexParallaxSpec();
112                 break;
113             case PARALLAX_NONE:
114             default:
115                 mParallaxSpec = new NoParallaxSpec();
116                 break;
117         }
118     }
119 
120     /**
121      * Calculates the desired parallax and dimming values for a task surface and stores them in
122      * {@link #mRetreatingSideParallax}, {@link #mAdvancingSideParallax}, and
123      * {@link #mDimValue} These values will be then be applied in
124      * {@link #adjustRootSurface} and {@link #adjustDimSurface} respectively.
125      */
applyDividerPosition( int position, boolean isLeftRightSplit, DividerSnapAlgorithm snapAlgorithm)126     void applyDividerPosition(
127             int position, boolean isLeftRightSplit, DividerSnapAlgorithm snapAlgorithm) {
128         mDimmingSide = DOCKED_INVALID;
129         mRetreatingSideParallax.set(0, 0);
130         mAdvancingSideParallax.set(0, 0);
131         mDimValue = 0;
132         Rect displayBounds = mSplitLayout.getRootBounds();
133 
134         // Figure out which side is shrinking, and assign retreating/advancing bounds
135         final boolean topLeftShrink = isLeftRightSplit
136                 ? position < mSplitLayout.getTopLeftContentBounds().right
137                 : position < mSplitLayout.getTopLeftContentBounds().bottom;
138         if (topLeftShrink) {
139             mShrinkSide = isLeftRightSplit ? DOCKED_LEFT : DOCKED_TOP;
140             mRetreatingContent.set(mSplitLayout.getTopLeftContentBounds());
141             mRetreatingSurface.set(mSplitLayout.getTopLeftBounds());
142             mAdvancingContent.set(mSplitLayout.getBottomRightContentBounds());
143             mAdvancingSurface.set(mSplitLayout.getBottomRightBounds());
144         } else {
145             mShrinkSide = isLeftRightSplit ? DOCKED_RIGHT : DOCKED_BOTTOM;
146             mRetreatingContent.set(mSplitLayout.getBottomRightContentBounds());
147             mRetreatingSurface.set(mSplitLayout.getBottomRightBounds());
148             mAdvancingContent.set(mSplitLayout.getTopLeftContentBounds());
149             mAdvancingSurface.set(mSplitLayout.getTopLeftBounds());
150         }
151 
152         // Figure out if we should be dimming one side
153         mDimmingSide = mParallaxSpec.getDimmingSide(position, snapAlgorithm, isLeftRightSplit);
154 
155         // If so, calculate dimming
156         if (mDimmingSide != DOCKED_INVALID) {
157             mDimValue = mParallaxSpec.getDimValue(position, snapAlgorithm);
158         }
159 
160         // Calculate parallax and modify mRetreatingSideParallax and mAdvancingSideParallax, for use
161         // in adjustRootSurface().
162         mParallaxSpec.getParallax(mRetreatingSideParallax, mAdvancingSideParallax, position,
163                 snapAlgorithm, isLeftRightSplit, displayBounds, mRetreatingSurface,
164                 mRetreatingContent, mAdvancingSurface, mAdvancingContent, mDimmingSide,
165                 topLeftShrink);
166     }
167 
168     /** Applies the calculated parallax and dimming values to task surfaces. */
169     void adjustRootSurface(SurfaceControl.Transaction t,
170             SurfaceControl leash1, SurfaceControl leash2) {
171         SurfaceControl retreatingLeash = null;
172         SurfaceControl advancingLeash = null;
173 
174         if (mParallaxType == PARALLAX_DISMISSING) {
175             switch (mDimmingSide) {
176                 case DOCKED_TOP:
177                 case DOCKED_LEFT:
178                     retreatingLeash = leash1;
179                     mTempRect.set(mSplitLayout.getTopLeftBounds());
180                     advancingLeash = leash2;
181                     mTempRect2.set(mSplitLayout.getBottomRightBounds());
182                     break;
183                 case DOCKED_BOTTOM:
184                 case DOCKED_RIGHT:
185                     retreatingLeash = leash2;
186                     mTempRect.set(mSplitLayout.getBottomRightBounds());
187                     advancingLeash = leash1;
188                     mTempRect2.set(mSplitLayout.getTopLeftBounds());
189                     break;
190             }
191         } else if (mParallaxType == PARALLAX_ALIGN_CENTER || mParallaxType == PARALLAX_FLEX) {
192             switch (mShrinkSide) {
193                 case DOCKED_TOP:
194                 case DOCKED_LEFT:
195                     retreatingLeash = leash1;
196                     mTempRect.set(mSplitLayout.getTopLeftBounds());
197                     advancingLeash = leash2;
198                     mTempRect2.set(mSplitLayout.getBottomRightBounds());
199                     break;
200                 case DOCKED_BOTTOM:
201                 case DOCKED_RIGHT:
202                     retreatingLeash = leash2;
203                     mTempRect.set(mSplitLayout.getBottomRightBounds());
204                     advancingLeash = leash1;
205                     mTempRect2.set(mSplitLayout.getTopLeftBounds());
206                     break;
207             }
208         }
209         if (mParallaxType != PARALLAX_NONE
210                 && retreatingLeash != null && advancingLeash != null) {
211             t.setPosition(retreatingLeash, mTempRect.left + mRetreatingSideParallax.x,
212                     mTempRect.top + mRetreatingSideParallax.y);
213             // Transform the screen-based split bounds to surface-based crop bounds.
214             mTempRect.offsetTo(-mRetreatingSideParallax.x, -mRetreatingSideParallax.y);
215             t.setWindowCrop(retreatingLeash, mTempRect);
216 
217             t.setPosition(advancingLeash, mTempRect2.left + mAdvancingSideParallax.x,
218                     mTempRect2.top + mAdvancingSideParallax.y);
219             // Transform the screen-based split bounds to surface-based crop bounds.
220             mTempRect2.offsetTo(-mAdvancingSideParallax.x, -mAdvancingSideParallax.y);
221             t.setWindowCrop(advancingLeash, mTempRect2);
222         }
223     }
224 
225     void adjustDimSurface(SurfaceControl.Transaction t,
226             SurfaceControl dimLayer1, SurfaceControl dimLayer2) {
227         SurfaceControl targetDimLayer;
228         SurfaceControl oppositeDimLayer;
229         switch (mDimmingSide) {
230             case DOCKED_TOP:
231             case DOCKED_LEFT:
232                 targetDimLayer = dimLayer1;
233                 oppositeDimLayer = dimLayer2;
234                 break;
235             case DOCKED_BOTTOM:
236             case DOCKED_RIGHT:
237                 targetDimLayer = dimLayer2;
238                 oppositeDimLayer = dimLayer1;
239                 break;
240             case DOCKED_INVALID:
241             default:
242                 t.setAlpha(dimLayer1, 0).hide(dimLayer1);
243                 t.setAlpha(dimLayer2, 0).hide(dimLayer2);
244                 return;
245         }
246         t.setAlpha(targetDimLayer, mDimValue)
247                 .setVisibility(targetDimLayer, mDimValue > 0.001f);
248         t.setAlpha(oppositeDimLayer, 0f)
249                 .setVisibility(oppositeDimLayer, false);
250     }
251 }
252