• 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.stack;
18 
19 import android.util.Log;
20 import android.view.View;
21 import android.view.ViewGroup;
22 
23 import com.android.systemui.R;
24 import com.android.systemui.statusbar.DismissView;
25 import com.android.systemui.statusbar.EmptyShadeView;
26 import com.android.systemui.statusbar.ExpandableNotificationRow;
27 import com.android.systemui.statusbar.ExpandableView;
28 import com.android.systemui.statusbar.SpeedBumpView;
29 
30 import java.util.HashMap;
31 import java.util.List;
32 import java.util.Map;
33 
34 /**
35  * A state of a {@link com.android.systemui.statusbar.stack.NotificationStackScrollLayout} which
36  * can be applied to a viewGroup.
37  */
38 public class StackScrollState {
39 
40     private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
41 
42     private final ViewGroup mHostView;
43     private Map<ExpandableView, StackViewState> mStateMap;
44     private final int mClearAllTopPadding;
45 
StackScrollState(ViewGroup hostView)46     public StackScrollState(ViewGroup hostView) {
47         mHostView = hostView;
48         mStateMap = new HashMap<ExpandableView, StackViewState>();
49         mClearAllTopPadding = hostView.getContext().getResources().getDimensionPixelSize(
50                 R.dimen.clear_all_padding_top);
51     }
52 
getHostView()53     public ViewGroup getHostView() {
54         return mHostView;
55     }
56 
resetViewStates()57     public void resetViewStates() {
58         int numChildren = mHostView.getChildCount();
59         for (int i = 0; i < numChildren; i++) {
60             ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
61             resetViewState(child);
62 
63             // handling reset for child notifications
64             if (child instanceof ExpandableNotificationRow) {
65                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
66                 List<ExpandableNotificationRow> children =
67                         row.getNotificationChildren();
68                 if (row.areChildrenExpanded() && children != null) {
69                     for (ExpandableNotificationRow childRow : children) {
70                         resetViewState(childRow);
71                     }
72                 }
73             }
74         }
75     }
76 
resetViewState(ExpandableView view)77     private void resetViewState(ExpandableView view) {
78         StackViewState viewState = mStateMap.get(view);
79         if (viewState == null) {
80             viewState = new StackViewState();
81             mStateMap.put(view, viewState);
82         }
83         // initialize with the default values of the view
84         viewState.height = view.getIntrinsicHeight();
85         viewState.gone = view.getVisibility() == View.GONE;
86         viewState.alpha = 1;
87         viewState.notGoneIndex = -1;
88     }
89 
getViewStateForView(View requestedView)90     public StackViewState getViewStateForView(View requestedView) {
91         return mStateMap.get(requestedView);
92     }
93 
removeViewStateForView(View child)94     public void removeViewStateForView(View child) {
95         mStateMap.remove(child);
96     }
97 
98     /**
99      * Apply the properties saved in {@link #mStateMap} to the children of the {@link #mHostView}.
100      * The properties are only applied if they effectively changed.
101      */
apply()102     public void apply() {
103         int numChildren = mHostView.getChildCount();
104         for (int i = 0; i < numChildren; i++) {
105             ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
106             StackViewState state = mStateMap.get(child);
107             if (!applyState(child, state)) {
108                 continue;
109             }
110             if(child instanceof SpeedBumpView) {
111                 performSpeedBumpAnimation(i, (SpeedBumpView) child, state, 0);
112             } else if (child instanceof DismissView) {
113                 DismissView dismissView = (DismissView) child;
114                 boolean visible = state.topOverLap < mClearAllTopPadding;
115                 dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
116             } else if (child instanceof EmptyShadeView) {
117                 EmptyShadeView emptyShadeView = (EmptyShadeView) child;
118                 boolean visible = state.topOverLap <= 0;
119                 emptyShadeView.performVisibilityAnimation(
120                         visible && !emptyShadeView.willBeGone());
121             }
122         }
123     }
124 
125     /**
126      * Applies a  {@link StackViewState} to an  {@link ExpandableView}.
127      *
128      * @return whether the state was applied correctly
129      */
130     public boolean applyState(ExpandableView view, StackViewState state) {
131         if (state == null) {
132             Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
133                     "to the hostView");
134             return false;
135         }
136         if (state.gone) {
137             return false;
138         }
139         applyViewState(view, state);
140 
141         int height = view.getActualHeight();
142         int newHeight = state.height;
143 
144         // apply height
145         if (height != newHeight) {
146             view.setActualHeight(newHeight, false /* notifyListeners */);
147         }
148 
149         // apply dimming
150         view.setDimmed(state.dimmed, false /* animate */);
151 
152         // apply hiding sensitive
153         view.setHideSensitive(
154                 state.hideSensitive, false /* animated */, 0 /* delay */, 0 /* duration */);
155 
156         // apply speed bump state
157         view.setBelowSpeedBump(state.belowSpeedBump);
158 
159         // apply dark
160         view.setDark(state.dark, false /* animate */, 0 /* delay */);
161 
162         // apply clipping
163         float oldClipTopAmount = view.getClipTopAmount();
164         if (oldClipTopAmount != state.clipTopAmount) {
165             view.setClipTopAmount(state.clipTopAmount);
166         }
167         float oldClipTopOptimization = view.getClipTopOptimization();
168         if (oldClipTopOptimization != state.topOverLap) {
169             view.setClipTopOptimization(state.topOverLap);
170         }
171         if (view instanceof ExpandableNotificationRow) {
172             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
173             row.applyChildrenState(this);
174         }
175         return true;
176     }
177 
178     /**
179      * Applies a  {@link ViewState} to a normal view.
180      */
181     public void applyViewState(View view, ViewState state) {
182         float alpha = view.getAlpha();
183         float yTranslation = view.getTranslationY();
184         float xTranslation = view.getTranslationX();
185         float zTranslation = view.getTranslationZ();
186         float scale = view.getScaleX();
187         float newAlpha = state.alpha;
188         float newYTranslation = state.yTranslation;
189         float newZTranslation = state.zTranslation;
190         float newScale = state.scale;
191         boolean becomesInvisible = newAlpha == 0.0f;
192         if (alpha != newAlpha && xTranslation == 0) {
193             // apply layer type
194             boolean becomesFullyVisible = newAlpha == 1.0f;
195             boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
196                     && view.hasOverlappingRendering();
197             int layerType = view.getLayerType();
198             int newLayerType = newLayerTypeIsHardware
199                     ? View.LAYER_TYPE_HARDWARE
200                     : View.LAYER_TYPE_NONE;
201             if (layerType != newLayerType) {
202                 view.setLayerType(newLayerType, null);
203             }
204 
205             // apply alpha
206             view.setAlpha(newAlpha);
207         }
208 
209         // apply visibility
210         int oldVisibility = view.getVisibility();
211         int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
212         if (newVisibility != oldVisibility) {
213             if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
214                 // We don't want views to change visibility when they are animating to GONE
215                 view.setVisibility(newVisibility);
216             }
217         }
218 
219         // apply yTranslation
220         if (yTranslation != newYTranslation) {
221             view.setTranslationY(newYTranslation);
222         }
223 
224         // apply zTranslation
225         if (zTranslation != newZTranslation) {
226             view.setTranslationZ(newZTranslation);
227         }
228 
229         // apply scale
230         if (scale != newScale) {
231             view.setScaleX(newScale);
232             view.setScaleY(newScale);
233         }
234     }
235 
236     public void performSpeedBumpAnimation(int i, SpeedBumpView speedBump, StackViewState state,
237             long delay) {
238         View nextChild = getNextChildNotGone(i);
239         if (nextChild != null) {
240             float lineEnd = state.yTranslation + state.height / 2;
241             StackViewState nextState = getViewStateForView(nextChild);
242             boolean startIsAboveNext = nextState.yTranslation > lineEnd;
243             speedBump.animateDivider(startIsAboveNext, delay, null /* onFinishedRunnable */);
244         }
245     }
246 
247     private View getNextChildNotGone(int childIndex) {
248         int childCount = mHostView.getChildCount();
249         for (int i = childIndex + 1; i < childCount; i++) {
250             View child = mHostView.getChildAt(i);
251             if (child.getVisibility() != View.GONE) {
252                 return child;
253             }
254         }
255         return null;
256     }
257 
258 }
259