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