DockedStackDividerController.java revision 741c0ba8aa84f2879c50884f53955bceb5b6e7f2
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 && !mAnimatingForMinimizedDockedStack) {
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
551        // If we're not in an animation, the starting point depends on whether we're adjusted
552        // or not. If we're already in an animation, we start from where the current animation
553        // left off, so that the motion doesn't look discontinuous.
554        if (!mAnimatingForIme) {
555            mAnimationStart = mAdjustedForIme ? 1 : 0;
556            mDividerAnimationStart = mAdjustedForDivider ? 1 : 0;
557            mLastAnimationProgress = mAnimationStart;
558            mLastDividerProgress = mDividerAnimationStart;
559        } else {
560            mAnimationStart = mLastAnimationProgress;
561            mDividerAnimationStart = mLastDividerProgress;
562        }
563        mAnimatingForIme = true;
564        mAnimationStarted = false;
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        if (stack == null) {
610            return;
611        }
612        if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) {
613            mService.mWindowPlacerLocked.performSurfacePlacement();
614        }
615    }
616
617    private boolean isAnimationMaximizing() {
618        return mAnimationTarget == 0f;
619    }
620
621    public boolean animate(long now) {
622        if (mWindow == null) {
623            return false;
624        }
625        if (mAnimatingForMinimizedDockedStack) {
626            return animateForMinimizedDockedStack(now);
627        } else if (mAnimatingForIme) {
628            return animateForIme(now);
629        } else {
630            if (mDimLayer != null && mDimLayer.isDimming()) {
631                mDimLayer.setLayer(mService.mLayersController.getResizeDimLayer());
632            }
633            return false;
634        }
635    }
636
637    private boolean animateForIme(long now) {
638        if (!mAnimationStarted || mAnimationStartDelayed) {
639            mAnimationStarted = true;
640            mAnimationStartTime = now;
641            mAnimationDuration = (long)
642                    (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
643        }
644        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
645        t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
646                .getInterpolation(t);
647        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
648        boolean updated = false;
649        for (int i = stacks.size() - 1; i >= 0; --i) {
650            final TaskStack stack = stacks.get(i);
651            if (stack != null && stack.isAdjustedForIme()) {
652                if (t >= 1f && mAnimationTarget == 0f && mDividerAnimationTarget == 0f) {
653                    stack.resetAdjustedForIme(true /* adjustBoundsNow */);
654                    updated = true;
655                } else {
656                    mLastAnimationProgress = getInterpolatedAnimationValue(t);
657                    mLastDividerProgress = getInterpolatedDividerValue(t);
658                    updated |= stack.updateAdjustForIme(
659                            mLastAnimationProgress,
660                            mLastDividerProgress,
661                            false /* force */);
662                }
663                if (t >= 1f) {
664                    stack.endImeAdjustAnimation();
665                }
666            }
667        }
668        if (updated) {
669            mService.mWindowPlacerLocked.performSurfacePlacement();
670        }
671        if (t >= 1.0f) {
672            mLastAnimationProgress = mAnimationTarget;
673            mLastDividerProgress = mDividerAnimationTarget;
674            mAnimatingForIme = false;
675            return false;
676        } else {
677            return true;
678        }
679    }
680
681    private boolean animateForMinimizedDockedStack(long now) {
682        final TaskStack stack = mService.mStackIdToStack.get(DOCKED_STACK_ID);
683        if (!mAnimationStarted) {
684            mAnimationStarted = true;
685            mAnimationStartTime = now;
686            final long transitionDuration = isAnimationMaximizing()
687                    ? mService.mAppTransition.getLastClipRevealTransitionDuration()
688                    : DEFAULT_APP_TRANSITION_DURATION;
689            mAnimationDuration = (long)
690                    (transitionDuration * mService.getTransitionAnimationScaleLocked());
691            mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
692            notifyDockedStackMinimizedChanged(mMinimizedDock,
693                    (long) (mAnimationDuration * mMaximizeMeetFraction));
694        }
695        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
696        t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
697                .getInterpolation(t);
698        if (stack != null) {
699            if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
700                mService.mWindowPlacerLocked.performSurfacePlacement();
701            }
702        }
703        if (t >= 1.0f) {
704            mAnimatingForMinimizedDockedStack = false;
705            return false;
706        } else {
707            return true;
708        }
709    }
710
711    private float getInterpolatedAnimationValue(float t) {
712        return t * mAnimationTarget + (1 - t) * mAnimationStart;
713    }
714
715    private float getInterpolatedDividerValue(float t) {
716        return t * mDividerAnimationTarget + (1 - t) * mDividerAnimationStart;
717    }
718
719    /**
720     * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
721     */
722    private float getMinimizeAmount(TaskStack stack, float t) {
723        final float naturalAmount = getInterpolatedAnimationValue(t);
724        if (isAnimationMaximizing()) {
725            return adjustMaximizeAmount(stack, t, naturalAmount);
726        } else {
727            return naturalAmount;
728        }
729    }
730
731    /**
732     * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
733     * during the transition such that the edge of the clip reveal rect is met earlier in the
734     * transition so we don't create a visible "hole", but only if both the clip reveal and the
735     * docked stack divider start from about the same portion on the screen.
736     */
737    private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
738        if (mMaximizeMeetFraction == 1f) {
739            return naturalAmount;
740        }
741        final int minimizeDistance = stack.getMinimizeDistance();
742        float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
743                / (float) minimizeDistance;
744        final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
745        final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
746        return amountPrime * t2 + naturalAmount * (1 - t2);
747    }
748
749    /**
750     * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
751     * edge. See {@link #adjustMaximizeAmount}.
752     */
753    private float getClipRevealMeetFraction(TaskStack stack) {
754        if (!isAnimationMaximizing() || stack == null ||
755                !mService.mAppTransition.hadClipRevealAnimation()) {
756            return 1f;
757        }
758        final int minimizeDistance = stack.getMinimizeDistance();
759        final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
760                / (float) minimizeDistance;
761        final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
762                / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
763        return CLIP_REVEAL_MEET_EARLIEST
764                + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
765    }
766
767    @Override
768    public boolean dimFullscreen() {
769        return false;
770    }
771
772    @Override
773    public DisplayInfo getDisplayInfo() {
774        return mDisplayContent.getDisplayInfo();
775    }
776
777    @Override
778    public void getDimBounds(Rect outBounds) {
779        // This dim layer user doesn't need this.
780    }
781
782    @Override
783    public String toShortString() {
784        return TAG;
785    }
786
787    WindowState getWindow() {
788        return mWindow;
789    }
790
791    void dump(String prefix, PrintWriter pw) {
792        pw.println(prefix + "DockedStackDividerController");
793        pw.println(prefix + "  mLastVisibility=" + mLastVisibility);
794        pw.println(prefix + "  mMinimizedDock=" + mMinimizedDock);
795        pw.println(prefix + "  mAdjustedForIme=" + mAdjustedForIme);
796        pw.println(prefix + "  mAdjustedForDivider=" + mAdjustedForDivider);
797        if (mDimLayer.isDimming()) {
798            pw.println(prefix + "  Dim layer is dimming: ");
799            mDimLayer.printTo(prefix + "    ", pw);
800        }
801    }
802}
803