1/*
2 * Copyright (C) 2014 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.systemui.recents.views;
18
19import android.content.Context;
20import android.view.View;
21
22import com.android.systemui.Interpolators;
23import com.android.systemui.R;
24import com.android.systemui.recents.Recents;
25import com.android.systemui.recents.RecentsActivity;
26import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
27import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
28import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
29import com.android.systemui.recents.events.ui.DismissAllTaskViewsEvent;
30import com.android.systemui.recents.events.activity.MultiWindowStateChangedEvent;
31import com.android.systemui.recents.events.ui.dragndrop.DragEndCancelledEvent;
32import com.android.systemui.recents.events.ui.dragndrop.DragEndEvent;
33import com.android.systemui.recents.model.TaskStack;
34
35/** Manages the scrims for the various system bars. */
36public class SystemBarScrimViews {
37
38    private static final int DEFAULT_ANIMATION_DURATION = 150;
39
40    private Context mContext;
41
42    private View mNavBarScrimView;
43
44    private boolean mHasNavBarScrim;
45    private boolean mShouldAnimateNavBarScrim;
46    private boolean mHasTransposedNavBar;
47    private boolean mHasDockedTasks;
48    private int mNavBarScrimEnterDuration;
49
50    public SystemBarScrimViews(RecentsActivity activity) {
51        mContext = activity;
52        mNavBarScrimView = activity.findViewById(R.id.nav_bar_scrim);
53        mNavBarScrimView.forceHasOverlappingRendering(false);
54        mNavBarScrimEnterDuration = activity.getResources().getInteger(
55                R.integer.recents_nav_bar_scrim_enter_duration);
56        mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavigationBar();
57        mHasDockedTasks = Recents.getSystemServices().hasDockedTask();
58    }
59
60    /**
61     * Updates the nav bar scrim.
62     */
63    public void updateNavBarScrim(boolean animateNavBarScrim, boolean hasStackTasks,
64            AnimationProps animation) {
65        prepareEnterRecentsAnimation(isNavBarScrimRequired(hasStackTasks), animateNavBarScrim);
66        if (animateNavBarScrim && animation != null) {
67            animateNavBarScrimVisibility(true, animation);
68        }
69    }
70
71    /**
72     * Prepares the scrim views for animating when entering Recents. This will be called before
73     * the first draw, unless we are updating the scrim on configuration change.
74     */
75    private void prepareEnterRecentsAnimation(boolean hasNavBarScrim, boolean animateNavBarScrim) {
76        mHasNavBarScrim = hasNavBarScrim;
77        mShouldAnimateNavBarScrim = animateNavBarScrim;
78
79        mNavBarScrimView.setVisibility(mHasNavBarScrim && !mShouldAnimateNavBarScrim ?
80                View.VISIBLE : View.INVISIBLE);
81    }
82
83    /**
84     * Animates the nav bar scrim visibility.
85     */
86    private void animateNavBarScrimVisibility(boolean visible, AnimationProps animation) {
87        int toY = 0;
88        if (visible) {
89            mNavBarScrimView.setVisibility(View.VISIBLE);
90            mNavBarScrimView.setTranslationY(mNavBarScrimView.getMeasuredHeight());
91        } else {
92            toY = mNavBarScrimView.getMeasuredHeight();
93        }
94        if (animation != AnimationProps.IMMEDIATE) {
95            mNavBarScrimView.animate()
96                    .translationY(toY)
97                    .setDuration(animation.getDuration(AnimationProps.BOUNDS))
98                    .setInterpolator(animation.getInterpolator(AnimationProps.BOUNDS))
99                    .start();
100        } else {
101            mNavBarScrimView.setTranslationY(toY);
102        }
103    }
104
105    /**
106     * @return Whether to show the nav bar scrim.
107     */
108    private boolean isNavBarScrimRequired(boolean hasStackTasks) {
109        return hasStackTasks && !mHasTransposedNavBar && !mHasDockedTasks;
110    }
111
112    /**** EventBus events ****/
113
114    /**
115     * Starts animating the scrim views when entering Recents.
116     */
117    public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
118        if (mHasNavBarScrim) {
119            AnimationProps animation = mShouldAnimateNavBarScrim
120                    ? new AnimationProps()
121                            .setDuration(AnimationProps.BOUNDS, mNavBarScrimEnterDuration)
122                            .setInterpolator(AnimationProps.BOUNDS, Interpolators.DECELERATE_QUINT)
123                    : AnimationProps.IMMEDIATE;
124            animateNavBarScrimVisibility(true, animation);
125        }
126    }
127
128    /**
129     * Starts animating the scrim views when leaving Recents (either via launching a task, or
130     * going home).
131     */
132    public final void onBusEvent(DismissRecentsToHomeAnimationStarted event) {
133        if (mHasNavBarScrim) {
134            AnimationProps animation = createBoundsAnimation(
135                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
136            animateNavBarScrimVisibility(false, animation);
137        }
138    }
139
140    public final void onBusEvent(DismissAllTaskViewsEvent event) {
141        if (mHasNavBarScrim) {
142            AnimationProps animation = createBoundsAnimation(
143                    TaskStackAnimationHelper.EXIT_TO_HOME_TRANSLATION_DURATION);
144            animateNavBarScrimVisibility(false, animation);
145        }
146    }
147
148    public final void onBusEvent(ConfigurationChangedEvent event) {
149        if (event.fromDeviceOrientationChange) {
150            mHasNavBarScrim = Recents.getSystemServices().hasTransposedNavigationBar();
151        }
152        animateScrimToCurrentNavBarState(event.hasStackTasks);
153    }
154
155    public final void onBusEvent(MultiWindowStateChangedEvent event) {
156        mHasDockedTasks = event.inMultiWindow;
157        animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0);
158    }
159
160    public final void onBusEvent(final DragEndEvent event) {
161        // Hide the nav bar scrims once we drop to a dock region
162        if (event.dropTarget instanceof TaskStack.DockState) {
163            animateScrimToCurrentNavBarState(false /* hasStackTasks */);
164        }
165    }
166
167    public final void onBusEvent(final DragEndCancelledEvent event) {
168        // Restore the scrims to the normal state
169        animateScrimToCurrentNavBarState(event.stack.getStackTaskCount() > 0);
170    }
171
172    /**
173     * Animates the scrim to match the state of the current nav bar.
174     */
175    private void animateScrimToCurrentNavBarState(boolean hasStackTasks) {
176        boolean hasNavBarScrim = isNavBarScrimRequired(hasStackTasks);
177        if (mHasNavBarScrim != hasNavBarScrim) {
178            AnimationProps animation = hasNavBarScrim
179                    ? createBoundsAnimation(DEFAULT_ANIMATION_DURATION)
180                    : AnimationProps.IMMEDIATE;
181            animateNavBarScrimVisibility(hasNavBarScrim, animation);
182        }
183        mHasNavBarScrim = hasNavBarScrim;
184    }
185
186    /**
187     * @return a default animation to aniamte the bounds of the scrim.
188     */
189    private AnimationProps createBoundsAnimation(int duration) {
190        return new AnimationProps()
191                .setDuration(AnimationProps.BOUNDS, duration)
192                .setInterpolator(AnimationProps.BOUNDS, Interpolators.FAST_OUT_SLOW_IN);
193    }
194}
195