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