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