DockedStackDividerController.java revision 853304c0b11921db142a3945ab66fae5f0cc7d8a
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.INVALID_STACK_ID;
21import static android.view.WindowManager.DOCKED_BOTTOM;
22import static android.view.WindowManager.DOCKED_LEFT;
23import static android.view.WindowManager.DOCKED_RIGHT;
24import static android.view.WindowManager.DOCKED_TOP;
25import static com.android.server.wm.AppTransition.DEFAULT_APP_TRANSITION_DURATION;
26import static com.android.server.wm.AppTransition.TOUCH_RESPONSE_INTERPOLATOR;
27import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
28import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
29import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
30import static com.android.server.wm.WindowManagerService.H.NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED;
31
32import android.content.Context;
33import android.graphics.Rect;
34import android.os.RemoteCallbackList;
35import android.os.RemoteException;
36import android.util.ArraySet;
37import android.util.Slog;
38import android.view.DisplayInfo;
39import android.view.IDockedStackListener;
40import android.view.SurfaceControl;
41import android.view.animation.AnimationUtils;
42import android.view.animation.Interpolator;
43import android.view.animation.PathInterpolator;
44
45import com.android.server.wm.DimLayer.DimLayerUser;
46import com.android.server.wm.WindowManagerService.H;
47
48import java.util.ArrayList;
49
50/**
51 * Keeps information about the docked stack divider.
52 */
53public class DockedStackDividerController implements DimLayerUser {
54
55    private static final String TAG = TAG_WITH_CLASS_NAME ? "DockedStackDividerController" : TAG_WM;
56
57    /**
58     * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
59     * revealing surface at the earliest.
60     */
61    private static final float CLIP_REVEAL_MEET_EARLIEST = 0.6f;
62
63    /**
64     * The fraction during the maximize/clip reveal animation the divider meets the edge of the clip
65     * revealing surface at the latest.
66     */
67    private static final float CLIP_REVEAL_MEET_LAST = 1f;
68
69    /**
70     * If the app translates at least CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance, we start
71     * meet somewhere between {@link #CLIP_REVEAL_MEET_LAST} and {@link #CLIP_REVEAL_MEET_EARLIEST}.
72     */
73    private static final float CLIP_REVEAL_MEET_FRACTION_MIN = 0.4f;
74
75    /**
76     * If the app translates equals or more than CLIP_REVEAL_MEET_FRACTION_MIN * minimize distance,
77     * we meet at {@link #CLIP_REVEAL_MEET_EARLIEST}.
78     */
79    private static final float CLIP_REVEAL_MEET_FRACTION_MAX = 0.8f;
80
81    private static final Interpolator IME_ADJUST_ENTRY_INTERPOLATOR =
82            new PathInterpolator(0.2f, 0f, 0.1f, 1f);
83
84    private static final long IME_ADJUST_ANIM_DURATION = 280;
85
86    private static final long IME_ADJUST_DRAWN_TIMEOUT = 200;
87
88    private final WindowManagerService mService;
89    private final DisplayContent mDisplayContent;
90    private int mDividerWindowWidth;
91    private int mDividerInsets;
92    private boolean mResizing;
93    private WindowState mWindow;
94    private final Rect mTmpRect = new Rect();
95    private final Rect mTmpRect2 = new Rect();
96    private final Rect mLastRect = new Rect();
97    private boolean mLastVisibility = false;
98    private final RemoteCallbackList<IDockedStackListener> mDockedStackListeners
99            = new RemoteCallbackList<>();
100    private final DimLayer mDimLayer;
101
102    private boolean mMinimizedDock;
103    private boolean mAnimatingForMinimizedDockedStack;
104    private boolean mAnimationStarted;
105    private long mAnimationStartTime;
106    private float mAnimationStart;
107    private float mAnimationTarget;
108    private long mAnimationDuration;
109    private boolean mAnimationStartDelayed;
110    private final Interpolator mMinimizedDockInterpolator;
111    private float mMaximizeMeetFraction;
112    private final Rect mTouchRegion = new Rect();
113    private boolean mAnimatingForIme;
114    private boolean mAdjustedForIme;
115    private WindowState mDelayedImeWin;
116
117    DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
118        mService = service;
119        mDisplayContent = displayContent;
120        final Context context = service.mContext;
121        mDimLayer = new DimLayer(displayContent.mService, this, displayContent.getDisplayId(),
122                "DockedStackDim");
123        mMinimizedDockInterpolator = AnimationUtils.loadInterpolator(
124                context, android.R.interpolator.fast_out_slow_in);
125        loadDimens();
126    }
127
128    private void loadDimens() {
129        final Context context = mService.mContext;
130        mDividerWindowWidth = context.getResources().getDimensionPixelSize(
131                com.android.internal.R.dimen.docked_stack_divider_thickness);
132        mDividerInsets = context.getResources().getDimensionPixelSize(
133                com.android.internal.R.dimen.docked_stack_divider_insets);
134    }
135
136    void onConfigurationChanged() {
137        loadDimens();
138    }
139
140    boolean isResizing() {
141        return mResizing;
142    }
143
144    int getContentWidth() {
145        return mDividerWindowWidth - 2 * mDividerInsets;
146    }
147
148    int getContentInsets() {
149        return mDividerInsets;
150    }
151
152    void setResizing(boolean resizing) {
153        if (mResizing != resizing) {
154            mResizing = resizing;
155            resetDragResizingChangeReported();
156        }
157    }
158
159    void setTouchRegion(Rect touchRegion) {
160        mTouchRegion.set(touchRegion);
161    }
162
163    void getTouchRegion(Rect outRegion) {
164        outRegion.set(mTouchRegion);
165        outRegion.offset(mWindow.getFrameLw().left, mWindow.getFrameLw().top);
166    }
167
168    private void resetDragResizingChangeReported() {
169        final WindowList windowList = mDisplayContent.getWindowList();
170        for (int i = windowList.size() - 1; i >= 0; i--) {
171            windowList.get(i).resetDragResizingChangeReported();
172        }
173    }
174
175    void setWindow(WindowState window) {
176        mWindow = window;
177        reevaluateVisibility(false);
178    }
179
180    void reevaluateVisibility(boolean force) {
181        if (mWindow == null) {
182            return;
183        }
184        TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID);
185
186        // If the stack is invisible, we policy force hide it in WindowAnimator.shouldForceHide
187        final boolean visible = stack != null;
188        if (mLastVisibility == visible && !force) {
189            return;
190        }
191        mLastVisibility = visible;
192        notifyDockedDividerVisibilityChanged(visible);
193        if (!visible) {
194            setResizeDimLayer(false, INVALID_STACK_ID, 0f);
195        }
196    }
197
198    boolean wasVisible() {
199        return mLastVisibility;
200    }
201
202    void setAdjustedForIme(boolean adjusted, boolean animate, WindowState imeWin) {
203        if (mAdjustedForIme != adjusted) {
204            mAdjustedForIme = adjusted;
205            if (animate) {
206                startImeAdjustAnimation(adjusted, imeWin);
207            } else {
208
209                // Animation might be delayed, so only notify if we don't run an animation.
210                notifyAdjustedForImeChanged(adjusted, 0 /* duration */);
211            }
212        }
213    }
214
215    void positionDockedStackedDivider(Rect frame) {
216        TaskStack stack = mDisplayContent.getDockedStackLocked();
217        if (stack == null) {
218            // Unfortunately we might end up with still having a divider, even though the underlying
219            // stack was already removed. This is because we are on AM thread and the removal of the
220            // divider was deferred to WM thread and hasn't happened yet. In that case let's just
221            // keep putting it in the same place it was before the stack was removed to have
222            // continuity and prevent it from jumping to the center. It will get hidden soon.
223            frame.set(mLastRect);
224            return;
225        } else {
226            stack.getDimBounds(mTmpRect);
227        }
228        int side = stack.getDockSide();
229        switch (side) {
230            case DOCKED_LEFT:
231                frame.set(mTmpRect.right - mDividerInsets, frame.top,
232                        mTmpRect.right + frame.width() - mDividerInsets, frame.bottom);
233                break;
234            case DOCKED_TOP:
235                frame.set(frame.left, mTmpRect.bottom - mDividerInsets,
236                        mTmpRect.right, mTmpRect.bottom + frame.height() - mDividerInsets);
237                break;
238            case DOCKED_RIGHT:
239                frame.set(mTmpRect.left - frame.width() + mDividerInsets, frame.top,
240                        mTmpRect.left + mDividerInsets, frame.bottom);
241                break;
242            case DOCKED_BOTTOM:
243                frame.set(frame.left, mTmpRect.top - frame.height() + mDividerInsets,
244                        frame.right, mTmpRect.top + mDividerInsets);
245                break;
246        }
247        mLastRect.set(frame);
248    }
249
250    void notifyDockedDividerVisibilityChanged(boolean visible) {
251        final int size = mDockedStackListeners.beginBroadcast();
252        for (int i = 0; i < size; ++i) {
253            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
254            try {
255                listener.onDividerVisibilityChanged(visible);
256            } catch (RemoteException e) {
257                Slog.e(TAG_WM, "Error delivering divider visibility changed event.", e);
258            }
259        }
260        mDockedStackListeners.finishBroadcast();
261    }
262
263    void notifyDockedStackExistsChanged(boolean exists) {
264        final int size = mDockedStackListeners.beginBroadcast();
265        for (int i = 0; i < size; ++i) {
266            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
267            try {
268                listener.onDockedStackExistsChanged(exists);
269            } catch (RemoteException e) {
270                Slog.e(TAG_WM, "Error delivering docked stack exists changed event.", e);
271            }
272        }
273        mDockedStackListeners.finishBroadcast();
274        if (!exists) {
275            setMinimizedDockedStack(false);
276        }
277    }
278
279    void notifyDockedStackMinimizedChanged(boolean minimizedDock, long animDuration) {
280        mService.mH.removeMessages(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED);
281        mService.mH.obtainMessage(NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED,
282                minimizedDock ? 1 : 0, 0).sendToTarget();
283        final int size = mDockedStackListeners.beginBroadcast();
284        for (int i = 0; i < size; ++i) {
285            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
286            try {
287                listener.onDockedStackMinimizedChanged(minimizedDock, animDuration);
288            } catch (RemoteException e) {
289                Slog.e(TAG_WM, "Error delivering minimized dock changed event.", e);
290            }
291        }
292        mDockedStackListeners.finishBroadcast();
293    }
294
295    void notifyDockSideChanged(int newDockSide) {
296        final int size = mDockedStackListeners.beginBroadcast();
297        for (int i = 0; i < size; ++i) {
298            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
299            try {
300                listener.onDockSideChanged(newDockSide);
301            } catch (RemoteException e) {
302                Slog.e(TAG_WM, "Error delivering dock side changed event.", e);
303            }
304        }
305        mDockedStackListeners.finishBroadcast();
306    }
307
308    void notifyAdjustedForImeChanged(boolean adjustedForIme, long animDuration) {
309        final int size = mDockedStackListeners.beginBroadcast();
310        for (int i = 0; i < size; ++i) {
311            final IDockedStackListener listener = mDockedStackListeners.getBroadcastItem(i);
312            try {
313                listener.onAdjustedForImeChanged(adjustedForIme, animDuration);
314            } catch (RemoteException e) {
315                Slog.e(TAG_WM, "Error delivering adjusted for ime changed event.", e);
316            }
317        }
318        mDockedStackListeners.finishBroadcast();
319    }
320
321    void registerDockedStackListener(IDockedStackListener listener) {
322        mDockedStackListeners.register(listener);
323        notifyDockedDividerVisibilityChanged(wasVisible());
324        notifyDockedStackExistsChanged(
325                mDisplayContent.mService.mStackIdToStack.get(DOCKED_STACK_ID) != null);
326        notifyDockedStackMinimizedChanged(mMinimizedDock, 0 /* animDuration */);
327        notifyAdjustedForImeChanged(mAdjustedForIme, 0 /* animDuration */);
328
329    }
330
331    void setResizeDimLayer(boolean visible, int targetStackId, float alpha) {
332        SurfaceControl.openTransaction();
333        final TaskStack stack = mDisplayContent.mService.mStackIdToStack.get(targetStackId);
334        final TaskStack dockedStack = mDisplayContent.getDockedStackLocked();
335        boolean visibleAndValid = visible && stack != null && dockedStack != null;
336        if (visibleAndValid) {
337            stack.getDimBounds(mTmpRect);
338            if (mTmpRect.height() > 0 && mTmpRect.width() > 0) {
339                mDimLayer.setBounds(mTmpRect);
340                mDimLayer.show(mDisplayContent.mService.mLayersController.getResizeDimLayer(),
341                        alpha, 0 /* duration */);
342            } else {
343                visibleAndValid = false;
344            }
345        }
346        if (!visibleAndValid) {
347            mDimLayer.hide();
348        }
349        SurfaceControl.closeTransaction();
350    }
351
352    /**
353     * Notifies the docked stack divider controller of a visibility change that happens without
354     * an animation.
355     */
356    void notifyAppVisibilityChanged(AppWindowToken wtoken, boolean visible) {
357        final Task task = wtoken.mTask;
358        if (!task.isHomeTask() || !task.isVisibleForUser()) {
359            return;
360        }
361
362        // If the app that having visibility change is not the top visible one in the task,
363        // it does not affect whether the docked stack is minimized, ignore it.
364        if (task.getTopVisibleAppToken() == null || task.getTopVisibleAppToken() != wtoken) {
365            return;
366        }
367
368        // If the stack is completely offscreen, this might just be an intermediate state when
369        // docking a task/launching recents at the same time, but home doesn't actually get
370        // visible after the state settles in.
371        if (isWithinDisplay(task)
372                && mDisplayContent.getDockedStackVisibleForUserLocked() != null) {
373            setMinimizedDockedStack(visible, false /* animate */);
374        }
375    }
376
377    void notifyAppTransitionStarting(ArraySet<AppWindowToken> openingApps,
378            ArraySet<AppWindowToken> closingApps) {
379        if (containsHomeTaskWithinDisplay(openingApps)) {
380            setMinimizedDockedStack(true /* minimized */, true /* animate */);
381        } else if (containsHomeTaskWithinDisplay(closingApps)) {
382            setMinimizedDockedStack(false /* minimized */, true /* animate */);
383        }
384    }
385
386    private boolean containsHomeTaskWithinDisplay(ArraySet<AppWindowToken> apps) {
387        for (int i = apps.size() - 1; i >= 0; i--) {
388            final Task task = apps.valueAt(i).mTask;
389            if (task != null && task.isHomeTask()) {
390                return isWithinDisplay(task);
391            }
392        }
393        return false;
394    }
395
396    private boolean isWithinDisplay(Task task) {
397        task.mStack.getBounds(mTmpRect);
398        mDisplayContent.getLogicalDisplayRect(mTmpRect2);
399        return mTmpRect.intersect(mTmpRect2);
400    }
401
402    /**
403     * Sets whether the docked stack is currently in a minimized state, i.e. all the tasks in the
404     * docked stack are heavily clipped so you can only see a minimal peek state.
405     *
406     * @param minimizedDock Whether the docked stack is currently minimized.
407     * @param animate Whether to animate the change.
408     */
409    private void setMinimizedDockedStack(boolean minimizedDock, boolean animate) {
410        final boolean wasMinimized = mMinimizedDock;
411        mMinimizedDock = minimizedDock;
412        if (minimizedDock == wasMinimized
413                || mDisplayContent.getDockedStackVisibleForUserLocked() == null) {
414            return;
415        }
416
417        clearImeAdjustAnimation();
418        if (minimizedDock) {
419            if (animate) {
420                startAdjustAnimation(0f, 1f);
421            } else {
422                setMinimizedDockedStack(true);
423            }
424        } else {
425            if (animate) {
426                startAdjustAnimation(1f, 0f);
427            } else {
428                setMinimizedDockedStack(false);
429            }
430        }
431    }
432
433    private void clearImeAdjustAnimation() {
434        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
435        for (int i = stacks.size() - 1; i >= 0; --i) {
436            final TaskStack stack = stacks.get(i);
437            if (stack != null && stack.isAdjustedForIme()) {
438                stack.resetAdjustedForIme(true /* adjustBoundsNow */);
439            }
440        }
441        mAnimatingForIme = false;
442    }
443
444    private void startAdjustAnimation(float from, float to) {
445        mAnimatingForMinimizedDockedStack = true;
446        mAnimationStarted = false;
447        mAnimationStart = from;
448        mAnimationTarget = to;
449    }
450
451    private void startImeAdjustAnimation(boolean adjusted, WindowState imeWin) {
452        mAnimatingForIme = true;
453        mAnimationStarted = false;
454        mAnimationStart = adjusted ? 0 : 1;
455        mAnimationTarget = adjusted ? 1 : 0;
456
457        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
458        for (int i = stacks.size() - 1; i >= 0; --i) {
459            final TaskStack stack = stacks.get(i);
460            if (stack.isVisibleLocked() && stack.isAdjustedForIme()) {
461                stack.beginImeAdjustAnimation();
462            }
463        }
464
465        // We put all tasks into drag resizing mode - wait until all of them have completed the
466        // drag resizing switch.
467        if (!mService.mWaitingForDrawn.isEmpty()) {
468            mService.mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT);
469            mService.mH.sendEmptyMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT,
470                    IME_ADJUST_DRAWN_TIMEOUT);
471            mAnimationStartDelayed = true;
472            if (imeWin != null) {
473
474                // There might be an old window delaying the animation start - clear it.
475                if (mDelayedImeWin != null) {
476                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
477                }
478                mDelayedImeWin = imeWin;
479                imeWin.mWinAnimator.startDelayingAnimationStart();
480            }
481            mService.mWaitingForDrawnCallback = () -> {
482                mAnimationStartDelayed = false;
483                if (mDelayedImeWin != null) {
484                    mDelayedImeWin.mWinAnimator.endDelayingAnimationStart();
485                }
486                notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION);
487            };
488        } else {
489            notifyAdjustedForImeChanged(adjusted, IME_ADJUST_ANIM_DURATION);
490        }
491    }
492
493    private void setMinimizedDockedStack(boolean minimized) {
494        final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
495        notifyDockedStackMinimizedChanged(minimized, 0);
496        if (stack == null) {
497            return;
498        }
499        if (stack.setAdjustedForMinimizedDock(minimized ? 1f : 0f)) {
500            mService.mWindowPlacerLocked.performSurfacePlacement();
501        }
502    }
503
504    private boolean isAnimationMaximizing() {
505        return mAnimationTarget == 0f;
506    }
507
508    public boolean animate(long now) {
509        if (mAnimatingForMinimizedDockedStack) {
510            return animateForMinimizedDockedStack(now);
511        } else if (mAnimatingForIme) {
512            return animateForIme(now);
513        } else {
514            return false;
515        }
516    }
517
518    private boolean animateForIme(long now) {
519        if (!mAnimationStarted || mAnimationStartDelayed) {
520            mAnimationStarted = true;
521            mAnimationStartTime = now;
522            mAnimationDuration = (long)
523                    (IME_ADJUST_ANIM_DURATION * mService.getWindowAnimationScaleLocked());
524        }
525        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
526        t = (mAnimationTarget == 1f ? IME_ADJUST_ENTRY_INTERPOLATOR : TOUCH_RESPONSE_INTERPOLATOR)
527                .getInterpolation(t);
528        final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
529        boolean updated = false;
530        for (int i = stacks.size() - 1; i >= 0; --i) {
531            final TaskStack stack = stacks.get(i);
532            if (stack != null && stack.isAdjustedForIme()) {
533                if (t >= 1f && mAnimationTarget == 0f) {
534                    stack.resetAdjustedForIme(true /* adjustBoundsNow */);
535                    updated = true;
536                } else {
537                    updated |= stack.updateAdjustForIme(getInterpolatedAnimationValue(t),
538                            false /* force */);
539                }
540                if (t >= 1f) {
541                    stack.endImeAdjustAnimation();
542                }
543            }
544        }
545        if (updated) {
546            mService.mWindowPlacerLocked.performSurfacePlacement();
547        }
548        if (t >= 1.0f) {
549            mAnimatingForIme = false;
550            return false;
551        } else {
552            return true;
553        }
554    }
555
556    private boolean animateForMinimizedDockedStack(long now) {
557        final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
558        if (!mAnimationStarted) {
559            mAnimationStarted = true;
560            mAnimationStartTime = now;
561            final long transitionDuration = isAnimationMaximizing()
562                    ? mService.mAppTransition.getLastClipRevealTransitionDuration()
563                    : DEFAULT_APP_TRANSITION_DURATION;
564            mAnimationDuration = (long)
565                    (transitionDuration * mService.getTransitionAnimationScaleLocked());
566            mMaximizeMeetFraction = getClipRevealMeetFraction(stack);
567            notifyDockedStackMinimizedChanged(mMinimizedDock,
568                    (long) (mAnimationDuration * mMaximizeMeetFraction));
569        }
570        float t = Math.min(1f, (float) (now - mAnimationStartTime) / mAnimationDuration);
571        t = (isAnimationMaximizing() ? TOUCH_RESPONSE_INTERPOLATOR : mMinimizedDockInterpolator)
572                .getInterpolation(t);
573        if (stack != null) {
574            if (stack.setAdjustedForMinimizedDock(getMinimizeAmount(stack, t))) {
575                mService.mWindowPlacerLocked.performSurfacePlacement();
576            }
577        }
578        if (t >= 1.0f) {
579            mAnimatingForMinimizedDockedStack = false;
580            return false;
581        } else {
582            return true;
583        }
584    }
585
586    private float getInterpolatedAnimationValue(float t) {
587        return t * mAnimationTarget + (1 - t) * mAnimationStart;
588    }
589
590    /**
591     * Gets the amount how much to minimize a stack depending on the interpolated fraction t.
592     */
593    private float getMinimizeAmount(TaskStack stack, float t) {
594        final float naturalAmount = getInterpolatedAnimationValue(t);
595        if (isAnimationMaximizing()) {
596            return adjustMaximizeAmount(stack, t, naturalAmount);
597        } else {
598            return naturalAmount;
599        }
600    }
601
602    /**
603     * When maximizing the stack during a clip reveal transition, this adjusts the minimize amount
604     * during the transition such that the edge of the clip reveal rect is met earlier in the
605     * transition so we don't create a visible "hole", but only if both the clip reveal and the
606     * docked stack divider start from about the same portion on the screen.
607     */
608    private float adjustMaximizeAmount(TaskStack stack, float t, float naturalAmount) {
609        if (mMaximizeMeetFraction == 1f) {
610            return naturalAmount;
611        }
612        final int minimizeDistance = stack.getMinimizeDistance();
613        float startPrime = mService.mAppTransition.getLastClipRevealMaxTranslation()
614                / (float) minimizeDistance;
615        final float amountPrime = t * mAnimationTarget + (1 - t) * startPrime;
616        final float t2 = Math.min(t / mMaximizeMeetFraction, 1);
617        return amountPrime * t2 + naturalAmount * (1 - t2);
618    }
619
620    /**
621     * Retrieves the animation fraction at which the docked stack has to meet the clip reveal
622     * edge. See {@link #adjustMaximizeAmount}.
623     */
624    private float getClipRevealMeetFraction(TaskStack stack) {
625        if (!isAnimationMaximizing() || stack == null ||
626                !mService.mAppTransition.hadClipRevealAnimation()) {
627            return 1f;
628        }
629        final int minimizeDistance = stack.getMinimizeDistance();
630        final float fraction = Math.abs(mService.mAppTransition.getLastClipRevealMaxTranslation())
631                / (float) minimizeDistance;
632        final float t = Math.max(0, Math.min(1, (fraction - CLIP_REVEAL_MEET_FRACTION_MIN)
633                / (CLIP_REVEAL_MEET_FRACTION_MAX - CLIP_REVEAL_MEET_FRACTION_MIN)));
634        return CLIP_REVEAL_MEET_EARLIEST
635                + (1 - t) * (CLIP_REVEAL_MEET_LAST - CLIP_REVEAL_MEET_EARLIEST);
636    }
637
638    @Override
639    public boolean isFullscreen() {
640        return false;
641    }
642
643    @Override
644    public DisplayInfo getDisplayInfo() {
645        return mDisplayContent.getDisplayInfo();
646    }
647
648    @Override
649    public void getDimBounds(Rect outBounds) {
650        // This dim layer user doesn't need this.
651    }
652
653    @Override
654    public String toShortString() {
655        return TAG;
656    }
657}