1b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek/*
2684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Copyright (C) 2015 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;
28737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinekimport android.view.View;
29b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.view.ViewTreeObserver;
30b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport android.view.accessibility.AccessibilityEvent;
31b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
32b659c4f44a839e6ad7ef6834cc0d35954e04460aChris Wrenimport com.android.internal.logging.MetricsLogger;
33b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.R;
34a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow;
35b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.NotificationData;
36b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.phone.PhoneStatusBar;
37b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
38b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.io.FileDescriptor;
39b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.io.PrintWriter;
40684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinekimport java.util.ArrayList;
41a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekimport java.util.HashMap;
42b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.util.HashSet;
43b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.util.Stack;
44a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekimport java.util.TreeSet;
45b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
46684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek/**
47684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A manager which handles heads up notifications which is a special mode where
48684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * they simply peek from the top of the screen.
49684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */
50a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekpublic class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener {
51b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private static final String TAG = "HeadsUpManager";
52b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private static final boolean DEBUG = false;
53b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms";
540fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek    private static final int TAG_CLICKED_NOTIFICATION = R.id.is_clicked_heads_up_tag;
55b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
56b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final int mHeadsUpNotificationDecay;
57b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final int mMinimumDisplayTime;
58b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
59684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    private final int mTouchAcceptanceDelay;
60b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final ArrayMap<String, Long> mSnoozedPackages;
61b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>();
62b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final int mDefaultSnoozeLengthMs;
63b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final Handler mHandler = new Handler();
64b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() {
65b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
66b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        private Stack<HeadsUpEntry> mPoolObjects = new Stack<>();
67b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
68b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        @Override
69b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public HeadsUpEntry acquire() {
70b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            if (!mPoolObjects.isEmpty()) {
71b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                return mPoolObjects.pop();
72b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
73b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return new HeadsUpEntry();
74b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
75b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
76b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        @Override
77b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public boolean release(HeadsUpEntry instance) {
78684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            instance.reset();
79b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mPoolObjects.push(instance);
80b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
81b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
82b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    };
83b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
84737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    private final View mStatusBarWindowView;
85737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    private final int mStatusBarHeight;
8679d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek    private final int mNotificationsTopPadding;
87b659c4f44a839e6ad7ef6834cc0d35954e04460aChris Wren    private final Context mContext;
88b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private PhoneStatusBar mBar;
89b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private int mSnoozeLengthMs;
90b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private ContentObserver mSettingsObserver;
91a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>();
92a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>();
93b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private HashSet<String> mSwipedOutKeys = new HashSet<>();
94b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private int mUser;
95b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private Clock mClock;
96b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mReleaseOnExpandFinish;
97b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mTrackingHeadsUp;
98b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>();
99b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean mIsExpanded;
100684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    private boolean mHasPinnedNotification;
101a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    private int[] mTmpTwoArray = new int[2];
102737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    private boolean mHeadsUpGoingAway;
103737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    private boolean mWaitingOnCollapseWhenGoingAway;
104737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    private boolean mIsObserving;
105b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
106737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    public HeadsUpManager(final Context context, View statusBarWindowView) {
107b659c4f44a839e6ad7ef6834cc0d35954e04460aChris Wren        mContext = context;
108b659c4f44a839e6ad7ef6834cc0d35954e04460aChris Wren        Resources resources = mContext.getResources();
109684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay);
110b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSnoozedPackages = new ArrayMap<>();
111b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms);
112b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSnoozeLengthMs = mDefaultSnoozeLengthMs;
113b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time);
114e53e6bbb82b411f99083e4a6d2071fde45d68d53Selim Cinek        mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay);
115b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mClock = new Clock();
116b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
117b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(),
118b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs);
119b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSettingsObserver = new ContentObserver(mHandler) {
120b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            @Override
121b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            public void onChange(boolean selfChange) {
122b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                final int packageSnoozeLengthMs = Settings.Global.getInt(
123b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1);
124b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) {
125b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    mSnoozeLengthMs = packageSnoozeLengthMs;
126b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs);
127b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
128b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
129b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        };
130b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        context.getContentResolver().registerContentObserver(
131b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false,
132b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                mSettingsObserver);
133737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        mStatusBarWindowView = statusBarWindowView;
134737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        mStatusBarHeight = resources.getDimensionPixelSize(
135737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                com.android.internal.R.dimen.status_bar_height);
13679d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek        mNotificationsTopPadding = context.getResources()
13779d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek                .getDimensionPixelSize(R.dimen.notifications_top_padding);
138737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    }
139737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek
140737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    private void updateTouchableRegionListener() {
141737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway
142737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                || mWaitingOnCollapseWhenGoingAway;
143737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        if (shouldObserve == mIsObserving) {
144737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            return;
145737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        }
146737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        if (shouldObserve) {
147737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this);
148737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            mStatusBarWindowView.requestLayout();
149737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        } else {
150737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this);
151737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        }
152737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        mIsObserving = shouldObserve;
153b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
154b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
155b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void setBar(PhoneStatusBar bar) {
156b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mBar = bar;
157b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
158b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
159b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void addListener(OnHeadsUpChangedListener listener) {
160b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mListeners.add(listener);
161b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
162b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
163b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public PhoneStatusBar getBar() {
164b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mBar;
165b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
166b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
167b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
168b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * Called when posting a new notification to the heads up.
169b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
170b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void showNotification(NotificationData.Entry headsUp) {
171b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "showNotification");
172b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        addHeadsUpEntry(headsUp);
173b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        updateNotification(headsUp, true);
174b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        headsUp.setInterruption();
175b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
176b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
177b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
178b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * Called when updating or posting a notification to the heads up.
179b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
180b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void updateNotification(NotificationData.Entry headsUp, boolean alert) {
181b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "updateNotification");
182b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
183b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        headsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */);
184b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
185b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
186b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (alert) {
187b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key);
188b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            headsUpEntry.updateEntry();
189131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek            setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp));
190b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
191b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
192b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
193b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void addHeadsUpEntry(NotificationData.Entry entry) {
194b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry headsUpEntry = mEntryPool.acquire();
195a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
196a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        // This will also add the entry to the sortedList
197b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        headsUpEntry.setEntry(entry);
198b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mHeadsUpEntries.put(entry.key, headsUpEntry);
199a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        entry.row.setHeadsUp(true);
200131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek        setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(entry));
201b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (OnHeadsUpChangedListener listener : mListeners) {
202684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            listener.onHeadsUpStateChanged(entry, true);
203b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
204b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
205b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
206b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
207131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek    private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) {
208131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek        return !mIsExpanded || hasFullScreenIntent(entry);
209131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek    }
210131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek
211131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek    private boolean hasFullScreenIntent(NotificationData.Entry entry) {
212131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek        return entry.notification.getNotification().fullScreenIntent != null;
213131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek    }
214131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek
215684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) {
2161f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        ExpandableNotificationRow row = headsUpEntry.entry.row;
217684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        if (row.isPinned() != isPinned) {
218684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            row.setPinned(isPinned);
219684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            updatePinnedMode();
220684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            for (OnHeadsUpChangedListener listener : mListeners) {
221684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                if (isPinned) {
222684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                    listener.onHeadsUpPinned(row);
223684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                } else {
224684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                    listener.onHeadsUpUnPinned(row);
2251f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek                }
2261f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            }
2271f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        }
2281f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek    }
2291f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek
230b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private void removeHeadsUpEntry(NotificationData.Entry entry) {
231b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key);
232a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        mSortedEntries.remove(remove);
233b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
234b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        entry.row.setHeadsUp(false);
235684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        setEntryPinned(remove, false /* isPinned */);
236b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (OnHeadsUpChangedListener listener : mListeners) {
237684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            listener.onHeadsUpStateChanged(entry, false);
238b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
239684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mEntryPool.release(remove);
240b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
241b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
242684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    private void updatePinnedMode() {
243684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        boolean hasPinnedNotification = hasPinnedNotificationInternal();
244684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        if (hasPinnedNotification == mHasPinnedNotification) {
245b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return;
246b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
247684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        mHasPinnedNotification = hasPinnedNotification;
248063926bda7d32ca74075a6a106c712efc3aa3581Chris Wren        if (mHasPinnedNotification) {
249063926bda7d32ca74075a6a106c712efc3aa3581Chris Wren            MetricsLogger.count(mContext, "note_peek", 1);
250063926bda7d32ca74075a6a106c712efc3aa3581Chris Wren        }
251737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        updateTouchableRegionListener();
252684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        for (OnHeadsUpChangedListener listener : mListeners) {
253b349af573121cc659d775696066a13ed7101c308John Spurlock            listener.onHeadsUpPinnedModeChanged(hasPinnedNotification);
254b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
255b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
256b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
257b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
258b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * React to the removal of the notification in the heads up.
259b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     *
260b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * @return true if the notification was removed and false if it still needs to be kept around
261b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * for a bit since it wasn't shown long enough
262b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
263b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean removeNotification(String key) {
264b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "remove");
265b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (wasShownLongEnough(key)) {
266b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            releaseImmediately(key);
267b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
268b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        } else {
269684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            getHeadsUpEntry(key).removeAsSoonAsPossible();
270b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return false;
271b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
272b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
273b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
274b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private boolean wasShownLongEnough(String key) {
275b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
276b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry topEntry = getTopEntry();
277b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (mSwipedOutKeys.contains(key)) {
278b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            // We always instantly dismiss views being manually swiped out.
279b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mSwipedOutKeys.remove(key);
280b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
281b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
282b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (headsUpEntry != topEntry) {
283b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
284b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
285b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return headsUpEntry.wasShownLongEnough();
286b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
287b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
288b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean isHeadsUp(String key) {
289b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mHeadsUpEntries.containsKey(key);
290b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
291b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
292b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
293b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * Push any current Heads Up notification down into the shade.
294b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
295b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void releaseAllImmediately() {
296b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (DEBUG) Log.v(TAG, "releaseAllImmediately");
297684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet());
298684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        for (String key : keys) {
299b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            releaseImmediately(key);
300b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
301b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
302b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
303b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void releaseImmediately(String key) {
304b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry headsUpEntry = getHeadsUpEntry(key);
305b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (headsUpEntry == null) {
306b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return;
307b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
308b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        NotificationData.Entry shadeEntry = headsUpEntry.entry;
309b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        removeHeadsUpEntry(shadeEntry);
310b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
311b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
312b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean isSnoozed(String packageName) {
313b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        final String key = snoozeKey(packageName, mUser);
314b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        Long snoozedUntil = mSnoozedPackages.get(key);
315b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (snoozedUntil != null) {
316b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            if (snoozedUntil > SystemClock.elapsedRealtime()) {
317b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                if (DEBUG) Log.v(TAG, key + " snoozed");
318b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                return true;
319b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
320b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mSnoozedPackages.remove(packageName);
321b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
322b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return false;
323b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
324b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
325b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void snooze() {
326684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        for (String key : mHeadsUpEntries.keySet()) {
327b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            HeadsUpEntry entry = mHeadsUpEntries.get(key);
328b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            String packageName = entry.entry.notification.getPackageName();
329b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mSnoozedPackages.put(snoozeKey(packageName, mUser),
330b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    SystemClock.elapsedRealtime() + mSnoozeLengthMs);
331b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
332b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mReleaseOnExpandFinish = true;
333b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
334b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
335b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private static String snoozeKey(String packageName, int user) {
336b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return user + "," + packageName;
337b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
338b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
339b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    private HeadsUpEntry getHeadsUpEntry(String key) {
340b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mHeadsUpEntries.get(key);
341b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
342b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
343b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public NotificationData.Entry getEntry(String key) {
344b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return mHeadsUpEntries.get(key).entry;
345b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
346b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
347a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek    public TreeSet<HeadsUpEntry> getSortedEntries() {
348a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        return mSortedEntries;
349b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
350b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
351b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public HeadsUpEntry getTopEntry() {
352a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        return mSortedEntries.isEmpty() ? null : mSortedEntries.first();
353b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
354b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
355b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    /**
356684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * Decides whether a click is invalid for a notification, i.e it has not been shown long enough
357684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * that a user might have consciously clicked on it.
358684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     *
359b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     * @param key the key of the touched notification
360684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * @return whether the touch is invalid and should be discarded
361b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek     */
362b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean shouldSwallowClick(String key) {
3632f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek        HeadsUpEntry entry = mHeadsUpEntries.get(key);
3642f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek        if (entry != null && mClock.currentTimeMillis() < entry.postTime) {
365b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return true;
366b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
367b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return false;
368b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
369b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
370b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
371131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek        if (mIsExpanded) {
372131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek            // The touchable region is always the full area when expanded
373131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek            return;
374131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek        }
375737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        if (mHasPinnedNotification) {
376a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            int minX = Integer.MAX_VALUE;
377a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            int maxX = 0;
378a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            int minY = Integer.MAX_VALUE;
379a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            int maxY = 0;
380684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            for (HeadsUpEntry entry : mSortedEntries) {
381a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                ExpandableNotificationRow row = entry.entry.row;
382684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek                if (row.isPinned()) {
383a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    row.getLocationOnScreen(mTmpTwoArray);
384a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    minX = Math.min(minX, mTmpTwoArray[0]);
385a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    minY = Math.min(minY, 0);
386a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth());
387a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                    maxY = Math.max(maxY, row.getHeadsUpHeight());
388a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                }
389a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            }
390a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek
391a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
39279d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek            info.touchableRegion.set(minX, minY, maxX, maxY + mNotificationsTopPadding);
393737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) {
394737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION);
395737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight);
396a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        }
397b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
398b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
399b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void setUser(int user) {
400b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mUser = user;
401b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
402b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
403b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
404b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.println("HeadsUpManager state:");
405684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        pw.print("  mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay);
406b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.print("  mSnoozeLengthMs="); pw.println(mSnoozeLengthMs);
407b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.print("  now="); pw.println(SystemClock.elapsedRealtime());
408b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.print("  mUser="); pw.println(mUser);
409a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek        for (HeadsUpEntry entry: mSortedEntries) {
410a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            pw.print("  HeadsUpEntry="); pw.println(entry.entry);
411b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
412b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        int N = mSnoozedPackages.size();
413b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        pw.println("  snoozed packages: " + N);
414b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        for (int i = 0; i < N; i++) {
415b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            pw.print("    "); pw.print(mSnoozedPackages.valueAt(i));
416b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            pw.print(", "); pw.println(mSnoozedPackages.keyAt(i));
417b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
418b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
419b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
420b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public boolean hasPinnedHeadsUp() {
421684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        return mHasPinnedNotification;
422b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
423b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
424684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    private boolean hasPinnedNotificationInternal() {
425684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        for (String key : mHeadsUpEntries.keySet()) {
426b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            HeadsUpEntry entry = mHeadsUpEntries.get(key);
427684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            if (entry.entry.row.isPinned()) {
428b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                return true;
429b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
430b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
431b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return false;
432b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
433b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
434684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    /**
435684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * Notifies that a notification was swiped out and will be removed.
436684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     *
437684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * @param key the notification key
438684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     */
439684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    public void addSwipedOutNotification(String key) {
440b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mSwipedOutKeys.add(key);
441b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
442b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
443684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    public void unpinAll() {
444684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        for (String key : mHeadsUpEntries.keySet()) {
445b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            HeadsUpEntry entry = mHeadsUpEntries.get(key);
446684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            setEntryPinned(entry, false /* isPinned */);
447b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
448b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
449b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
450b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void onExpandingFinished() {
451b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        if (mReleaseOnExpandFinish) {
452b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            releaseAllImmediately();
453b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mReleaseOnExpandFinish = false;
454b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        } else {
455b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) {
456b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                removeHeadsUpEntry(entry);
457b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            }
458b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
459acd0df65dd8be97aae5617c9a8346d4a4ab88abdSelim Cinek        mEntriesToRemoveAfterExpand.clear();
460b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
461b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
462b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void setTrackingHeadsUp(boolean trackingHeadsUp) {
463b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        mTrackingHeadsUp = trackingHeadsUp;
464b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
465b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
466b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public void setIsExpanded(boolean isExpanded) {
4671f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        if (isExpanded != mIsExpanded) {
4681f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            mIsExpanded = isExpanded;
4691f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            if (isExpanded) {
470737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                // make sure our state is sane
471737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                mWaitingOnCollapseWhenGoingAway = false;
472737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                mHeadsUpGoingAway = false;
473737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                updateTouchableRegionListener();
4741f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek            }
4751f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek        }
476b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
477b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
478b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public int getTopHeadsUpHeight() {
479b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        HeadsUpEntry topEntry = getTopEntry();
480b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0;
481b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
482b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
483684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    /**
484684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * Compare two entries and decide how they should be ranked.
485684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     *
486684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * @return -1 if the first argument should be ranked higher than the second, 1 if the second
487684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * one should be ranked higher and 0 if they are equal.
488684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     */
489fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek    public int compare(NotificationData.Entry a, NotificationData.Entry b) {
490fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        HeadsUpEntry aEntry = getHeadsUpEntry(a.key);
491fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        HeadsUpEntry bEntry = getHeadsUpEntry(b.key);
492fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        if (aEntry == null || bEntry == null) {
493fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek            return aEntry == null ? 1 : -1;
494fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        }
495fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek        return aEntry.compareTo(bEntry);
496fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek    }
497fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek
498737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    /**
499737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek     * Set that we are exiting the headsUp pinned mode, but some notifications might still be
500737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek     * animating out. This is used to keep the touchable regions in a sane state.
501737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek     */
502737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    public void setHeadsUpGoingAway(boolean headsUpGoingAway) {
503737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        if (headsUpGoingAway != mHeadsUpGoingAway) {
504737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            mHeadsUpGoingAway = headsUpGoingAway;
505737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            if (!headsUpGoingAway) {
506737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                waitForStatusBarLayout();
507737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            }
508737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            updateTouchableRegionListener();
509737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        }
510737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    }
511737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek
512737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    /**
513737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek     * We need to wait on the whole panel to collapse, before we can remove the touchable region
514737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek     * listener.
515737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek     */
516737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    private void waitForStatusBarLayout() {
517737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        mWaitingOnCollapseWhenGoingAway = true;
518737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
519737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            @Override
520737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            public void onLayoutChange(View v, int left, int top, int right, int bottom,
521737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                    int oldLeft,
522737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                    int oldTop, int oldRight, int oldBottom) {
523737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) {
524737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                    mStatusBarWindowView.removeOnLayoutChangeListener(this);
525737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                    mWaitingOnCollapseWhenGoingAway = false;
526737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                    updateTouchableRegionListener();
527737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek                }
528737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek            }
529737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek        });
530737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek    }
531737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek
5320fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek    public static void setIsClickedNotification(View child, boolean clicked) {
5330fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek        child.setTag(TAG_CLICKED_NOTIFICATION, clicked ? true : null);
5340fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek    }
5350fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek
5360fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek    public static boolean isClickedHeadsUpNotification(View child) {
5370fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek        Boolean clicked = (Boolean) child.getTag(TAG_CLICKED_NOTIFICATION);
5380fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek        return clicked != null && clicked;
5390fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek    }
5400fccc729fd3b19a62efd90ae13b207faa3d1e8bbSelim Cinek
541684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek    /**
542684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * This represents a notification and how long it is in a heads up mode. It also manages its
543684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     * lifecycle automatically when created.
544684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek     */
545b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public class HeadsUpEntry implements Comparable<HeadsUpEntry> {
546b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public NotificationData.Entry entry;
547b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public long postTime;
548b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public long earliestRemovaltime;
549b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        private Runnable mRemoveHeadsUpRunnable;
550b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
551b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public void setEntry(final NotificationData.Entry entry) {
552b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            this.entry = entry;
553b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
554b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            // The actual post time will be just after the heads-up really slided in
555684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay;
556b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mRemoveHeadsUpRunnable = new Runnable() {
557b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                @Override
558b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                public void run() {
559b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    if (!mTrackingHeadsUp) {
560b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        removeHeadsUpEntry(entry);
561b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    } else {
562b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                        mEntriesToRemoveAfterExpand.add(entry);
563b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    }
564b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                }
565b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            };
566b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            updateEntry();
567b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
568b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
569b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public void updateEntry() {
570f87baef1b18f2564664a73f78859f23f92ad1d26Selim Cinek            mSortedEntries.remove(HeadsUpEntry.this);
571b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            long currentTime = mClock.currentTimeMillis();
572b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            earliestRemovaltime = currentTime + mMinimumDisplayTime;
57331d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek            postTime = Math.max(postTime, currentTime);
574684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            removeAutoRemovalCallbacks();
575131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek            if (!hasFullScreenIntent(entry)) {
57631d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek                long finishTime = postTime + mHeadsUpNotificationDecay;
57731d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek                long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime);
57831d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek                mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay);
57931d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek            }
580f87baef1b18f2564664a73f78859f23f92ad1d26Selim Cinek            mSortedEntries.add(HeadsUpEntry.this);
581b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
582b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
583b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        @Override
584b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public int compareTo(HeadsUpEntry o) {
5852ae7107d0e9cde3e88c7313cf4dbcebbd171597eSelim Cinek            boolean selfFullscreen = hasFullScreenIntent(entry);
5862ae7107d0e9cde3e88c7313cf4dbcebbd171597eSelim Cinek            boolean otherFullscreen = hasFullScreenIntent(o.entry);
5872ae7107d0e9cde3e88c7313cf4dbcebbd171597eSelim Cinek            if (selfFullscreen && !otherFullscreen) {
5882ae7107d0e9cde3e88c7313cf4dbcebbd171597eSelim Cinek                return -1;
5892ae7107d0e9cde3e88c7313cf4dbcebbd171597eSelim Cinek            } else if (!selfFullscreen && otherFullscreen) {
5902ae7107d0e9cde3e88c7313cf4dbcebbd171597eSelim Cinek                return 1;
5912ae7107d0e9cde3e88c7313cf4dbcebbd171597eSelim Cinek            }
592a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek            return postTime < o.postTime ? 1
593f87baef1b18f2564664a73f78859f23f92ad1d26Selim Cinek                    : postTime == o.postTime ? entry.key.compareTo(o.entry.key)
594a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek                            : -1;
595b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
596b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
597684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        public void removeAutoRemovalCallbacks() {
598b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mHandler.removeCallbacks(mRemoveHeadsUpRunnable);
599b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
600b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
601b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public boolean wasShownLongEnough() {
602b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return earliestRemovaltime < mClock.currentTimeMillis();
603b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
604b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
605684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        public void removeAsSoonAsPossible() {
606684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            removeAutoRemovalCallbacks();
607b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            mHandler.postDelayed(mRemoveHeadsUpRunnable,
608b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek                    earliestRemovaltime - mClock.currentTimeMillis());
609b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
610684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek
611684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        public void reset() {
612684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            removeAutoRemovalCallbacks();
613684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            entry = null;
614684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek            mRemoveHeadsUpRunnable = null;
615684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        }
616b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
617b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
618b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public static class Clock {
619b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        public long currentTimeMillis() {
620b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek            return SystemClock.elapsedRealtime();
621b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek        }
622b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
623b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek
624b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    public interface OnHeadsUpChangedListener {
625684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        /**
626684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         * The state whether there exist pinned heads-ups or not changed.
627684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         *
628684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         * @param inPinnedMode whether there are any pinned heads-ups
629684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         */
630b349af573121cc659d775696066a13ed7101c308John Spurlock        void onHeadsUpPinnedModeChanged(boolean inPinnedMode);
631684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek
632684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        /**
633684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         * A notification was just pinned to the top.
634684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         */
635684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        void onHeadsUpPinned(ExpandableNotificationRow headsUp);
636684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek
637684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        /**
638684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         * A notification was just unpinned from the top.
639684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         */
640684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        void onHeadsUpUnPinned(ExpandableNotificationRow headsUp);
641684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek
642684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        /**
643684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         * A notification just became a heads up or turned back to its normal state.
644684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         *
645684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         * @param entry the entry of the changed notification
646684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         * @param isHeadsUp whether the notification is now a headsUp notification
647684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek         */
648684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek        void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp);
649b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek    }
650b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek}
651