DockedStackDividerController.java revision 20ec11b4e61e5fffd639034f936ed5ef8359d414
120111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber/* 220111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber * Copyright (C) 2012 The Android Open Source Project 320111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber * 4559bf2836f5da25b75bfb229fec0d20d540ee426James Dong * Licensed under the Apache License, Version 2.0 (the "License"); 5956c553ab0ce72f8074ad0fda2ffd66a0305700cJames Dong * you may not use this file except in compliance with the License. 6608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber * You may obtain a copy of the License at 7f933441648ef6a71dee783d733aac17b9508b452Andreas Huber * 850c44c79d2d7dd6cd1485d9d939f67f80b8da1caGloria Wang * http://www.apache.org/licenses/LICENSE-2.0 9760943b5e7a09b602aba04ec451e97662f48b0a4James Dong * 10608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber * Unless required by applicable law or agreed to in writing, software 11608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber * distributed under the License is distributed on an "AS IS" BASIS, 12608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber * See the License for the specific language governing permissions and 14608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber * limitations under the License. 15608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber */ 169c075bca0b75093ca0514a3c8f74d73c8e9e83fdNipun Kwatra 17aa8b569eb652c22821b93a6e543449a52ad21158Lajos Molnarpackage com.android.server.wm; 1805ca3bfb847ff3c1980f2f0922a4d494c0e7ebabLajos Molnar 19608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport static android.app.ActivityManager.StackId.DOCKED_STACK_ID; 204bbfff2dbf3968c267c3b2ea9f8912a38372a9daAndreas Huberimport static android.app.ActivityManager.StackId.INVALID_STACK_ID; 21a9741a9232c81eaf59179acef91f5be46c42264eGloria Wangimport static android.view.WindowManager.DOCKED_BOTTOM; 22608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport static android.view.WindowManager.DOCKED_LEFT; 23608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport static android.view.WindowManager.DOCKED_RIGHT; 24856990b491d84b7ed4fefe337485c8997ba9dd02Glenn Kastenimport static android.view.WindowManager.DOCKED_TOP; 251156dc913a5ba7b2bc86489468d4914430f03d14Andreas Huberimport static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION; 26608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR; 27608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER; 280da4dab0a45a2bc1d95cbc6ef6a4850ed2569584Andreas Huberimport static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 29608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 30608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED; 314f1732b8068970b368a89271158ca29daf25650eztenghui 32608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport android.content.Context; 33608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport android.graphics.Rect; 345778822d86b0337407514b9372562b86edfa91cdAndreas Huberimport android.os.RemoteCallbackList; 35afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huberimport android.os.RemoteException; 3672cecca17d735db6532c45f0a7e10c47ee6f065aChong Zhangimport android.util.ArraySet; 37608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport android.util.Slog; 38608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport android.view.DisplayInfo; 391b86fe063badb5f28c467ade39be0f4008688947Andreas Huberimport android.view.IDockedStackListener; 404f1732b8068970b368a89271158ca29daf25650eztenghuiimport android.view.SurfaceControl; 41608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport android.view.animation.AnimationUtils; 42608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport android.view.animation.Interpolator; 43608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport android.view.animation.PathInterpolator; 445778822d86b0337407514b9372562b86edfa91cdAndreas Huber 45608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport com.android.server.wm.DimLayer.DimLayerUser; 46608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport com.android.server.wm.WindowManagerService.H; 47608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber 48608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huberimport java.util.ArrayList; 49608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber 50a98478bfbcc0f7fb4b164d3dce40ca96df75667dMarco Nelissen/** 51608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber * Keeps information about the docked stack divider. 52608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber */ 531a2fafbaa36390a06cc9a066fcbe147c8c47ea77Pannag Sanketipublic class DockedStackDividerController implements DimLayerUser { 54608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber 55608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM; 56608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber 57608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber /** 584456da54bcd206ed1f518c69cc959ca65a179c83Andreas Huber * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip 59608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber * revealing surface at the earliest. 60b371426ce4cf2fa6d8c3d1903b61322feb165d35Gloria Wang */ 614456da54bcd206ed1f518c69cc959ca65a179c83Andreas Huber private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f; 6285f12e9b9062402d6110df3f7099707912040edbAndreas Huber 6320111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber /** 6420111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip 6534581f44cde67960fbac3ba1f191a2c063ea5145Marco Nelissen * revealing surface at the latest. 66559bf2836f5da25b75bfb229fec0d20d540ee426James Dong */ 676c6b4d0d2b98a7ceee8b697daaf611f8df3254fbJames Dong private static final float CLIP_REVEAL_MEET_LAST = 1f; 686c6b4d0d2b98a7ceee8b697daaf611f8df3254fbJames Dong 69856990b491d84b7ed4fefe337485c8997ba9dd02Glenn Kasten /** 7039ddf8e0f18766f7ba1e3246b774aa6ebd93eea8Andreas Huber * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start 718cb0c4168bf4b678e4a6edfcf409247016be20d5Andreas Huber * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}. 722f46e8152fb881d3a1d7afd223f1ed51f6e358b8Robert Shih */ 738e6912423c3be3fc2f4bab8ac815f0dce075ded8Sreeram Ramachandran private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f; 7434581f44cde67960fbac3ba1f191a2c063ea5145Marco Nelissen 7534581f44cde67960fbac3ba1f191a2c063ea5145Marco Nelissen /** 7620111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, 7720111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}. 7843b1d8ad30bcd61c8cc62fce52fdc6d8381e5732Steve Block */ 79dcd25efb46c41c8d24a0a9cf61fb57f84149709eGloria Wang private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f; 80afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber 81afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR = 82afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber new PathInterpolator(0.2f, 0f, 0.1f, 1f); 83afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber 8443b1d8ad30bcd61c8cc62fce52fdc6d8381e5732Steve Block private static final long IME_ADJUST_ANIM_DURATION = 280; 8543b1d8ad30bcd61c8cc62fce52fdc6d8381e5732Steve Block 86afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private static final long IME_ADJUST_DRAWN_TIMEOUT = 200; 87afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber 88afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private static final int DIVIDER_WIDTH_INACTIVE_DP = 4; 898e6912423c3be3fc2f4bab8ac815f0dce075ded8Sreeram Ramachandran 90bf927f8ec7979f2b64331c2b2f12a6a5dba05bcaVignesh Venkatasubramanian private final WindowManagerService mService; 91afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private final DisplayContent mDisplayContent; 92afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private int mDividerWindowWidth; 93afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private int mDividerWindowWidthInactive; 94afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private int mDividerInsets; 951e5b2b3361ddd07259bf4b29820ca4aa5f3a861bJamie Gennis private boolean mResizing; 96afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private WindowState mWindow; 97afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private final Rect mTmpRect = new Rect(); 98afc16d667afa23f5aa00154ccad62f8c45cf5419Andreas Huber private final Rect mTmpRect2 = new Rect(); 9943b1d8ad30bcd61c8cc62fce52fdc6d8381e5732Steve Block private final Rect mLastRect = new Rect(); 1008db188489871c770d5d56cf67b0001222415db41Eric Laurent private boolean mLastVisibility = false; 10120111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners 102f44de515d3b6098a0b585865c1a0c7b20d3075a6Andreas Huber = new RemoteCallbackList<>(); 103f5ab57c2d5e02af7483c94eddb177e4f5c9e9892Andreas Huber private final DimLayer mDimLayer; 104c71f6e2392bf55cc85ee7c1a376441e9b9aae4c8James Dong 105093437c388e5dff6903a3d43f2ca9f8a1ba4744aAndreas Huber private boolean mMinimizedDock; 106744f5739019d1fd917f981e740b353c3d73fd1a8David Smith private boolean mAnimatingForMinimizedDockedStack; 107343947abc8b7c126f966fd32a0b18bff6c2cecd1Robert Shih private boolean mAnimationStarted; 108965d08ba16ee82bc85f69546360c18e7da907406Gloria Wang private long mAnimationStartTime; 1094d1265cd007b9754d0645bc4fb95701140a45648Andreas Huber private float mAnimationStart; 110fd88f86ec6788170fb4d903c1b0932a18ce1197cJohann private float mAnimationTarget; 111cda17c606b0fe3ccda4dc68a6d43882410ea2462Andreas Huber private long mAnimationDuration; 112608d77b1cf4fb9f63dc861e4e1fa3e80a732f626Andreas Huber private boolean mAnimationStartDelayed; 113856990b491d84b7ed4fefe337485c8997ba9dd02Glenn Kasten private final Interpolator mMinimizedDockInterpolator; 114744f5739019d1fd917f981e740b353c3d73fd1a8David Smith private float mMaximizeMeetFraction; 1154116807a35a27abf635bf6199ed9ad8703c9e94dColin Cross private final Rect mTouchRegion = new Rect(); 116609f1a00c96cf5605f4614e7bb6d0487c98969c5Andreas Huber private boolean mAnimatingForIme; 117956c553ab0ce72f8074ad0fda2ffd66a0305700cJames Dong private boolean mAdjustedForIme; 118cda17c606b0fe3ccda4dc68a6d43882410ea2462Andreas Huber private WindowState mDelayedImeWin; 119cda17c606b0fe3ccda4dc68a6d43882410ea2462Andreas Huber private boolean mAdjustedForDivider; 1205ecbdf58b0674ac4a8b9d56b49ebeb1033e5bd27Dan Albert private float mDividerAnimationStart; 1215ecbdf58b0674ac4a8b9d56b49ebeb1033e5bd27Dan Albert private float mDividerAnimationTarget; 12272b56ef3d9f164159f58725781b01dd3b052b51aMarco Nelissen private float mLastAnimationProgress; 123def582e93022fa5eb7a64d4a11c15598afc0db86Andreas Huber private float mLastDividerProgress; 12420111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber 12520111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) { 12620111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber mService = service; 1278d5ec2a336c114688e5d2950b124440e07e79ad9Ying Wang mDisplayContent = displayContent; 1288d5ec2a336c114688e5d2950b124440e07e79ad9Ying Wang final Context context = service.mContext; 12920111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(), 13020111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber "DockedStackDim"); 13120111aa043c5f404472bc63b90bc5aad906b1101Andreas Huber mMinimizedDockInterpolator = AnimationUtils.loadInterpolator( 132 context, android.R.interpolator.fast_out_slow_in); 133 loadDimens(); 134 } 135 136 private void loadDimens() { 137 final Context context = mService.mContext; 138 mDividerWindowWidth = context.getResources().getDimensionPixelSize( 139 com.android.internal.R.dimen.docked_stack_divider_thickness); 140 mDividerInsets = context.getResources().getDimensionPixelSize( 141 com.android.internal.R.dimen.docked_stack_divider_insets); 142 mDividerWindowWidthInactive = WindowManagerService.dipToPixel( 143 DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics()); 144 } 145 146 void onConfigurationChanged() { 147 loadDimens(); 148 } 149 150 boolean isResizing() { 151 return mResizing; 152 } 153 154 int getContentWidth() { 155 return mDividerWindowWidth - 2 * mDividerInsets; 156 } 157 158 int getContentInsets() { 159 return mDividerInsets; 160 } 161 162 int getContentWidthInactive() { 163 return mDividerWindowWidthInactive; 164 } 165 166 void setResizing(boolean resizing) { 167 if (mResizing != resizing) { 168 mResizing = resizing; 169 resetDragResizingChangeReported(); 170 } 171 } 172 173 void setTouchRegion(Rect touchRegion) { 174 mTouchRegion.set(touchRegion); 175 } 176 177 void getTouchRegion(Rect outRegion) { 178 outRegion.set(mTouchRegion); 179 outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top); 180 } 181 182 private void resetDragResizingChangeReported() { 183 final WindowList windowList = mDisplayContent.getWindowList(); 184 for (int i = windowList.size() - 1; i >= 0; i--) { 185 windowList.get(i).resetDragResizingChangeReported(); 186 } 187 } 188 189 void setWindow(WindowState window) { 190 mWindow = window; 191 reevaluateVisibility(false); 192 } 193 194 void reevaluateVisibility(boolean force) { 195 if (mWindow == null) { 196 return; 197 } 198 TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID); 199 200 // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide 201 final boolean visible = stack != null; 202 if (mLastVisibility == visible && !force) { 203 return; 204 } 205 mLastVisibility = visible; 206 notifyDockedDividerVisibilityChanged(visible); 207 if (!visible) { 208 setResizeDimLayer(false, INVALID_STACK_ID, 0f); 209 } 210 } 211 212 boolean wasVisible() { 213 return mLastVisibility; 214 } 215 216 void setAdjustedForIme( 217 boolean adjustedForIme, boolean adjustedForDivider, 218 boolean animate, WindowState imeWin) { 219 if (mAdjustedForIme != adjustedForIme || mAdjustedForDivider != adjustedForDivider) { 220 if (animate) { 221 startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin); 222 } else { 223 // Animation might be delayed, so only notify if we don't run an animation. 224 notifyAdjustedForImeChanged(adjustedForIme || adjustedForDivider, 0 /* duration */); 225 } 226 mAdjustedForIme = adjustedForIme; 227 mAdjustedForDivider = adjustedForDivider; 228 } 229 } 230 231 void positionDockedStackedDivider(Rect frame) { 232 TaskStack stack = mDisplayContent.getDockedStackLocked(); 233 if (stack == null) { 234 // Unfortunately we might end up with still having a divider, even though the underlying 235 // stack was already removed. This is because we are on AM thread and the removal of the 236 // divider was deferred to WM thread and hasn't happened yet. In that case let's just 237 // keep putting it in the same place it was before the stack was removed to have 238 // continuity and prevent it from jumping to the center. It will get hidden soon. 239 frame.set(mLastRect); 240 return; 241 } else { 242 stack.getDimBounds(mTmpRect); 243 } 244 int side = stack.getDockSide(); 245 switch (side) { 246 case DOCKED_LEFT: 247 frame.set(mTmpRect.right - mDividerInsets, frame.top, 248 mTmpRect.right + frame.width() - mDividerInsets, frame.bottom); 249 break; 250 case DOCKED_TOP: 251 frame.set(frame.left, mTmpRect.bottom - mDividerInsets, 252 mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets); 253 break; 254 case DOCKED_RIGHT: 255 frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top, 256 mTmpRect.left + mDividerInsets, frame.bottom); 257 break; 258 case DOCKED_BOTTOM: 259 frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets, 260 frame.right, mTmpRect.top + mDividerInsets); 261 break; 262 } 263 mLastRect.set(frame); 264 } 265 266 void notifyDockedDividerVisibilityChanged(boolean visible) { 267 final int size = mDockedStackListeners.beginBroadcast(); 268 for (int i = 0; i < size; ++i) { 269 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 270 try { 271 listener.onDividerVisibilityChanged(visible); 272 } catch (RemoteException e) { 273 Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e); 274 } 275 } 276 mDockedStackListeners.finishBroadcast(); 277 } 278 279 void notifyDockedStackExistsChanged(boolean exists) { 280 final int size = mDockedStackListeners.beginBroadcast(); 281 for (int i = 0; i < size; ++i) { 282 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 283 try { 284 listener.onDockedStackExistsChanged(exists); 285 } catch (RemoteException e) { 286 Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e); 287 } 288 } 289 mDockedStackListeners.finishBroadcast(); 290 if (!exists) { 291 setMinimizedDockedStack(false); 292 } 293 } 294 295 void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) { 296 mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED); 297 mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED, 298 minimizedDock ? 1 : 0, 0).sendToTarget(); 299 final int size = mDockedStackListeners.beginBroadcast(); 300 for (int i = 0; i < size; ++i) { 301 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 302 try { 303 listener.onDockedStackMinimizedChanged(minimizedDock, animDuration); 304 } catch (RemoteException e) { 305 Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e); 306 } 307 } 308 mDockedStackListeners.finishBroadcast(); 309 } 310 311 void notifyDockSideChanged(int newDockSide) { 312 final int size = mDockedStackListeners.beginBroadcast(); 313 for (int i = 0; i < size; ++i) { 314 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 315 try { 316 listener.onDockSideChanged(newDockSide); 317 } catch (RemoteException e) { 318 Slog.e(TAG_WM, "Error delivering dock side changed event.", e); 319 } 320 } 321 mDockedStackListeners.finishBroadcast(); 322 } 323 324 void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) { 325 final int size = mDockedStackListeners.beginBroadcast(); 326 for (int i = 0; i < size; ++i) { 327 final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i); 328 try { 329 listener.onAdjustedForImeChanged(adjustedForIme, animDuration); 330 } catch (RemoteException e) { 331 Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e); 332 } 333 } 334 mDockedStackListeners.finishBroadcast(); 335 } 336 337 void registerDockedStackListener(IDockedStackListener listener) { 338 mDockedStackListeners.register(listener); 339 notifyDockedDividerVisibilityChanged(wasVisible()); 340 notifyDockedStackExistsChanged( 341 mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null); 342 notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */); 343 notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */); 344 345 } 346 347 void setResizeDimLayer(boolean visible, int targetStackId, float alpha) { 348 SurfaceControl.openTransaction(); 349 final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId); 350 final TaskStack dockedStack = mDisplayContent.getDockedStackLocked(); 351 boolean visibleAndValid = visible && stack != null && dockedStack != null; 352 if (visibleAndValid) { 353 stack.getDimBounds(mTmpRect); 354 if (mTmpRect.height() > 0 && mTmpRect.width() > 0) { 355 mDimLayer.setBounds(mTmpRect); 356 mDimLayer.show(mService.mLayersController.getResizeDimLayer(), 357 alpha, 0 /* duration */); 358 } else { 359 visibleAndValid = false; 360 } 361 } 362 if (!visibleAndValid) { 363 mDimLayer.hide(); 364 } 365 SurfaceControl.closeTransaction(); 366 } 367 368 /** 369 * Notifies the docked stack divider controller of a visibility change that happens without 370 * an animation. 371 */ 372 void notifyAppVisibilityChanged(AppWindowToken wtoken, boolean visible) { 373 final Task task = wtoken.mTask; 374 if (!task.isHomeTask() || !task.isVisibleForUser()) { 375 return; 376 } 377 378 // If the app that having visibility change is not the top visible one in the task, 379 // it does not affect whether the docked stack is minimized, ignore it. 380 if (task.getTopVisibleAppToken() == null || task.getTopVisibleAppToken() != wtoken) { 381 return; 382 } 383 384 // If the stack is completely offscreen, this might just be an intermediate state when 385 // docking a task/launching recents at the same time, but home doesn't actually get 386 // visible after the state settles in. 387 if (isWithinDisplay(task) 388 && mDisplayContent.getDockedStackVisibleForUserLocked() != null) { 389 setMinimizedDockedStack(visible, false /* animate */); 390 } 391 } 392 393 void notifyAppTransitionStarting(ArraySet<AppWindowToken> openingApps, 394 ArraySet<AppWindowToken> closingApps) { 395 if (containsHomeTaskWithinDisplay(openingApps)) { 396 setMinimizedDockedStack(true /* minimized */, true /* animate */); 397 } else if (containsHomeTaskWithinDisplay(closingApps)) { 398 setMinimizedDockedStack(false /* minimized */, true /* animate */); 399 } 400 } 401 402 private boolean containsHomeTaskWithinDisplay(ArraySet<AppWindowToken> apps) { 403 for (int i = apps.size() - 1; i >= 0; i--) { 404 final Task task = apps.valueAt(i).mTask; 405 if (task != null && task.isHomeTask()) { 406 return isWithinDisplay(task); 407 } 408 } 409 return false; 410 } 411 412 private boolean isWithinDisplay(Task task) { 413 task.mStack.getBounds(mTmpRect); 414 mDisplayContent.getLogicalDisplayRect(mTmpRect2); 415 return mTmpRect.intersect(mTmpRect2); 416 } 417 418 /** 419 * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the 420 * docked stack are heavily clipped so you can only see a minimal peek state. 421 * 422 * @param minimizedDock Whether the docked stack is currently minimized. 423 * @param animate Whether to animate the change. 424 */ 425 private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) { 426 final boolean wasMinimized = mMinimizedDock; 427 mMinimizedDock = minimizedDock; 428 if (minimizedDock == wasMinimized 429 || mDisplayContent.getDockedStackVisibleForUserLocked() == null) { 430 return; 431 } 432 433 clearImeAdjustAnimation(); 434 if (minimizedDock) { 435 if (animate) { 436 startAdjustAnimation(0f, 1f); 437 } else { 438 setMinimizedDockedStack(true); 439 } 440 } else { 441 if (animate) { 442 startAdjustAnimation(1f, 0f); 443 } else { 444 setMinimizedDockedStack(false); 445 } 446 } 447 } 448 449 private void clearImeAdjustAnimation() { 450 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 451 for (int i = stacks.size() - 1; i >= 0; --i) { 452 final TaskStack stack = stacks.get(i); 453 if (stack != null && stack.isAdjustedForIme()) { 454 stack.resetAdjustedForIme(true /* adjustBoundsNow */); 455 } 456 } 457 mAnimatingForIme = false; 458 } 459 460 private void startAdjustAnimation(float from, float to) { 461 mAnimatingForMinimizedDockedStack = true; 462 mAnimationStarted = false; 463 mAnimationStart = from; 464 mAnimationTarget = to; 465 } 466 467 private void startImeAdjustAnimation( 468 boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) { 469 mAnimatingForIme = true; 470 mAnimationStarted = false; 471 472 // If we're not in an animation, the starting point depends on whether we're adjusted 473 // or not. If we're already in an animation, we start from where the current animation 474 // left off, so that the motion doesn't look discontinuous. 475 if (!mAnimatingForIme) { 476 mAnimationStart = mAdjustedForIme ? 1 : 0; 477 mDividerAnimationStart = mAdjustedForDivider ? 1 : 0; 478 mLastAnimationProgress = mAnimationStart; 479 mLastDividerProgress = mDividerAnimationStart; 480 } else { 481 mAnimationStart = mLastAnimationProgress; 482 mDividerAnimationStart = mLastDividerProgress; 483 } 484 mAnimationTarget = adjustedForIme ? 1 : 0; 485 mDividerAnimationTarget = adjustedForDivider ? 1 : 0; 486 487 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 488 for (int i = stacks.size() - 1; i >= 0; --i) { 489 final TaskStack stack = stacks.get(i); 490 if (stack.isVisibleLocked() && stack.isAdjustedForIme()) { 491 stack.beginImeAdjustAnimation(); 492 } 493 } 494 495 // We put all tasks into drag resizing mode - wait until all of them have completed the 496 // drag resizing switch. 497 if (!mService.mWaitingForDrawn.isEmpty()) { 498 mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT); 499 mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, 500 IME_ADJUST_DRAWN_TIMEOUT); 501 mAnimationStartDelayed = true; 502 if (imeWin != null) { 503 504 // There might be an old window delaying the animation start - clear it. 505 if (mDelayedImeWin != null) { 506 mDelayedImeWin.mWinAnimator.endDelayingAnimationStart(); 507 } 508 mDelayedImeWin = imeWin; 509 imeWin.mWinAnimator.startDelayingAnimationStart(); 510 } 511 mService.mWaitingForDrawnCallback = () -> { 512 mAnimationStartDelayed = false; 513 if (mDelayedImeWin != null) { 514 mDelayedImeWin.mWinAnimator.endDelayingAnimationStart(); 515 } 516 notifyAdjustedForImeChanged( 517 adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION); 518 }; 519 } else { 520 notifyAdjustedForImeChanged( 521 adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION); 522 } 523 } 524 525 private void setMinimizedDockedStack(boolean minimized) { 526 final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked(); 527 notifyDockedStackMinimizedChanged(minimized, 0); 528 if (stack == null) { 529 return; 530 } 531 if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) { 532 mService.mWindowPlacerLocked.performSurfacePlacement(); 533 } 534 } 535 536 private boolean isAnimationMaximizing() { 537 return mAnimationTarget == 0f; 538 } 539 540 public boolean animate(long now) { 541 if (mWindow == null) { 542 return false; 543 } 544 if (mAnimatingForMinimizedDockedStack) { 545 return animateForMinimizedDockedStack(now); 546 } else if (mAnimatingForIme) { 547 return animateForIme(now); 548 } else { 549 if (mDimLayer != null && mDimLayer.isDimming()) { 550 mDimLayer.setLayer(mService.mLayersController.getResizeDimLayer()); 551 } 552 return false; 553 } 554 } 555 556 private boolean animateForIme(long now) { 557 if (!mAnimationStarted || mAnimationStartDelayed) { 558 mAnimationStarted = true; 559 mAnimationStartTime = now; 560 mAnimationDuration = (long) 561 (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked()); 562 } 563 float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration); 564 t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR) 565 .getInterpolation(t); 566 final ArrayList<TaskStack> stacks = mDisplayContent.getStacks(); 567 boolean updated = false; 568 for (int i = stacks.size() - 1; i >= 0; --i) { 569 final TaskStack stack = stacks.get(i); 570 if (stack != null && stack.isAdjustedForIme()) { 571 if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) { 572 stack.resetAdjustedForIme(true /* adjustBoundsNow */); 573 updated = true; 574 } else { 575 mLastAnimationProgress = getInterpolatedAnimationValue(t); 576 mLastDividerProgress = getInterpolatedDividerValue(t); 577 updated |= stack.updateAdjustForIme( 578 mLastAnimationProgress, 579 mLastDividerProgress, 580 false /* force */); 581 } 582 if (t >= 1f) { 583 stack.endImeAdjustAnimation(); 584 } 585 } 586 } 587 if (updated) { 588 mService.mWindowPlacerLocked.performSurfacePlacement(); 589 } 590 if (t >= 1.0f) { 591 mLastAnimationProgress = mAnimationTarget; 592 mLastDividerProgress = mDividerAnimationTarget; 593 mAnimatingForIme = false; 594 return false; 595 } else { 596 return true; 597 } 598 } 599 600 private boolean animateForMinimizedDockedStack(long now) { 601 final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked(); 602 if (!mAnimationStarted) { 603 mAnimationStarted = true; 604 mAnimationStartTime = now; 605 final long transitionDuration = isAnimationMaximizing() 606 ? mService.mAppTransition.getLastClipRevealTransitionDuration() 607 : DEFAULT_APP_TRANSITION_DURATION; 608 mAnimationDuration = (long) 609 (transitionDuration * mService.getTransitionAnimationScaleLocked()); 610 mMaximizeMeetFraction = getClipRevealMeetFraction(stack); 611 notifyDockedStackMinimizedChanged(mMinimizedDock, 612 (long) (mAnimationDuration * mMaximizeMeetFraction)); 613 } 614 float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration); 615 t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator) 616 .getInterpolation(t); 617 if (stack != null) { 618 if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) { 619 mService.mWindowPlacerLocked.performSurfacePlacement(); 620 } 621 } 622 if (t >= 1.0f) { 623 mAnimatingForMinimizedDockedStack = false; 624 return false; 625 } else { 626 return true; 627 } 628 } 629 630 private float getInterpolatedAnimationValue(float t) { 631 return t * mAnimationTarget + (1 - t) * mAnimationStart; 632 } 633 634 private float getInterpolatedDividerValue(float t) { 635 return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart; 636 } 637 638 /** 639 * Gets the amount how much to minimize a stack depending on the interpolated fraction t. 640 */ 641 private float getMinimizeAmount(TaskStack stack, float t) { 642 final float naturalAmount = getInterpolatedAnimationValue(t); 643 if (isAnimationMaximizing()) { 644 return adjustMaximizeAmount(stack, t, naturalAmount); 645 } else { 646 return naturalAmount; 647 } 648 } 649 650 /** 651 * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount 652 * during the transition such that the edge of the clip reveal rect is met earlier in the 653 * transition so we don't create a visible "hole", but only if both the clip reveal and the 654 * docked stack divider start from about the same portion on the screen. 655 */ 656 private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) { 657 if (mMaximizeMeetFraction == 1f) { 658 return naturalAmount; 659 } 660 final int minimizeDistance = stack.getMinimizeDistance(); 661 float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation() 662 / (float) minimizeDistance; 663 final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime; 664 final float t2 = Math.min(t / mMaximizeMeetFraction, 1); 665 return amountPrime * t2 + naturalAmount * (1 - t2); 666 } 667 668 /** 669 * Retrieves the animation fraction at which the docked stack has to meet the clip reveal 670 * edge. See {@link #adjustMaximizeAmount}. 671 */ 672 private float getClipRevealMeetFraction(TaskStack stack) { 673 if (!isAnimationMaximizing() || stack == null || 674 !mService.mAppTransition.hadClipRevealAnimation()) { 675 return 1f; 676 } 677 final int minimizeDistance = stack.getMinimizeDistance(); 678 final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation()) 679 / (float) minimizeDistance; 680 final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN) 681 / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN))); 682 return CLIP_REVEAL_MEET_EARLIEST 683 + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST); 684 } 685 686 @Override 687 public boolean isFullscreen() { 688 return false; 689 } 690 691 @Override 692 public DisplayInfo getDisplayInfo() { 693 return mDisplayContent.getDisplayInfo(); 694 } 695 696 @Override 697 public void getDimBounds(Rect outBounds) { 698 // This dim layer user doesn't need this. 699 } 700 701 @Override 702 public String toShortString() { 703 return TAG; 704 } 705 706 WindowState getWindow() { 707 return mWindow; 708 } 709} 710