blob: c5ab9f6049c6134c8a9351f887712677ddbc64ce [file] [log] [blame]
Selim Cinek29aab962018-02-27 17:05:45 -08001/*
2 * Copyright (C) 2018 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
Rohan Shah20790b82018-07-02 17:21:04 -070017package com.android.systemui.statusbar.notification.stack;
Selim Cinek29aab962018-02-27 17:05:45 -080018
Dave Mankoffa4d195d2018-11-16 13:33:27 -050019import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.NUM_SECTIONS;
Selim Cinek29aab962018-02-27 17:05:45 -080020
Lucas Dupin00be88f2019-01-03 17:50:52 -080021import com.android.systemui.statusbar.AmbientPulseManager;
Ned Burnsf81c4c42019-01-07 14:10:43 -050022import com.android.systemui.statusbar.notification.collection.NotificationEntry;
Rohan Shah20790b82018-07-02 17:21:04 -070023import com.android.systemui.statusbar.notification.row.ActivatableNotificationView;
24import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
Dave Mankoffa4d195d2018-11-16 13:33:27 -050025import com.android.systemui.statusbar.notification.row.ExpandableView;
Selim Cinek29aab962018-02-27 17:05:45 -080026import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener;
27
28import java.util.HashSet;
29
Lucas Dupin00be88f2019-01-03 17:50:52 -080030import javax.inject.Inject;
31import javax.inject.Singleton;
32
Selim Cinek29aab962018-02-27 17:05:45 -080033/**
34 * A class that manages the roundness for notification views
35 */
Lucas Dupin00be88f2019-01-03 17:50:52 -080036@Singleton
37class NotificationRoundnessManager implements OnHeadsUpChangedListener,
38 AmbientPulseManager.OnAmbientChangedListener {
Selim Cinek29aab962018-02-27 17:05:45 -080039
Lucas Dupin00be88f2019-01-03 17:50:52 -080040 private final ActivatableNotificationView[] mFirstInSectionViews;
41 private final ActivatableNotificationView[] mLastInSectionViews;
42 private final ActivatableNotificationView[] mTmpFirstInSectionViews;
43 private final ActivatableNotificationView[] mTmpLastInSectionViews;
Selim Cinek29aab962018-02-27 17:05:45 -080044 private boolean mExpanded;
Dave Mankoffa4d195d2018-11-16 13:33:27 -050045 private HashSet<ExpandableView> mAnimatedChildren;
Selim Cinek29aab962018-02-27 17:05:45 -080046 private Runnable mRoundingChangedCallback;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080047 private ExpandableNotificationRow mTrackedHeadsUp;
Lucas Dupin00be88f2019-01-03 17:50:52 -080048 private ActivatableNotificationView mTrackedAmbient;
Selim Cinekaa9db1f2018-02-27 17:35:47 -080049 private float mAppearFraction;
Selim Cinek29aab962018-02-27 17:05:45 -080050
Lucas Dupin00be88f2019-01-03 17:50:52 -080051 @Inject
52 NotificationRoundnessManager(AmbientPulseManager ambientPulseManager) {
Gus Prevase2d6f042018-10-17 15:25:30 -040053 mFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
54 mLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
55 mTmpFirstInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
56 mTmpLastInSectionViews = new ActivatableNotificationView[NUM_SECTIONS];
Lucas Dupin00be88f2019-01-03 17:50:52 -080057 ambientPulseManager.addListener(this);
Gus Prevase2d6f042018-10-17 15:25:30 -040058 }
59
Selim Cinek29aab962018-02-27 17:05:45 -080060 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -050061 public void onHeadsUpPinned(NotificationEntry headsUp) {
Evan Laird94492852018-10-25 13:43:01 -040062 updateView(headsUp.getRow(), false /* animate */);
Selim Cinek29aab962018-02-27 17:05:45 -080063 }
64
65 @Override
Ned Burnsf81c4c42019-01-07 14:10:43 -050066 public void onHeadsUpUnPinned(NotificationEntry headsUp) {
Evan Laird94492852018-10-25 13:43:01 -040067 updateView(headsUp.getRow(), true /* animate */);
Selim Cinek29aab962018-02-27 17:05:45 -080068 }
69
Selim Cinek8875de12018-03-22 10:14:32 -070070 public void onHeadsupAnimatingAwayChanged(ExpandableNotificationRow row,
71 boolean isAnimatingAway) {
Gus Prevase2d6f042018-10-17 15:25:30 -040072 updateView(row, false /* animate */);
Selim Cinek8875de12018-03-22 10:14:32 -070073 }
74
Lucas Dupin00be88f2019-01-03 17:50:52 -080075 @Override
76 public void onAmbientStateChanged(NotificationEntry entry, boolean isPulsing) {
77 ActivatableNotificationView row = entry.getRow();
78 if (isPulsing) {
79 mTrackedAmbient = row;
80 } else if (mTrackedAmbient == row) {
81 mTrackedAmbient = null;
82 }
83 updateView(row, false /* animate */);
84 }
85
Gus Prevase2d6f042018-10-17 15:25:30 -040086 private void updateView(ActivatableNotificationView view, boolean animate) {
87 boolean changed = updateViewWithoutCallback(view, animate);
88 if (changed) {
Selim Cinek29aab962018-02-27 17:05:45 -080089 mRoundingChangedCallback.run();
90 }
91 }
92
Gus Prevase2d6f042018-10-17 15:25:30 -040093 private boolean updateViewWithoutCallback(ActivatableNotificationView view,
94 boolean animate) {
95 float topRoundness = getRoundness(view, true /* top */);
96 float bottomRoundness = getRoundness(view, false /* top */);
97 boolean topChanged = view.setTopRoundness(topRoundness, animate);
98 boolean bottomChanged = view.setBottomRoundness(bottomRoundness, animate);
99 boolean firstInSection = isFirstInSection(view, false /* exclude first section */);
100 boolean lastInSection = isLastInSection(view, false /* exclude last section */);
101 view.setFirstInSection(firstInSection);
102 view.setLastInSection(lastInSection);
103 return (firstInSection || lastInSection) && (topChanged || bottomChanged);
104 }
105
106 private boolean isFirstInSection(ActivatableNotificationView view,
107 boolean includeFirstSection) {
108 int numNonEmptySections = 0;
109 for (int i = 0; i < mFirstInSectionViews.length; i++) {
110 if (view == mFirstInSectionViews[i]) {
111 return includeFirstSection || numNonEmptySections > 0;
112 }
113 if (mFirstInSectionViews[i] != null) {
114 numNonEmptySections++;
115 }
116 }
117 return false;
118 }
119
120 private boolean isLastInSection(ActivatableNotificationView view, boolean includeLastSection) {
121 int numNonEmptySections = 0;
122 for (int i = mLastInSectionViews.length - 1; i >= 0; i--) {
123 if (view == mLastInSectionViews[i]) {
124 return includeLastSection || numNonEmptySections > 0;
125 }
126 if (mLastInSectionViews[i] != null) {
127 numNonEmptySections++;
128 }
129 }
130 return false;
131 }
132
Selim Cinek29aab962018-02-27 17:05:45 -0800133 private float getRoundness(ActivatableNotificationView view, boolean top) {
134 if ((view.isPinned() || view.isHeadsUpAnimatingAway()) && !mExpanded) {
135 return 1.0f;
136 }
Gus Prevase2d6f042018-10-17 15:25:30 -0400137 if (isFirstInSection(view, true /* include first section */) && top) {
Selim Cinek29aab962018-02-27 17:05:45 -0800138 return 1.0f;
139 }
Gus Prevase2d6f042018-10-17 15:25:30 -0400140 if (isLastInSection(view, true /* include last section */) && !top) {
Selim Cinek29aab962018-02-27 17:05:45 -0800141 return 1.0f;
142 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800143 if (view == mTrackedHeadsUp && mAppearFraction <= 0.0f) {
144 // If we're pushing up on a headsup the appear fraction is < 0 and it needs to still be
145 // rounded.
146 return 1.0f;
147 }
Lucas Dupin00be88f2019-01-03 17:50:52 -0800148 if (view == mTrackedAmbient) {
149 return 1.0f;
150 }
Selim Cinek29aab962018-02-27 17:05:45 -0800151 return 0.0f;
152 }
153
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800154 public void setExpanded(float expandedHeight, float appearFraction) {
155 mExpanded = expandedHeight != 0.0f;
156 mAppearFraction = appearFraction;
157 if (mTrackedHeadsUp != null) {
Gus Prevase2d6f042018-10-17 15:25:30 -0400158 updateView(mTrackedHeadsUp, true);
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800159 }
Selim Cinek29aab962018-02-27 17:05:45 -0800160 }
161
Gus Prevase2d6f042018-10-17 15:25:30 -0400162 public void updateRoundedChildren(NotificationSection[] sections) {
163 boolean anyChanged = false;
164 for (int i = 0; i < NUM_SECTIONS; i++) {
165 mTmpFirstInSectionViews[i] = mFirstInSectionViews[i];
166 mTmpLastInSectionViews[i] = mLastInSectionViews[i];
167 mFirstInSectionViews[i] = sections[i].getFirstVisibleChild();
168 mLastInSectionViews[i] = sections[i].getLastVisibleChild();
Selim Cinek29aab962018-02-27 17:05:45 -0800169 }
Gus Prevase2d6f042018-10-17 15:25:30 -0400170 anyChanged |= handleRemovedOldViews(sections, mTmpFirstInSectionViews, true);
171 anyChanged |= handleRemovedOldViews(sections, mTmpLastInSectionViews, false);
172 anyChanged |= handleAddedNewViews(sections, mTmpFirstInSectionViews, true);
173 anyChanged |= handleAddedNewViews(sections, mTmpLastInSectionViews, false);
174 if (anyChanged) {
175 mRoundingChangedCallback.run();
Selim Cinek29aab962018-02-27 17:05:45 -0800176 }
Gus Prevase2d6f042018-10-17 15:25:30 -0400177 }
178
179 private boolean handleRemovedOldViews(NotificationSection[] sections,
180 ActivatableNotificationView[] oldViews, boolean first) {
181 boolean anyChanged = false;
182 for (ActivatableNotificationView oldView : oldViews) {
183 if (oldView != null) {
184 boolean isStillPresent = false;
185 boolean adjacentSectionChanged = false;
186 for (NotificationSection section : sections) {
187 ActivatableNotificationView newView =
188 (first ? section.getFirstVisibleChild()
189 : section.getLastVisibleChild());
190 if (newView == oldView) {
191 isStillPresent = true;
192 if (oldView.isFirstInSection() != isFirstInSection(oldView,
193 false /* exclude first section */)
194 || oldView.isLastInSection() != isLastInSection(oldView,
195 false /* exclude last section */)) {
196 adjacentSectionChanged = true;
197 }
198 break;
199 }
200 }
201 if (!isStillPresent || adjacentSectionChanged) {
202 anyChanged = true;
203 if (!oldView.isRemoved()) {
204 updateViewWithoutCallback(oldView, oldView.isShown());
205 }
206 }
207 }
Selim Cinek29aab962018-02-27 17:05:45 -0800208 }
Gus Prevase2d6f042018-10-17 15:25:30 -0400209 return anyChanged;
210 }
211
212 private boolean handleAddedNewViews(NotificationSection[] sections,
213 ActivatableNotificationView[] oldViews, boolean first) {
214 boolean anyChanged = false;
215 for (NotificationSection section : sections) {
216 ActivatableNotificationView newView =
217 (first ? section.getFirstVisibleChild() : section.getLastVisibleChild());
218 if (newView != null) {
219 boolean wasAlreadyPresent = false;
220 for (ActivatableNotificationView oldView : oldViews) {
221 if (oldView == newView) {
222 wasAlreadyPresent = true;
223 break;
224 }
225 }
226 if (!wasAlreadyPresent) {
227 anyChanged = true;
228 updateViewWithoutCallback(newView,
229 newView.isShown() && !mAnimatedChildren.contains(newView));
230 }
231 }
Selim Cinek29aab962018-02-27 17:05:45 -0800232 }
Gus Prevase2d6f042018-10-17 15:25:30 -0400233 return anyChanged;
Selim Cinek29aab962018-02-27 17:05:45 -0800234 }
235
Dave Mankoffa4d195d2018-11-16 13:33:27 -0500236 public void setAnimatedChildren(HashSet<ExpandableView> animatedChildren) {
Selim Cinek29aab962018-02-27 17:05:45 -0800237 mAnimatedChildren = animatedChildren;
238 }
239
240 public void setOnRoundingChangedCallback(Runnable roundingChangedCallback) {
241 mRoundingChangedCallback = roundingChangedCallback;
242 }
Selim Cinekaa9db1f2018-02-27 17:35:47 -0800243
244 public void setTrackingHeadsUp(ExpandableNotificationRow row) {
245 mTrackedHeadsUp = row;
246 }
Selim Cinek29aab962018-02-27 17:05:45 -0800247}