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