• 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 
29 import java.util.List;
30 import java.util.WeakHashMap;
31 
32 /**
33  * A state of a {@link com.android.systemui.statusbar.stack.NotificationStackScrollLayout} which
34  * can be applied to a viewGroup.
35  */
36 public class StackScrollState {
37 
38     private static final String CHILD_NOT_FOUND_TAG = "StackScrollStateNoSuchChild";
39 
40     private final ViewGroup mHostView;
41     private WeakHashMap<ExpandableView, StackViewState> mStateMap;
42     private final int mClearAllTopPadding;
43 
StackScrollState(ViewGroup hostView)44     public StackScrollState(ViewGroup hostView) {
45         mHostView = hostView;
46         mStateMap = new WeakHashMap<>();
47         mClearAllTopPadding = hostView.getContext().getResources().getDimensionPixelSize(
48                 R.dimen.clear_all_padding_top);
49     }
50 
getHostView()51     public ViewGroup getHostView() {
52         return mHostView;
53     }
54 
resetViewStates()55     public void resetViewStates() {
56         int numChildren = mHostView.getChildCount();
57         for (int i = 0; i < numChildren; i++) {
58             ExpandableView child = (ExpandableView) mHostView.getChildAt(i);
59             resetViewState(child);
60 
61             // handling reset for child notifications
62             if (child instanceof ExpandableNotificationRow) {
63                 ExpandableNotificationRow row = (ExpandableNotificationRow) child;
64                 List<ExpandableNotificationRow> children =
65                         row.getNotificationChildren();
66                 if (row.isSummaryWithChildren() && children != null) {
67                     for (ExpandableNotificationRow childRow : children) {
68                         resetViewState(childRow);
69                     }
70                 }
71             }
72         }
73     }
74 
resetViewState(ExpandableView view)75     private void resetViewState(ExpandableView view) {
76         StackViewState viewState = mStateMap.get(view);
77         if (viewState == null) {
78             viewState = new StackViewState();
79             mStateMap.put(view, viewState);
80         }
81         // initialize with the default values of the view
82         viewState.height = view.getIntrinsicHeight();
83         viewState.gone = view.getVisibility() == View.GONE;
84         viewState.alpha = 1f;
85         viewState.shadowAlpha = 1f;
86         viewState.notGoneIndex = -1;
87         viewState.hidden = false;
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 DismissView) {
111                 DismissView dismissView = (DismissView) child;
112                 boolean visible = state.clipTopAmount < mClearAllTopPadding;
113                 dismissView.performVisibilityAnimation(visible && !dismissView.willBeGone());
114             } else if (child instanceof EmptyShadeView) {
115                 EmptyShadeView emptyShadeView = (EmptyShadeView) child;
116                 boolean visible = state.clipTopAmount <= 0;
117                 emptyShadeView.performVisibilityAnimation(
118                         visible && !emptyShadeView.willBeGone());
119             }
120         }
121     }
122 
123     /**
124      * Applies a  {@link StackViewState} to an  {@link ExpandableView}.
125      *
126      * @return whether the state was applied correctly
127      */
128     public boolean applyState(ExpandableView view, StackViewState state) {
129         if (state == null) {
130             Log.wtf(CHILD_NOT_FOUND_TAG, "No child state was found when applying this state " +
131                     "to the hostView");
132             return false;
133         }
134         if (state.gone) {
135             return false;
136         }
137         applyViewState(view, state);
138 
139         int height = view.getActualHeight();
140         int newHeight = state.height;
141 
142         // apply height
143         if (height != newHeight) {
144             view.setActualHeight(newHeight, false /* notifyListeners */);
145         }
146 
147         float shadowAlpha = view.getShadowAlpha();
148         float newShadowAlpha = state.shadowAlpha;
149 
150         // apply shadowAlpha
151         if (shadowAlpha != newShadowAlpha) {
152             view.setShadowAlpha(newShadowAlpha);
153         }
154 
155         // apply dimming
156         view.setDimmed(state.dimmed, false /* animate */);
157 
158         // apply hiding sensitive
159         view.setHideSensitive(
160                 state.hideSensitive, false /* animated */, 0 /* delay */, 0 /* duration */);
161 
162         // apply speed bump state
163         view.setBelowSpeedBump(state.belowSpeedBump);
164 
165         // apply dark
166         view.setDark(state.dark, false /* animate */, 0 /* delay */);
167 
168         // apply clipping
169         float oldClipTopAmount = view.getClipTopAmount();
170         if (oldClipTopAmount != state.clipTopAmount) {
171             view.setClipTopAmount(state.clipTopAmount);
172         }
173         if (view instanceof ExpandableNotificationRow) {
174             ExpandableNotificationRow row = (ExpandableNotificationRow) view;
175             if (state.isBottomClipped) {
176                 row.setClipToActualHeight(true);
177             }
178             row.applyChildrenState(this);
179         }
180         return true;
181     }
182 
183     /**
184      * Applies a  {@link ViewState} to a normal view.
185      */
186     public void applyViewState(View view, ViewState state) {
187         float alpha = view.getAlpha();
188         float yTranslation = view.getTranslationY();
189         float xTranslation = view.getTranslationX();
190         float zTranslation = view.getTranslationZ();
191         float newAlpha = state.alpha;
192         float newYTranslation = state.yTranslation;
193         float newZTranslation = state.zTranslation;
194         boolean becomesInvisible = newAlpha == 0.0f || state.hidden;
195         if (alpha != newAlpha && xTranslation == 0) {
196             // apply layer type
197             boolean becomesFullyVisible = newAlpha == 1.0f;
198             boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
199                     && view.hasOverlappingRendering();
200             int layerType = view.getLayerType();
201             int newLayerType = newLayerTypeIsHardware
202                     ? View.LAYER_TYPE_HARDWARE
203                     : View.LAYER_TYPE_NONE;
204             if (layerType != newLayerType) {
205                 view.setLayerType(newLayerType, null);
206             }
207 
208             // apply alpha
209             view.setAlpha(newAlpha);
210         }
211 
212         // apply visibility
213         int oldVisibility = view.getVisibility();
214         int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
215         if (newVisibility != oldVisibility) {
216             if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
217                 // We don't want views to change visibility when they are animating to GONE
218                 view.setVisibility(newVisibility);
219             }
220         }
221 
222         // apply yTranslation
223         if (yTranslation != newYTranslation) {
224             view.setTranslationY(newYTranslation);
225         }
226 
227         // apply zTranslation
228         if (zTranslation != newZTranslation) {
229             view.setTranslationZ(newZTranslation);
230         }
231     }
232 }
233