1 /* 2 * Copyright (C) 2016 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; 18 19 import android.support.v4.util.ArraySet; 20 import android.view.View; 21 22 import com.android.systemui.statusbar.ExpandableNotificationRow; 23 import com.android.systemui.statusbar.NotificationData; 24 import com.android.systemui.statusbar.policy.OnHeadsUpChangedListener; 25 26 import java.util.ArrayList; 27 28 /** 29 * A manager that ensures that notifications are visually stable. It will suppress reorderings 30 * and reorder at the right time when they are out of view. 31 */ 32 public class VisualStabilityManager implements OnHeadsUpChangedListener { 33 34 private final ArrayList<Callback> mCallbacks = new ArrayList<>(); 35 36 private boolean mPanelExpanded; 37 private boolean mScreenOn; 38 private boolean mReorderingAllowed; 39 private VisibilityLocationProvider mVisibilityLocationProvider; 40 private ArraySet<View> mAllowedReorderViews = new ArraySet<>(); 41 private ArraySet<View> mLowPriorityReorderingViews = new ArraySet<>(); 42 private ArraySet<View> mAddedChildren = new ArraySet<>(); 43 private boolean mPulsing; 44 45 /** 46 * Add a callback to invoke when reordering is allowed again. 47 * @param callback 48 */ addReorderingAllowedCallback(Callback callback)49 public void addReorderingAllowedCallback(Callback callback) { 50 if (mCallbacks.contains(callback)) { 51 return; 52 } 53 mCallbacks.add(callback); 54 } 55 56 /** 57 * Set the panel to be expanded. 58 */ setPanelExpanded(boolean expanded)59 public void setPanelExpanded(boolean expanded) { 60 mPanelExpanded = expanded; 61 updateReorderingAllowed(); 62 } 63 64 /** 65 * @param screenOn whether the screen is on 66 */ setScreenOn(boolean screenOn)67 public void setScreenOn(boolean screenOn) { 68 mScreenOn = screenOn; 69 updateReorderingAllowed(); 70 } 71 72 /** 73 * @param pulsing whether we are currently pulsing for ambient display. 74 */ setPulsing(boolean pulsing)75 public void setPulsing(boolean pulsing) { 76 if (mPulsing == pulsing) { 77 return; 78 } 79 mPulsing = pulsing; 80 updateReorderingAllowed(); 81 } 82 updateReorderingAllowed()83 private void updateReorderingAllowed() { 84 boolean reorderingAllowed = (!mScreenOn || !mPanelExpanded) && !mPulsing; 85 boolean changed = reorderingAllowed && !mReorderingAllowed; 86 mReorderingAllowed = reorderingAllowed; 87 if (changed) { 88 notifyCallbacks(); 89 } 90 } 91 notifyCallbacks()92 private void notifyCallbacks() { 93 for (int i = 0; i < mCallbacks.size(); i++) { 94 Callback callback = mCallbacks.get(i); 95 callback.onReorderingAllowed(); 96 } 97 mCallbacks.clear(); 98 } 99 100 /** 101 * @return whether reordering is currently allowed in general. 102 */ isReorderingAllowed()103 public boolean isReorderingAllowed() { 104 return mReorderingAllowed; 105 } 106 107 /** 108 * @return whether a specific notification is allowed to reorder. Certain notifications are 109 * allowed to reorder even if {@link #isReorderingAllowed()} returns false, like newly added 110 * notifications or heads-up notifications that are out of view. 111 */ canReorderNotification(ExpandableNotificationRow row)112 public boolean canReorderNotification(ExpandableNotificationRow row) { 113 if (mReorderingAllowed) { 114 return true; 115 } 116 if (mAddedChildren.contains(row)) { 117 return true; 118 } 119 if (mLowPriorityReorderingViews.contains(row)) { 120 return true; 121 } 122 if (mAllowedReorderViews.contains(row) 123 && !mVisibilityLocationProvider.isInVisibleLocation(row)) { 124 return true; 125 } 126 return false; 127 } 128 setVisibilityLocationProvider( VisibilityLocationProvider visibilityLocationProvider)129 public void setVisibilityLocationProvider( 130 VisibilityLocationProvider visibilityLocationProvider) { 131 mVisibilityLocationProvider = visibilityLocationProvider; 132 } 133 onReorderingFinished()134 public void onReorderingFinished() { 135 mAllowedReorderViews.clear(); 136 mAddedChildren.clear(); 137 mLowPriorityReorderingViews.clear(); 138 } 139 140 @Override onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp)141 public void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp) { 142 if (isHeadsUp) { 143 // Heads up notifications should in general be allowed to reorder if they are out of 144 // view and stay at the current location if they aren't. 145 mAllowedReorderViews.add(entry.row); 146 } 147 } 148 onLowPriorityUpdated(NotificationData.Entry entry)149 public void onLowPriorityUpdated(NotificationData.Entry entry) { 150 mLowPriorityReorderingViews.add(entry.row); 151 } 152 153 /** 154 * Notify the visual stability manager that a new view was added and should be allowed to 155 * reorder next time. 156 */ notifyViewAddition(View view)157 public void notifyViewAddition(View view) { 158 mAddedChildren.add(view); 159 } 160 161 public interface Callback { 162 /** 163 * Called when reordering is allowed again. 164 */ onReorderingAllowed()165 void onReorderingAllowed(); 166 } 167 168 } 169