HeadsUpManager.java revision 2f6b3fb90f069fdb8502dedf657790bf3d94dbd0
1b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek/*
2b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Copyright (C) 2011 The Android Open Source Project
3b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek *
4b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Licensed under the Apache License, Version 2.0 (the "License");
5b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * you may not use this file except in compliance with the License.
6b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * You may obtain a copy of the License at
7b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek *
8b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek *      http://www.apache.org/licenses/LICENSE-2.0
9b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek *
10b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Unless required by applicable law or agreed to in writing, software
11b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * distributed under the License is distributed on an "AS IS" BASIS,
12b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * See the License for the specific language governing permissions and
14b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * limitations under the License.
15b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */
16b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
17b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekpackage com.android.systemui.statusbar.policy;
18b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
19b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.content.Context;
20b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.content.res.Resources;
21b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.database.ContentObserver;
22b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.os.Handler;
23b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.os.SystemClock;
24b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.provider.Settings;
25b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.util.ArrayMap;
26b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.util.Log;
27b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.util.Pools;
28b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.view.ViewTreeObserver;
29b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.view.accessibility.AccessibilityEvent;
30b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
31b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.R;
32a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow;
33b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.NotificationData;
34b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.phone.PhoneStatusBar;
35b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
36b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.io.FileDescriptor;
37b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.io.PrintWriter;
38a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekimport java.util.HashMap;
39b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.util.HashSet;
40b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.util.Stack;
41a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekimport java.util.TreeSet;
42b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
43a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekpublic class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener {
44b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private static final String TAG = "HeadsUpManager";
45b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private static final boolean DEBUG = false;
46b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
47b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
48b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final int mHeadsUpNotificationDecay;
49b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final int mMinimumDisplayTime;
50b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
51b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final int mTouchSensitivityDelay;
52b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final ArrayMap<String, Long> mSnoozedPackages;
53b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
54b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final int mDefaultSnoozeLengthMs;
55b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final Handler mHandler = new Handler();
56b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
57b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
58b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
59b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
60b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        @Override
61b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public HeadsUpEntry acquire() {
62b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            if (!mPoolObjects.isEmpty()) {
63b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                return mPoolObjects.pop();
64b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
65b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return new HeadsUpEntry();
66b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
67b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
68b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        @Override
69b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public boolean release(HeadsUpEntry instance) {
70b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            instance.removeAutoCancelCallbacks();
71b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mPoolObjects.push(instance);
72b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
73b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
74b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    };
75b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
76b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
77b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private PhoneStatusBar mBar;
78b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private int mSnoozeLengthMs;
79b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private ContentObserver mSettingsObserver;
80a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
81a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>();
82b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private HashSet<String> mSwipedOutKeys = new HashSet<>();
83b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private int mUser;
84b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private Clock mClock;
85b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mReleaseOnExpandFinish;
86b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mTrackingHeadsUp;
87b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
88b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mIsExpanded;
89b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mHasPinnedHeadsUp;
90a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private int[] mTmpTwoArray = new int[2];
91b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
92a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    public HeadsUpManager(final Context context, ViewTreeObserver observer) {
93b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        Resources resources = context.getResources();
94b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mTouchSensitivityDelay = resources.getInteger(R.integer.heads_up_sensitivity_delay);
95b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "create() " + mTouchSensitivityDelay);
96b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSnoozedPackages = new ArrayMap<>();
97b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
98b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSnoozeLengthMs = mDefaultSnoozeLengthMs;
99b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
100e53e6bbb82b411f99083e4a6d2071fde45d68d53Selim Cinek        mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
101b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mClock = new Clock();
102b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
103b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
104b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
105b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSettingsObserver = new ContentObserver(mHandler) {
106b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            @Override
107b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            public void onChange(boolean selfChange) {
108b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                final int packageSnoozeLengthMs = Settings.Global.getInt(
109b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
110b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
111b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mSnoozeLengthMs = packageSnoozeLengthMs;
112b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
113b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
114b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
115b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        };
116b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        context.getContentResolver().registerContentObserver(
117b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
118b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                mSettingsObserver);
119b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
120a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        observer.addOnComputeInternalInsetsListener(this);
121b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
122b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
123b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void setBar(PhoneStatusBar bar) {
124b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mBar = bar;
125b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
126b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
127b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void addListener(OnHeadsUpChangedListener listener) {
128b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mListeners.add(listener);
129b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
130b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
131b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public PhoneStatusBar getBar() {
132b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mBar;
133b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
134b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
135b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
136b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * Called when posting a new notification to the heads up.
137b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
138b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void showNotification(NotificationData.Entry headsUp) {
139b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "showNotification");
140b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        addHeadsUpEntry(headsUp);
141b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        updateNotification(headsUp, true);
142b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        headsUp.setInterruption();
143b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
144b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
145b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
146b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * Called when updating or posting a notification to the heads up.
147b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
148b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
149b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "updateNotification");
150b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
151b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        headsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
152b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
153b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
154b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (alert) {
155b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key);
156b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            headsUpEntry.updateEntry();
157aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek            setEntryToShade(headsUpEntry, mIsExpanded, false /* justAdded */, false);
158b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
159b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
160b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
161b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void addHeadsUpEntry(NotificationData.Entry entry) {
162b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry headsUpEntry = mEntryPool.acquire();
163a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
164a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        // This will also add the entry to the sortedList
165b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        headsUpEntry.setEntry(entry);
166b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mHeadsUpEntries.put(entry.key, headsUpEntry);
167a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        entry.row.setHeadsUp(true);
168aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek        setEntryToShade(headsUpEntry, mIsExpanded /* inShade */, true /* justAdded */, false);
169b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (OnHeadsUpChangedListener listener : mListeners) {
170b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            listener.OnHeadsUpStateChanged(entry, true);
171b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
172b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
173b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
174b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
175aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek    private void setEntryToShade(HeadsUpEntry headsUpEntry, boolean inShade, boolean justAdded,
176aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek            boolean forceImmediate) {
1771f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        ExpandableNotificationRow row = headsUpEntry.entry.row;
178aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek        if (row.isInShade() != inShade || justAdded) {
1791f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            row.setInShade(inShade);
180aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek            if (!justAdded || !inShade) {
181aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek                updatePinnedHeadsUpState(forceImmediate);
182aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek                for (OnHeadsUpChangedListener listener : mListeners) {
183aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek                    listener.OnHeadsUpPinnedChanged(row, !inShade);
1841f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek                }
1851f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            }
1861f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        }
1871f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek    }
1881f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek
189b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void removeHeadsUpEntry(NotificationData.Entry entry) {
190b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
191a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mSortedEntries.remove(remove);
192b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mEntryPool.release(remove);
193b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
194b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        entry.row.setHeadsUp(false);
195aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek        setEntryToShade(remove, true /* inShade */, false /* justAdded */,
196aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek                false /* forceImmediate */);
197b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (OnHeadsUpChangedListener listener : mListeners) {
198b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            listener.OnHeadsUpStateChanged(entry, false);
199b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
200b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
201b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
202b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void updatePinnedHeadsUpState(boolean forceImmediate) {
203b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        boolean hasPinnedHeadsUp = hasPinnedHeadsUpInternal();
204b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (hasPinnedHeadsUp == mHasPinnedHeadsUp) {
205b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return;
206b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
207b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mHasPinnedHeadsUp = hasPinnedHeadsUp;
208b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (OnHeadsUpChangedListener listener :mListeners) {
209b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            listener.OnPinnedHeadsUpExistChanged(hasPinnedHeadsUp, forceImmediate);
210b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
211b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
212b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
213b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
214b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * React to the removal of the notification in the heads up.
215b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     *
216b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * @return true if the notification was removed and false if it still needs to be kept around
217b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * for a bit since it wasn't shown long enough
218b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
219b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean removeNotification(String key) {
220b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "remove");
221b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (wasShownLongEnough(key)) {
222b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            releaseImmediately(key);
223b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
224b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        } else {
225b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            getHeadsUpEntry(key).hideAsSoonAsPossible();
226b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return false;
227b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
228b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
229b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
230b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean wasShownLongEnough(String key) {
231b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
232b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry topEntry = getTopEntry();
233b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (mSwipedOutKeys.contains(key)) {
234b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            // We always instantly dismiss views being manually swiped out.
235b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mSwipedOutKeys.remove(key);
236b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
237b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
238b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (headsUpEntry != topEntry) {
239b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
240b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
241b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return headsUpEntry.wasShownLongEnough();
242b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
243b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
244b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean isHeadsUp(String key) {
245b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mHeadsUpEntries.containsKey(key);
246b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
247b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
248b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
249b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
250b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * Push any current Heads Up notification down into the shade.
251b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
252b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void releaseAllImmediately() {
253b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "releaseAllImmediately");
254a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        HashSet<String> keys = new HashSet<>(mHeadsUpEntries.keySet());
255a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        for (String key: keys) {
256b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            releaseImmediately(key);
257b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
258b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
259b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
260b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void releaseImmediately(String key) {
261b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
262b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (headsUpEntry == null) {
263b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return;
264b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
265b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        NotificationData.Entry shadeEntry = headsUpEntry.entry;
266b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        removeHeadsUpEntry(shadeEntry);
267b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
268b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
269b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean isSnoozed(String packageName) {
270b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        final String key = snoozeKey(packageName, mUser);
271b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        Long snoozedUntil = mSnoozedPackages.get(key);
272b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (snoozedUntil != null) {
273b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            if (snoozedUntil > SystemClock.elapsedRealtime()) {
274b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                if (DEBUG) Log.v(TAG, key + " snoozed");
275b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                return true;
276b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
277b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mSnoozedPackages.remove(packageName);
278b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
279b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return false;
280b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
281b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
282b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void snooze() {
283b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (String key: mHeadsUpEntries.keySet()) {
284b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            HeadsUpEntry entry = mHeadsUpEntries.get(key);
285b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            String packageName = entry.entry.notification.getPackageName();
286b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mSnoozedPackages.put(snoozeKey(packageName, mUser),
287b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    SystemClock.elapsedRealtime() + mSnoozeLengthMs);
288b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
289b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mReleaseOnExpandFinish = true;
290b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
291b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
292b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private static String snoozeKey(String packageName, int user) {
293b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return user + "," + packageName;
294b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
295b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
296b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private HeadsUpEntry getHeadsUpEntry(String key) {
297b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mHeadsUpEntries.get(key);
298b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
299b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
300b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public NotificationData.Entry getEntry(String key) {
301b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mHeadsUpEntries.get(key).entry;
302b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
303b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
304a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    public TreeSet<HeadsUpEntry> getSortedEntries() {
305a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        return mSortedEntries;
306b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
307b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
308b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public HeadsUpEntry getTopEntry() {
309a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        return mSortedEntries.isEmpty() ? null : mSortedEntries.first();
310b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
311b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
312b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
313b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * @param key the key of the touched notification
314b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * @return whether the touch is valid and should not be discarded
315b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
316b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean shouldSwallowClick(String key) {
3172f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek        HeadsUpEntry entry = mHeadsUpEntries.get(key);
3182f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek        if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
319b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
320b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
321b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return false;
322b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
323b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
324b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
325a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        if (!mIsExpanded && mHasPinnedHeadsUp) {
326a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            int minX = Integer.MAX_VALUE;
327a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            int maxX = 0;
328a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            int minY = Integer.MAX_VALUE;
329a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            int maxY = 0;
330a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            for (HeadsUpEntry entry: mSortedEntries) {
331a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                ExpandableNotificationRow row = entry.entry.row;
332a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                if (!row.isInShade()) {
333a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    row.getLocationOnScreen(mTmpTwoArray);
334a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    minX = Math.min(minX, mTmpTwoArray[0]);
335a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    minY = Math.min(minY, 0);
336a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth());
337a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    maxY = Math.max(maxY, row.getHeadsUpHeight());
338a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                }
339a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            }
340a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
341a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
342a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            info.touchableRegion.set(minX, minY, maxX, maxY);
343a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        }
344b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
345b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
346b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void setUser(int user) {
347b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mUser = user;
348b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
349b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
350b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
351b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.println("HeadsUpManager state:");
352b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.print("  mTouchSensitivityDelay="); pw.println(mTouchSensitivityDelay);
353b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.print("  mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
354b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.print("  now="); pw.println(SystemClock.elapsedRealtime());
355b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.print("  mUser="); pw.println(mUser);
356a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        for (HeadsUpEntry entry: mSortedEntries) {
357a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            pw.print("  HeadsUpEntry="); pw.println(entry.entry);
358b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
359b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        int N = mSnoozedPackages.size();
360b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.println("  snoozed packages: " + N);
361b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (int i = 0; i < N; i++) {
362b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            pw.print("    "); pw.print(mSnoozedPackages.valueAt(i));
363b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
364b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
365b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
366b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
367b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean hasPinnedHeadsUp() {
368b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mHasPinnedHeadsUp;
369b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
370b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
371b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean hasPinnedHeadsUpInternal() {
372b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (String key: mHeadsUpEntries.keySet()) {
373b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            HeadsUpEntry entry = mHeadsUpEntries.get(key);
374b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            if (!entry.entry.row.isInShade()) {
375b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                return true;
376b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
377b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
378b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return false;
379b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
380b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
381b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void addSwipedOutKey(String key) {
382b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSwipedOutKeys.add(key);
383b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
384b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
385b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public float getHighestPinnedHeadsUp() {
386b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        float max = 0;
387a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        for (HeadsUpEntry entry: mSortedEntries) {
388b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            if (!entry.entry.row.isInShade()) {
389b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                max = Math.max(max, entry.entry.row.getActualHeight());
390b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
391b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
392b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return max;
393b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
394b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
395b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void releaseAllToShade() {
396b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (String key: mHeadsUpEntries.keySet()) {
397b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            HeadsUpEntry entry = mHeadsUpEntries.get(key);
398aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek            setEntryToShade(entry, true /* toShade */, false /* justAdded */,
399aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek                    true /* forceImmediate */);
400b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
401b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
402b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
403b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void onExpandingFinished() {
404b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (mReleaseOnExpandFinish) {
405b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            releaseAllImmediately();
406b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mReleaseOnExpandFinish = false;
407b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        } else {
408b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
409b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                removeHeadsUpEntry(entry);
410b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
411b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mEntriesToRemoveAfterExpand.clear();
412b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
413b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
414b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
415b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
416b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mTrackingHeadsUp = trackingHeadsUp;
417b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
418b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
419b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void setIsExpanded(boolean isExpanded) {
4201f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        if (isExpanded != mIsExpanded) {
4211f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            mIsExpanded = isExpanded;
4221f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            if (isExpanded) {
4231f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek                releaseAllToShade();
4241f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            }
4251f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        }
426b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
427b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
428b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public int getTopHeadsUpHeight() {
429b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry topEntry = getTopEntry();
430b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0;
431b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
432b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
433fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek    public int compare(NotificationData.Entry a, NotificationData.Entry b) {
434fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        HeadsUpEntry aEntry = getHeadsUpEntry(a.key);
435fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        HeadsUpEntry bEntry = getHeadsUpEntry(b.key);
436fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        if (aEntry == null || bEntry == null) {
437fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek            return aEntry == null ? 1 : -1;
438fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        }
439fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        return aEntry.compareTo(bEntry);
440fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek    }
441fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek
442b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
443b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public NotificationData.Entry entry;
444b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public long postTime;
445b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public long earliestRemovaltime;
446b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        private Runnable mRemoveHeadsUpRunnable;
447b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
448b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public void setEntry(final NotificationData.Entry entry) {
449b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            this.entry = entry;
450b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
451b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            // The actual post time will be just after the heads-up really slided in
452b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            postTime = mClock.currentTimeMillis() + mTouchSensitivityDelay;
453b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mRemoveHeadsUpRunnable = new Runnable() {
454b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                @Override
455b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                public void run() {
456b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    if (!mTrackingHeadsUp) {
457b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        removeHeadsUpEntry(entry);
458b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    } else {
459b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        mEntriesToRemoveAfterExpand.add(entry);
460b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    }
461b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
462b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            };
463b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            updateEntry();
464b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
465b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
466b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public void updateEntry() {
467b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            long currentTime = mClock.currentTimeMillis();
468b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            earliestRemovaltime = currentTime + mMinimumDisplayTime;
46931d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek            postTime = Math.max(postTime, currentTime);
470b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            removeAutoCancelCallbacks();
47131d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek            if (canEntryDecay()) {
47231d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek                long finishTime = postTime + mHeadsUpNotificationDecay;
47331d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek                long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
47431d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek                mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
47531d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek            }
476a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            updateSortOrder(HeadsUpEntry.this);
477b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
478b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
47931d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek        private boolean canEntryDecay() {
48031d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek            return entry.notification.getNotification().fullScreenIntent == null;
48131d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek        }
48231d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek
483b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        @Override
484b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public int compareTo(HeadsUpEntry o) {
485a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            return postTime < o.postTime ? 1
486b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    : postTime == o.postTime ? 0
487a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                            : -1;
488b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
489b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
490b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public void removeAutoCancelCallbacks() {
491b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
492b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
493b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
494b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public boolean wasShownLongEnough() {
495b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return earliestRemovaltime < mClock.currentTimeMillis();
496b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
497b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
498b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public void hideAsSoonAsPossible() {
499b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            removeAutoCancelCallbacks();
500b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mHandler.postDelayed(mRemoveHeadsUpRunnable,
501b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    earliestRemovaltime - mClock.currentTimeMillis());
502b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
503b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
504b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
505a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    /**
506a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     * Update the sorted heads up order.
507a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     *
508a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     * @param headsUpEntry the headsUp that changed
509a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek     */
510a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private void updateSortOrder(HeadsUpEntry headsUpEntry) {
511a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mSortedEntries.remove(headsUpEntry);
512a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mSortedEntries.add(headsUpEntry);
513a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    }
514a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
515b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public static class Clock {
516b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public long currentTimeMillis() {
517b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return SystemClock.elapsedRealtime();
518b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
519b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
520b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
521b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public interface OnHeadsUpChangedListener {
522b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        void OnPinnedHeadsUpExistChanged(boolean exist, boolean changeImmediatly);
523aac932591d7aa05bae61d2b47ed7647f35da0001Selim Cinek        void OnHeadsUpPinnedChanged(ExpandableNotificationRow headsUp, boolean isHeadsUp);
524b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        void OnHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp);
525b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
526b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek}
527