• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.systemui.statusbar.notification.row;
18 
19 import android.animation.AnimatorListenerAdapter;
20 import android.content.Context;
21 import android.util.AttributeSet;
22 import android.view.View;
23 import android.view.animation.Interpolator;
24 
25 import com.android.internal.annotations.VisibleForTesting;
26 import com.android.systemui.animation.Interpolators;
27 
28 /**
29  * A common base class for all views in the notification stack scroller which don't have a
30  * background.
31  */
32 public abstract class StackScrollerDecorView extends ExpandableView {
33 
34     protected View mContent;
35     protected View mSecondaryView;
36     private boolean mIsVisible = true;
37     private boolean mContentVisible = true;
38     private boolean mIsSecondaryVisible = true;
39     private int mDuration = 260;
40     private boolean mContentAnimating;
41     private final Runnable mContentVisibilityEndRunnable = () -> {
42         mContentAnimating = false;
43         if (getVisibility() != View.GONE && !mIsVisible) {
44             setVisibility(GONE);
45             setWillBeGone(false);
46             notifyHeightChanged(false /* needsAnimation */);
47         }
48     };
49 
50     private boolean mSecondaryAnimating = false;
51     private final Runnable mSecondaryVisibilityEndRunnable = () -> {
52         mSecondaryAnimating = false;
53         // If we were on screen, become GONE to avoid touches
54         if (mSecondaryView == null) return;
55         if (getVisibility() != View.GONE
56                 && mSecondaryView.getVisibility() != View.GONE
57                 && !mIsSecondaryVisible) {
58             mSecondaryView.setVisibility(View.GONE);
59         }
60     };
61 
StackScrollerDecorView(Context context, AttributeSet attrs)62     public StackScrollerDecorView(Context context, AttributeSet attrs) {
63         super(context, attrs);
64         setClipChildren(false);
65     }
66 
67     @Override
onFinishInflate()68     protected void onFinishInflate() {
69         super.onFinishInflate();
70         mContent = findContentView();
71         mSecondaryView = findSecondaryView();
72         setVisible(false /* nowVisible */, false /* animate */);
73         setSecondaryVisible(false /* nowVisible */, false /* animate */);
74     }
75 
76     @Override
onLayout(boolean changed, int left, int top, int right, int bottom)77     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
78         super.onLayout(changed, left, top, right, bottom);
79         setOutlineProvider(null);
80     }
81 
82     @Override
isTransparent()83     public boolean isTransparent() {
84         return true;
85     }
86 
87     /**
88      * Set the content of this view to be visible in an animated way.
89      *
90      * @param contentVisible True if the content should be visible or false if it should be hidden.
91      */
setContentVisible(boolean contentVisible)92     public void setContentVisible(boolean contentVisible) {
93         setContentVisible(contentVisible, true /* animate */);
94     }
95     /**
96      * Set the content of this view to be visible.
97      * @param contentVisible True if the content should be visible or false if it should be hidden.
98      * @param animate Should an animation be performed.
99      */
setContentVisible(boolean contentVisible, boolean animate)100     private void setContentVisible(boolean contentVisible, boolean animate) {
101         if (mContentVisible != contentVisible) {
102             mContentAnimating = animate;
103             mContentVisible = contentVisible;
104             setViewVisible(mContent, contentVisible, animate, mContentVisibilityEndRunnable);
105         }
106 
107         if (!mContentAnimating) {
108             mContentVisibilityEndRunnable.run();
109         }
110     }
111 
isContentVisible()112     public boolean isContentVisible() {
113         return mContentVisible;
114     }
115 
116     /**
117      * Make this view visible. If {@code false} is passed, the view will fade out it's content
118      * and set the view Visibility to GONE. If only the content should be changed
119      * {@link #setContentVisible(boolean)} can be used.
120      *
121      * @param nowVisible should the view be visible
122      * @param animate should the change be animated.
123      */
setVisible(boolean nowVisible, boolean animate)124     public void setVisible(boolean nowVisible, boolean animate) {
125         if (mIsVisible != nowVisible) {
126             mIsVisible = nowVisible;
127             if (animate) {
128                 if (nowVisible) {
129                     setVisibility(VISIBLE);
130                     setWillBeGone(false);
131                     notifyHeightChanged(false /* needsAnimation */);
132                 } else {
133                     setWillBeGone(true);
134                 }
135                 setContentVisible(nowVisible, true /* animate */);
136             } else {
137                 setVisibility(nowVisible ? VISIBLE : GONE);
138                 setContentVisible(nowVisible, false /* animate */);
139                 setWillBeGone(false);
140                 notifyHeightChanged(false /* needsAnimation */);
141             }
142         }
143     }
144 
145     /**
146      * Set the secondary view of this layout to visible.
147      *
148      * @param nowVisible should the secondary view be visible
149      * @param animate should the change be animated
150      */
setSecondaryVisible(boolean nowVisible, boolean animate)151     public void setSecondaryVisible(boolean nowVisible, boolean animate) {
152         if (mIsSecondaryVisible != nowVisible) {
153             mSecondaryAnimating = animate;
154             mIsSecondaryVisible = nowVisible;
155             setViewVisible(mSecondaryView, nowVisible, animate, mSecondaryVisibilityEndRunnable);
156         }
157 
158         if (!mSecondaryAnimating) {
159             mSecondaryVisibilityEndRunnable.run();
160         }
161     }
162 
163     @VisibleForTesting
isSecondaryVisible()164     boolean isSecondaryVisible() {
165         return mIsSecondaryVisible;
166     }
167 
168     /**
169      * Is this view visible. If a view is currently animating to gone, it will
170      * return {@code false}.
171      */
isVisible()172     public boolean isVisible() {
173         return mIsVisible;
174     }
175 
setDuration(int duration)176     void setDuration(int duration) {
177         mDuration = duration;
178     }
179 
180     /**
181      * Animate a view to a new visibility.
182      * @param view Target view, maybe content view or dismiss view.
183      * @param nowVisible Should it now be visible.
184      * @param animate Should this be done in an animated way.
185      * @param endRunnable A runnable that is run when the animation is done.
186      */
setViewVisible(View view, boolean nowVisible, boolean animate, Runnable endRunnable)187     private void setViewVisible(View view, boolean nowVisible,
188             boolean animate, Runnable endRunnable) {
189         if (view == null) {
190             return;
191         }
192 
193         // Make sure we're visible so animations work
194         if (view.getVisibility() != View.VISIBLE) {
195             view.setVisibility(View.VISIBLE);
196         }
197 
198         // cancel any previous animations
199         view.animate().cancel();
200         float endValue = nowVisible ? 1.0f : 0.0f;
201         if (!animate) {
202             view.setAlpha(endValue);
203             if (endRunnable != null) {
204                 endRunnable.run();
205             }
206             return;
207         }
208 
209         // Animate the view alpha
210         Interpolator interpolator = nowVisible ? Interpolators.ALPHA_IN : Interpolators.ALPHA_OUT;
211         view.animate()
212                 .alpha(endValue)
213                 .setInterpolator(interpolator)
214                 .setDuration(mDuration)
215                 .withEndAction(endRunnable);
216     }
217 
218     @Override
performRemoveAnimation(long duration, long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation, Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener)219     public long performRemoveAnimation(long duration, long delay,
220             float translationDirection, boolean isHeadsUpAnimation, float endLocation,
221             Runnable onFinishedRunnable,
222             AnimatorListenerAdapter animationListener) {
223         // TODO: Use duration
224         setContentVisible(false);
225         return 0;
226     }
227 
228     @Override
performAddAnimation(long delay, long duration, boolean isHeadsUpAppear)229     public void performAddAnimation(long delay, long duration, boolean isHeadsUpAppear) {
230         // TODO: use delay and duration
231         setContentVisible(true);
232     }
233 
234     @Override
needsClippingToShelf()235     public boolean needsClippingToShelf() {
236         return false;
237     }
238 
239     @Override
hasOverlappingRendering()240     public boolean hasOverlappingRendering() {
241         return false;
242     }
243 
findContentView()244     protected abstract View findContentView();
245 
246     /**
247      * Returns a view that might not always appear while the main content view is still visible.
248      */
findSecondaryView()249     protected abstract View findSecondaryView();
250 }
251