DockedStackDividerController.java revision f0b76b071c8434fbf4a76798e9cdd56ab67e523d
1/*
2 * Copyright (C) 2012 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
17package com.android.server.wm;
18
19import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
20import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
21import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
22import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
23import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
24import static android.view.Surface.ROTATION_270;
25import static android.view.Surface.ROTATION_90;
26import static android.view.WindowManager.DOCKED_BOTTOM;
27import static android.view.WindowManager.DOCKED_LEFT;
28import static android.view.WindowManager.DOCKED_RIGHT;
29import static android.view.WindowManager.DOCKED_TOP;
30import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
31import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
32import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
33import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
34import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
35
36import android.content.Context;
37import android.content.res.Configuration;
38import android.graphics.Rect;
39import android.os.RemoteCallbackList;
40import android.os.RemoteException;
41import android.util.Slog;
42import android.view.DisplayInfo;
43import android.view.IDockedStackListener;
44import android.view.SurfaceControl;
45import android.view.animation.AnimationUtils;
46import android.view.animation.Interpolator;
47import android.view.animation.PathInterpolator;
48
49import com.android.internal.policy.DividerSnapAlgorithm;
50import com.android.internal.policy.DockedDividerUtils;
51import com.android.server.wm.DimLayer.DimLayerUser;
52import com.android.server.wm.WindowManagerService.H;
53
54import java.io.PrintWriter;
55import java.util.ArrayList;
56
57/**
58 * Keeps information about the docked stack divider.
59 */
60public class DockedStackDividerController implements DimLayerUser {
61
62    private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
63
64    /**
65     * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
66     * revealing surface at the earliest.
67     */
68    private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f;
69
70    /**
71     * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
72     * revealing surface at the latest.
73     */
74    private static final float CLIP_REVEAL_MEET_LAST = 1f;
75
76    /**
77     * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start
78     * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}.
79     */
80    private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f;
81
82    /**
83     * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance,
84     * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}.
85     */
86    private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
87
88    private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR =
89            new PathInterpolator(0.2f, 0f, 0.1f, 1f);
90
91    private static final long IME_ADJUST_ANIM_DURATION = 280;
92
93    private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
94
95    private static final int DIVIDER_WIDTH_INACTIVE_DP = 4;
96
97    private final WindowManagerService mService;
98    private final DisplayContent mDisplayContent;
99    private int mDividerWindowWidth;
100    private int mDividerWindowWidthInactive;
101    private int mDividerInsets;
102    private boolean mResizing;
103    private WindowState mWindow;
104    private final Rect mTmpRect = new Rect();
105    private final Rect mTmpRect2 = new Rect();
106    private final Rect mTmpRect3 = new Rect();
107    private final Rect mLastRect = new Rect();
108    private boolean mLastVisibility = false;
109    private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
110            = new RemoteCallbackList<>();
111    private final DimLayer mDimLayer;
112
113    private boolean mMinimizedDock;
114    private boolean mAnimatingForMinimizedDockedStack;
115    private boolean mAnimationStarted;
116    private long mAnimationStartTime;
117    private float mAnimationStart;
118    private float mAnimationTarget;
119    private long mAnimationDuration;
120    private boolean mAnimationStartDelayed;
121    private final Interpolator mMinimizedDockInterpolator;
122    private float mMaximizeMeetFraction;
123    private final Rect mTouchRegion = new Rect();
124    private boolean mAnimatingForIme;
125    private boolean mAdjustedForIme;
126    private int mImeHeight;
127    private WindowState mDelayedImeWin;
128    private boolean mAdjustedForDivider;
129    private float mDividerAnimationStart;
130    private float mDividerAnimationTarget;
131    private float mLastAnimationProgress;
132    private float mLastDividerProgress;
133    private final DividerSnapAlgorithm[] mSnapAlgorithmForRotation = new DividerSnapAlgorithm[4];
134
135    DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
136        mService = service;
137        mDisplayContent = displayContent;
138        final Context context = service.mContext;
139        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
140                "DockedStackDim");
141        mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
142                context, android.R.interpolator.fast_out_slow_in);
143        loadDimens();
144    }
145
146    int getSmallestWidthDpForBounds(Rect bounds) {
147        final DisplayInfo di = mDisplayContent.getDisplayInfo();
148
149        // If the bounds are fullscreen, return the value of the fullscreen configuration
150        if (bounds == null || (bounds.left == 0 && bounds.top == 0
151                && bounds.right == di.logicalWidth && bounds.bottom == di.logicalHeight)) {
152            return mService.mCurConfiguration.smallestScreenWidthDp;
153        }
154        final int baseDisplayWidth = mDisplayContent.mBaseDisplayWidth;
155        final int baseDisplayHeight = mDisplayContent.mBaseDisplayHeight;
156        int minWidth = Integer.MAX_VALUE;
157
158        // Go through all screen orientations and find the orientation in which the task has the
159        // smallest width.
160        for (int rotation = 0; rotation < 4; rotation++) {
161            mTmpRect.set(bounds);
162            mDisplayContent.rotateBounds(di.rotation, rotation, mTmpRect);
163            final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
164            mTmpRect2.set(0, 0,
165                    rotated ? baseDisplayHeight : baseDisplayWidth,
166                    rotated ? baseDisplayWidth : baseDisplayHeight);
167            final int orientation = mTmpRect2.width() <= mTmpRect2.height()
168                    ? ORIENTATION_PORTRAIT
169                    : ORIENTATION_LANDSCAPE;
170            final int dockSide = TaskStack.getDockSideUnchecked(mTmpRect, mTmpRect2, orientation);
171            final int position = DockedDividerUtils.calculatePositionForBounds(mTmpRect, dockSide,
172                    getContentWidth());
173
174            // Since we only care about feasible states, snap to the closest snap target, like it
175            // would happen when actually rotating the screen.
176            final int snappedPosition = mSnapAlgorithmForRotation[rotation]
177                    .calculateNonDismissingSnapTarget(position).position;
178            DockedDividerUtils.calculateBoundsForPosition(snappedPosition, dockSide, mTmpRect,
179                    mTmpRect2.width(), mTmpRect2.height(), getContentWidth());
180            mService.mPolicy.getStableInsetsLw(rotation, mTmpRect2.width(), mTmpRect2.height(),
181                    mTmpRect3);
182            mService.subtractInsets(mTmpRect2, mTmpRect3, mTmpRect);
183            minWidth = Math.min(mTmpRect.width(), minWidth);
184        }
185        return (int) (minWidth / mDisplayContent.getDisplayMetrics().density);
186    }
187
188    private void initSnapAlgorithmForRotations() {
189        final Configuration baseConfig = mService.mCurConfiguration;
190
191        // Initialize the snap algorithms for all 4 screen orientations.
192        final Configuration config = new Configuration();
193        for (int rotation = 0; rotation < 4; rotation++) {
194            final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
195            final int dw = rotated
196                    ? mDisplayContent.mBaseDisplayHeight
197                    : mDisplayContent.mBaseDisplayWidth;
198            final int dh = rotated
199                    ? mDisplayContent.mBaseDisplayWidth
200                    : mDisplayContent.mBaseDisplayHeight;
201            mService.mPolicy.getStableInsetsLw(rotation, dw, dh, mTmpRect);
202            config.setToDefaults();
203            config.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;
204            config.screenWidthDp = (int)
205                    (mService.mPolicy.getConfigDisplayWidth(dw, dh, rotation, baseConfig.uiMode) /
206                            mDisplayContent.getDisplayMetrics().density);
207            config.screenHeightDp = (int)
208                    (mService.mPolicy.getConfigDisplayHeight(dw, dh, rotation, baseConfig.uiMode) /
209                            mDisplayContent.getDisplayMetrics().density);
210            final Context rotationContext = mService.mContext.createConfigurationContext(config);
211            mSnapAlgorithmForRotation[rotation] = new DividerSnapAlgorithm(
212                    rotationContext.getResources(), dw, dh, getContentWidth(),
213                    config.orientation == ORIENTATION_PORTRAIT, mTmpRect);
214        }
215    }
216
217    private void loadDimens() {
218        final Context context = mService.mContext;
219        mDividerWindowWidth = context.getResources().getDimensionPixelSize(
220                com.android.internal.R.dimen.docked_stack_divider_thickness);
221        mDividerInsets = context.getResources().getDimensionPixelSize(
222                com.android.internal.R.dimen.docked_stack_divider_insets);
223        mDividerWindowWidthInactive = WindowManagerService.dipToPixel(
224                DIVIDER_WIDTH_INACTIVE_DP, mDisplayContent.getDisplayMetrics());
225        initSnapAlgorithmForRotations();
226    }
227
228    void onConfigurationChanged() {
229        loadDimens();
230    }
231
232    boolean isResizing() {
233        return mResizing;
234    }
235
236    int getContentWidth() {
237        return mDividerWindowWidth - 2 * mDividerInsets;
238    }
239
240    int getContentInsets() {
241        return mDividerInsets;
242    }
243
244    int getContentWidthInactive() {
245        return mDividerWindowWidthInactive;
246    }
247
248    void setResizing(boolean resizing) {
249        if (mResizing != resizing) {
250            mResizing = resizing;
251            resetDragResizingChangeReported();
252        }
253    }
254
255    void setTouchRegion(Rect touchRegion) {
256        mTouchRegion.set(touchRegion);
257    }
258
259    void getTouchRegion(Rect outRegion) {
260        outRegion.set(mTouchRegion);
261        outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
262    }
263
264    private void resetDragResizingChangeReported() {
265        final WindowList windowList = mDisplayContent.getWindowList();
266        for (int i = windowList.size() - 1; i >= 0; i--) {
267            windowList.get(i).resetDragResizingChangeReported();
268        }
269    }
270
271    void setWindow(WindowState window) {
272        mWindow = window;
273        reevaluateVisibility(false);
274    }
275
276    void reevaluateVisibility(boolean force) {
277        if (mWindow == null) {
278            return;
279        }
280        TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID);
281
282        // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
283        final boolean visible = stack != null;
284        if (mLastVisibility == visible && !force) {
285            return;
286        }
287        mLastVisibility = visible;
288        notifyDockedDividerVisibilityChanged(visible);
289        if (!visible) {
290            setResizeDimLayer(false, INVALID_STACK_ID, 0f);
291        }
292    }
293
294    boolean wasVisible() {
295        return mLastVisibility;
296    }
297
298    void setAdjustedForIme(
299            boolean adjustedForIme, boolean adjustedForDivider,
300            boolean animate, WindowState imeWin, int imeHeight) {
301        if (mAdjustedForIme != adjustedForIme || (adjustedForIme && mImeHeight != imeHeight)
302                || mAdjustedForDivider != adjustedForDivider) {
303            if (animate) {
304                startImeAdjustAnimation(adjustedForIme, adjustedForDivider, imeWin);
305            } else {
306                // Animation might be delayed, so only notify if we don't run an animation.
307                notifyAdjustedForImeChanged(adjustedForIme || adjustedForDivider, 0 /* duration */);
308            }
309            mAdjustedForIme = adjustedForIme;
310            mImeHeight = imeHeight;
311            mAdjustedForDivider = adjustedForDivider;
312        }
313    }
314
315    int getImeHeightAdjustedFor() {
316        return mImeHeight;
317    }
318
319    void positionDockedStackedDivider(Rect frame) {
320        TaskStack stack = mDisplayContent.getDockedStackLocked();
321        if (stack == null) {
322            // Unfortunately we might end up with still having a divider, even though the underlying
323            // stack was already removed. This is because we are on AM thread and the removal of the
324            // divider was deferred to WM thread and hasn't happened yet. In that case let's just
325            // keep putting it in the same place it was before the stack was removed to have
326            // continuity and prevent it from jumping to the center. It will get hidden soon.
327            frame.set(mLastRect);
328            return;
329        } else {
330            stack.getDimBounds(mTmpRect);
331        }
332        int side = stack.getDockSide();
333        switch (side) {
334            case DOCKED_LEFT:
335                frame.set(mTmpRect.right - mDividerInsets, frame.top,
336                        mTmpRect.right + frame.width() - mDividerInsets, frame.bottom);
337                break;
338            case DOCKED_TOP:
339                frame.set(frame.left, mTmpRect.bottom - mDividerInsets,
340                        mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets);
341                break;
342            case DOCKED_RIGHT:
343                frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top,
344                        mTmpRect.left + mDividerInsets, frame.bottom);
345                break;
346            case DOCKED_BOTTOM:
347                frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets,
348                        frame.right, mTmpRect.top + mDividerInsets);
349                break;
350        }
351        mLastRect.set(frame);
352    }
353
354    void notifyDockedDividerVisibilityChanged(boolean visible) {
355        final int size = mDockedStackListeners.beginBroadcast();
356        for (int i = 0; i < size; ++i) {
357            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
358            try {
359                listener.onDividerVisibilityChanged(visible);
360            } catch (RemoteException e) {
361                Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e);
362            }
363        }
364        mDockedStackListeners.finishBroadcast();
365    }
366
367    void notifyDockedStackExistsChanged(boolean exists) {
368        final int size = mDockedStackListeners.beginBroadcast();
369        for (int i = 0; i < size; ++i) {
370            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
371            try {
372                listener.onDockedStackExistsChanged(exists);
373            } catch (RemoteException e) {
374                Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e);
375            }
376        }
377        mDockedStackListeners.finishBroadcast();
378        if (!exists) {
379            setMinimizedDockedStack(false);
380        }
381    }
382
383    void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
384        mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
385        mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
386                minimizedDock ? 1 : 0, 0).sendToTarget();
387        final int size = mDockedStackListeners.beginBroadcast();
388        for (int i = 0; i < size; ++i) {
389            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
390            try {
391                listener.onDockedStackMinimizedChanged(minimizedDock, animDuration);
392            } catch (RemoteException e) {
393                Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e);
394            }
395        }
396        mDockedStackListeners.finishBroadcast();
397    }
398
399    void notifyDockSideChanged(int newDockSide) {
400        final int size = mDockedStackListeners.beginBroadcast();
401        for (int i = 0; i < size; ++i) {
402            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
403            try {
404                listener.onDockSideChanged(newDockSide);
405            } catch (RemoteException e) {
406                Slog.e(TAG_WM, "Error delivering dock side changed event.", e);
407            }
408        }
409        mDockedStackListeners.finishBroadcast();
410    }
411
412    void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
413        final int size = mDockedStackListeners.beginBroadcast();
414        for (int i = 0; i < size; ++i) {
415            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
416            try {
417                listener.onAdjustedForImeChanged(adjustedForIme, animDuration);
418            } catch (RemoteException e) {
419                Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e);
420            }
421        }
422        mDockedStackListeners.finishBroadcast();
423    }
424
425    void registerDockedStackListener(IDockedStackListener listener) {
426        mDockedStackListeners.register(listener);
427        notifyDockedDividerVisibilityChanged(wasVisible());
428        notifyDockedStackExistsChanged(
429                mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
430        notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
431        notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
432
433    }
434
435    void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
436        SurfaceControl.openTransaction();
437        final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
438        final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
439        boolean visibleAndValid = visible && stack != null && dockedStack != null;
440        if (visibleAndValid) {
441            stack.getDimBounds(mTmpRect);
442            if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
443                mDimLayer.setBounds(mTmpRect);
444                mDimLayer.show(mService.mLayersController.getResizeDimLayer(),
445                        alpha, 0 /* duration */);
446            } else {
447                visibleAndValid = false;
448            }
449        }
450        if (!visibleAndValid) {
451            mDimLayer.hide();
452        }
453        SurfaceControl.closeTransaction();
454    }
455
456    /**
457     * Notifies the docked stack divider controller of a visibility change that happens without
458     * an animation.
459     */
460    void notifyAppVisibilityChanged() {
461        checkMinimizeChanged(false /* animate */);
462    }
463
464    void notifyAppTransitionStarting() {
465        checkMinimizeChanged(true /* animate */);
466    }
467
468    boolean isMinimizedDock() {
469        return mMinimizedDock;
470    }
471
472    private void checkMinimizeChanged(boolean animate) {
473        if (mDisplayContent.getDockedStackVisibleForUserLocked() == null) {
474            return;
475        }
476        final TaskStack homeStack = mDisplayContent.getHomeStack();
477        if (homeStack == null) {
478            return;
479        }
480        final Task homeTask = homeStack.findHomeTask();
481        if (homeTask == null || !isWithinDisplay(homeTask)) {
482            return;
483        }
484        final TaskStack fullscreenStack
485                = mService.mStackIdToStack.get(FULLSCREEN_WORKSPACE_STACK_ID);
486        final ArrayList<Task> homeStackTasks = homeStack.getTasks();
487        final Task topHomeStackTask = homeStackTasks.get(homeStackTasks.size() - 1);
488        final boolean homeVisible = homeTask.getTopVisibleAppToken() != null;
489        final boolean homeBehind = (fullscreenStack != null && fullscreenStack.isVisibleLocked())
490                || (homeStackTasks.size() > 1 && topHomeStackTask != homeTask);
491        setMinimizedDockedStack(homeVisible && !homeBehind, animate);
492    }
493
494    private boolean isWithinDisplay(Task task) {
495        task.mStack.getBounds(mTmpRect);
496        mDisplayContent.getLogicalDisplayRect(mTmpRect2);
497        return mTmpRect.intersect(mTmpRect2);
498    }
499
500    /**
501     * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the
502     * docked stack are heavily clipped so you can only see a minimal peek state.
503     *
504     * @param minimizedDock Whether the docked stack is currently minimized.
505     * @param animate Whether to animate the change.
506     */
507    private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
508        final boolean wasMinimized = mMinimizedDock;
509        mMinimizedDock = minimizedDock;
510        if (minimizedDock == wasMinimized) {
511            return;
512        }
513
514        clearImeAdjustAnimation();
515        if (minimizedDock) {
516            if (animate) {
517                startAdjustAnimation(0f, 1f);
518            } else {
519                setMinimizedDockedStack(true);
520            }
521        } else {
522            if (animate) {
523                startAdjustAnimation(1f, 0f);
524            } else {
525                setMinimizedDockedStack(false);
526            }
527        }
528    }
529
530    private void clearImeAdjustAnimation() {
531        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
532        for (int i = stacks.size() - 1; i >= 0; --i) {
533            final TaskStack stack = stacks.get(i);
534            if (stack != null && stack.isAdjustedForIme()) {
535                stack.resetAdjustedForIme(true /* adjustBoundsNow */);
536            }
537        }
538        mAnimatingForIme = false;
539    }
540
541    private void startAdjustAnimation(float from, float to) {
542        mAnimatingForMinimizedDockedStack = true;
543        mAnimationStarted = false;
544        mAnimationStart = from;
545        mAnimationTarget = to;
546    }
547
548    private void startImeAdjustAnimation(
549            boolean adjustedForIme, boolean adjustedForDivider, WindowState imeWin) {
550        mAnimatingForIme = true;
551        mAnimationStarted = false;
552
553        // If we're not in an animation, the starting point depends on whether we're adjusted
554        // or not. If we're already in an animation, we start from where the current animation
555        // left off, so that the motion doesn't look discontinuous.
556        if (!mAnimatingForIme) {
557            mAnimationStart = mAdjustedForIme ? 1 : 0;
558            mDividerAnimationStart = mAdjustedForDivider ? 1 : 0;
559            mLastAnimationProgress = mAnimationStart;
560            mLastDividerProgress = mDividerAnimationStart;
561        } else {
562            mAnimationStart = mLastAnimationProgress;
563            mDividerAnimationStart = mLastDividerProgress;
564        }
565        mAnimationTarget = adjustedForIme ? 1 : 0;
566        mDividerAnimationTarget = adjustedForDivider ? 1 : 0;
567
568        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
569        for (int i = stacks.size() - 1; i >= 0; --i) {
570            final TaskStack stack = stacks.get(i);
571            if (stack.isVisibleLocked() && stack.isAdjustedForIme()) {
572                stack.beginImeAdjustAnimation();
573            }
574        }
575
576        // We put all tasks into drag resizing mode - wait until all of them have completed the
577        // drag resizing switch.
578        if (!mService.mWaitingForDrawn.isEmpty()) {
579            mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
580            mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
581                    IME_ADJUST_DRAWN_TIMEOUT);
582            mAnimationStartDelayed = true;
583            if (imeWin != null) {
584
585                // There might be an old window delaying the animation start - clear it.
586                if (mDelayedImeWin != null) {
587                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
588                }
589                mDelayedImeWin = imeWin;
590                imeWin.mWinAnimator.startDelayingAnimationStart();
591            }
592            mService.mWaitingForDrawnCallback = () -> {
593                mAnimationStartDelayed = false;
594                if (mDelayedImeWin != null) {
595                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
596                }
597                notifyAdjustedForImeChanged(
598                        adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
599            };
600        } else {
601            notifyAdjustedForImeChanged(
602                    adjustedForIme || adjustedForDivider, IME_ADJUST_ANIM_DURATION);
603        }
604    }
605
606    private void setMinimizedDockedStack(boolean minimized) {
607        final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
608        notifyDockedStackMinimizedChanged(minimized, 0);
609        setMinimizeAmount(stack, minimized ? 1f : 0f);
610    }
611
612    private boolean isAnimationMaximizing() {
613        return mAnimationTarget == 0f;
614    }
615
616    public boolean animate(long now) {
617        if (mWindow == null) {
618            return false;
619        }
620        if (mAnimatingForMinimizedDockedStack) {
621            return animateForMinimizedDockedStack(now);
622        } else if (mAnimatingForIme) {
623            return animateForIme(now);
624        } else {
625            if (mDimLayer != null && mDimLayer.isDimming()) {
626                mDimLayer.setLayer(mService.mLayersController.getResizeDimLayer());
627            }
628            return false;
629        }
630    }
631
632    private boolean animateForIme(long now) {
633        if (!mAnimationStarted || mAnimationStartDelayed) {
634            mAnimationStarted = true;
635            mAnimationStartTime = now;
636            mAnimationDuration = (long)
637                    (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
638        }
639        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
640        t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
641                .getInterpolation(t);
642        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
643        boolean updated = false;
644        for (int i = stacks.size() - 1; i >= 0; --i) {
645            final TaskStack stack = stacks.get(i);
646            if (stack != null && stack.isAdjustedForIme()) {
647                if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) {
648                    stack.resetAdjustedForIme(true /* adjustBoundsNow */);
649                    updated = true;
650                } else {
651                    mLastAnimationProgress = getInterpolatedAnimationValue(t);
652                    mLastDividerProgress = getInterpolatedDividerValue(t);
653                    updated |= stack.updateAdjustForIme(
654                            mLastAnimationProgress,
655                            mLastDividerProgress,
656                            false /* force */);
657                }
658                if (t >= 1f) {
659                    stack.endImeAdjustAnimation();
660                }
661            }
662        }
663        if (updated) {
664            mService.mWindowPlacerLocked.performSurfacePlacement();
665        }
666        if (t >= 1.0f) {
667            mLastAnimationProgress = mAnimationTarget;
668            mLastDividerProgress = mDividerAnimationTarget;
669            mAnimatingForIme = false;
670            return false;
671        } else {
672            return true;
673        }
674    }
675
676    private boolean animateForMinimizedDockedStack(long now) {
677        final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
678        if (!mAnimationStarted) {
679            mAnimationStarted = true;
680            mAnimationStartTime = now;
681            final long transitionDuration = isAnimationMaximizing()
682                    ? mService.mAppTransition.getLastClipRevealTransitionDuration()
683                    : DEFAULT_APP_TRANSITION_DURATION;
684            mAnimationDuration = (long)
685                    (transitionDuration * mService.getTransitionAnimationScaleLocked());
686            mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
687            notifyDockedStackMinimizedChanged(mMinimizedDock,
688                    (long) (mAnimationDuration * mMaximizeMeetFraction));
689        }
690        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
691        t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
692                .getInterpolation(t);
693        setMinimizeAmount(stack, getMinimizeAmount(stack, t));
694
695        if (t >= 1.0f) {
696            mAnimatingForMinimizedDockedStack = false;
697            return false;
698        } else {
699            return true;
700        }
701    }
702
703    void setMinimizeAmount(TaskStack dockedStack, float minimizeAmount) {
704        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
705
706        // If the docked stack is not visible, clear the complementary stack on all stacks.
707        if (dockedStack == null) {
708            for (int i = stacks.size() - 1; i >= 0; --i) {
709                final TaskStack stack = stacks.get(i);
710                stack.resetAdjustedForComplementDock();
711            }
712            return;
713        }
714
715        // Otherwise if the docked stack minimize amount has changed, update the adjusted bounds
716        // on the other stack that's currently visible, so that the stack's getDimBounds()
717        // occupies what's left by the docked stack. This is needed so that stuff like wallpaper
718        // gets cropped properly to the area left by the dock.
719        if (dockedStack.setAdjustedForMinimizedDock(minimizeAmount)) {
720            final boolean adjusted =
721                    dockedStack.isVisibleForUserLocked() && minimizeAmount != 0.0f;
722            dockedStack.getDimBounds(mTmpRect2);
723            int dockSide = dockedStack.getDockSide();
724            for (int i = stacks.size() - 1; i >= 0; --i) {
725                final TaskStack stack = stacks.get(i);
726                if (stack == dockedStack) {
727                    continue;
728                }
729                if (stack.isVisibleLocked() && adjusted) {
730                    stack.setAdjustedForComplementDock(mTmpRect2, dockSide);
731                } else {
732                    stack.resetAdjustedForComplementDock();
733                }
734            }
735            mService.mWindowPlacerLocked.performSurfacePlacement();
736        }
737    }
738
739    private float getInterpolatedAnimationValue(float t) {
740        return t * mAnimationTarget + (1 - t) * mAnimationStart;
741    }
742
743    private float getInterpolatedDividerValue(float t) {
744        return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
745    }
746
747    /**
748     * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
749     */
750    private float getMinimizeAmount(TaskStack stack, float t) {
751        final float naturalAmount = getInterpolatedAnimationValue(t);
752        if (isAnimationMaximizing()) {
753            return adjustMaximizeAmount(stack, t, naturalAmount);
754        } else {
755            return naturalAmount;
756        }
757    }
758
759    /**
760     * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
761     * during the transition such that the edge of the clip reveal rect is met earlier in the
762     * transition so we don't create a visible "hole", but only if both the clip reveal and the
763     * docked stack divider start from about the same portion on the screen.
764     */
765    private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
766        if (mMaximizeMeetFraction == 1f) {
767            return naturalAmount;
768        }
769        final int minimizeDistance = stack.getMinimizeDistance();
770        float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
771                / (float) minimizeDistance;
772        final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
773        final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
774        return amountPrime * t2 + naturalAmount * (1 - t2);
775    }
776
777    /**
778     * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
779     * edge. See {@link #adjustMaximizeAmount}.
780     */
781    private float getClipRevealMeetFraction(TaskStack stack) {
782        if (!isAnimationMaximizing() || stack == null ||
783                !mService.mAppTransition.hadClipRevealAnimation()) {
784            return 1f;
785        }
786        final int minimizeDistance = stack.getMinimizeDistance();
787        final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
788                / (float) minimizeDistance;
789        final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
790                / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
791        return CLIP_REVEAL_MEET_EARLIEST
792                + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
793    }
794
795    @Override
796    public boolean dimFullscreen() {
797        return false;
798    }
799
800    @Override
801    public DisplayInfo getDisplayInfo() {
802        return mDisplayContent.getDisplayInfo();
803    }
804
805    @Override
806    public void getDimBounds(Rect outBounds) {
807        // This dim layer user doesn't need this.
808    }
809
810    @Override
811    public String toShortString() {
812        return TAG;
813    }
814
815    WindowState getWindow() {
816        return mWindow;
817    }
818
819    void dump(String prefix, PrintWriter pw) {
820        pw.println(prefix + "DockedStackDividerController");
821        pw.println(prefix + "  mLastVisibility=" + mLastVisibility);
822        pw.println(prefix + "  mMinimizedDock=" + mMinimizedDock);
823        pw.println(prefix + "  mAdjustedForIme=" + mAdjustedForIme);
824        pw.println(prefix + "  mAdjustedForDivider=" + mAdjustedForDivider);
825        if (mDimLayer.isDimming()) {
826            pw.println(prefix + "  Dim layer is dimming: ");
827            mDimLayer.printTo(prefix + "    ", pw);
828        }
829    }
830}
831