HeadsUpManager.java revision acd0df65dd8be97aae5617c9a8346d4a4ab88abd
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"; 54b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 55b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final int mHeadsUpNotificationDecay; 56b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final int mMinimumDisplayTime; 57b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 58684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private final int mTouchAcceptanceDelay; 59b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final ArrayMap<String, Long> mSnoozedPackages; 60b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>(); 61b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final int mDefaultSnoozeLengthMs; 62b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final Handler mHandler = new Handler(); 63b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() { 64b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 65b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private Stack<HeadsUpEntry> mPoolObjects = new Stack<>(); 66b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 67b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 68b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public HeadsUpEntry acquire() { 69b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (!mPoolObjects.isEmpty()) { 70b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mPoolObjects.pop(); 71b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 72b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return new HeadsUpEntry(); 73b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 74b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 75b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 76b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean release(HeadsUpEntry instance) { 77684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek instance.reset(); 78b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mPoolObjects.push(instance); 79b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 80b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 81b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek }; 82b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 83737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private final View mStatusBarWindowView; 84737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private final int mStatusBarHeight; 8579d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek private final int mNotificationsTopPadding; 86b659c4f44a839e6ad7ef6834cc0d35954e04460aChris Wren private final Context mContext; 87b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private PhoneStatusBar mBar; 88b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private int mSnoozeLengthMs; 89b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private ContentObserver mSettingsObserver; 90a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>(); 91a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>(); 92b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HashSet<String> mSwipedOutKeys = new HashSet<>(); 93b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private int mUser; 94b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private Clock mClock; 95b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mReleaseOnExpandFinish; 96b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mTrackingHeadsUp; 97b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>(); 98b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mIsExpanded; 99684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private boolean mHasPinnedNotification; 100a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private int[] mTmpTwoArray = new int[2]; 101737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private boolean mHeadsUpGoingAway; 102737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private boolean mWaitingOnCollapseWhenGoingAway; 103737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private boolean mIsObserving; 104b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 105737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek public HeadsUpManager(final Context context, View statusBarWindowView) { 106b659c4f44a839e6ad7ef6834cc0d35954e04460aChris Wren mContext = context; 107b659c4f44a839e6ad7ef6834cc0d35954e04460aChris Wren Resources resources = mContext.getResources(); 108684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay); 109b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozedPackages = new ArrayMap<>(); 110b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms); 111b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozeLengthMs = mDefaultSnoozeLengthMs; 112b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time); 113e53e6bbb82b411f99083e4a6d2071fde45d68d53Selim Cinek mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay); 114b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mClock = new Clock(); 115b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 116b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(), 117b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs); 118b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSettingsObserver = new ContentObserver(mHandler) { 119b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 120b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void onChange(boolean selfChange) { 121b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final int packageSnoozeLengthMs = Settings.Global.getInt( 122b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1); 123b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) { 124b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozeLengthMs = packageSnoozeLengthMs; 125b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs); 126b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 127b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 128b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek }; 129b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek context.getContentResolver().registerContentObserver( 130b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false, 131b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSettingsObserver); 132737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView = statusBarWindowView; 133737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarHeight = resources.getDimensionPixelSize( 134737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek com.android.internal.R.dimen.status_bar_height); 13579d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek mNotificationsTopPadding = context.getResources() 13679d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek .getDimensionPixelSize(R.dimen.notifications_top_padding); 137737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 138737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek 139737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private void updateTouchableRegionListener() { 140737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway 141737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek || mWaitingOnCollapseWhenGoingAway; 142737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (shouldObserve == mIsObserving) { 143737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek return; 144737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 145737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (shouldObserve) { 146737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); 147737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.requestLayout(); 148737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } else { 149737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this); 150737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 151737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mIsObserving = shouldObserve; 152b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 153b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 154b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setBar(PhoneStatusBar bar) { 155b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mBar = bar; 156b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 157b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 158b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void addListener(OnHeadsUpChangedListener listener) { 159b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mListeners.add(listener); 160b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 161b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 162b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public PhoneStatusBar getBar() { 163b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mBar; 164b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 165b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 166b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 167b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Called when posting a new notification to the heads up. 168b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 169b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void showNotification(NotificationData.Entry headsUp) { 170b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "showNotification"); 171b659c4f44a839e6ad7ef6834cc0d35954e04460aChris Wren MetricsLogger.count(mContext, "note_peek", 1); 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; 248737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 249684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (OnHeadsUpChangedListener listener : mListeners) { 250b349af573121cc659d775696066a13ed7101c308John Spurlock listener.onHeadsUpPinnedModeChanged(hasPinnedNotification); 251b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 252b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 253b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 254b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 255b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * React to the removal of the notification in the heads up. 256b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * 257b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * @return true if the notification was removed and false if it still needs to be kept around 258b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * for a bit since it wasn't shown long enough 259b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 260b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean removeNotification(String key) { 261b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "remove"); 262b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (wasShownLongEnough(key)) { 263b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek releaseImmediately(key); 264b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 265b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else { 266684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek getHeadsUpEntry(key).removeAsSoonAsPossible(); 267b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 268b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 269b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 270b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 271b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean wasShownLongEnough(String key) { 272b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); 273b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry topEntry = getTopEntry(); 274b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (mSwipedOutKeys.contains(key)) { 275b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek // We always instantly dismiss views being manually swiped out. 276b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSwipedOutKeys.remove(key); 277b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 278b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 279b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (headsUpEntry != topEntry) { 280b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 281b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 282b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return headsUpEntry.wasShownLongEnough(); 283b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 284b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 285b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean isHeadsUp(String key) { 286b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mHeadsUpEntries.containsKey(key); 287b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 288b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 289b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 290b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Push any current Heads Up notification down into the shade. 291b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 292b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void releaseAllImmediately() { 293b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "releaseAllImmediately"); 294684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet()); 295684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : keys) { 296b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek releaseImmediately(key); 297b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 298b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 299b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 300b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void releaseImmediately(String key) { 301b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); 302b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (headsUpEntry == null) { 303b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return; 304b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 305b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek NotificationData.Entry shadeEntry = headsUpEntry.entry; 306b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek removeHeadsUpEntry(shadeEntry); 307b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 308b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 309b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean isSnoozed(String packageName) { 310b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final String key = snoozeKey(packageName, mUser); 311b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek Long snoozedUntil = mSnoozedPackages.get(key); 312b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (snoozedUntil != null) { 313b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (snoozedUntil > SystemClock.elapsedRealtime()) { 314b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, key + " snoozed"); 315b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 316b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 317b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozedPackages.remove(packageName); 318b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 319b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 320b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 321b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 322b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void snooze() { 323684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : mHeadsUpEntries.keySet()) { 324b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 325b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek String packageName = entry.entry.notification.getPackageName(); 326b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozedPackages.put(snoozeKey(packageName, mUser), 327b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek SystemClock.elapsedRealtime() + mSnoozeLengthMs); 328b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 329b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mReleaseOnExpandFinish = true; 330b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 331b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 332b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private static String snoozeKey(String packageName, int user) { 333b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return user + "," + packageName; 334b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 335b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 336b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HeadsUpEntry getHeadsUpEntry(String key) { 337b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mHeadsUpEntries.get(key); 338b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 339b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 340b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public NotificationData.Entry getEntry(String key) { 341b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mHeadsUpEntries.get(key).entry; 342b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 343b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 344a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek public TreeSet<HeadsUpEntry> getSortedEntries() { 345a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return mSortedEntries; 346b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 347b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 348b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public HeadsUpEntry getTopEntry() { 349a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return mSortedEntries.isEmpty() ? null : mSortedEntries.first(); 350b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 351b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 352b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 353684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Decides whether a click is invalid for a notification, i.e it has not been shown long enough 354684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * that a user might have consciously clicked on it. 355684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 356b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * @param key the key of the touched notification 357684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @return whether the touch is invalid and should be discarded 358b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 359b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean shouldSwallowClick(String key) { 3602f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 3612f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek if (entry != null && mClock.currentTimeMillis() < entry.postTime) { 362b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 363b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 364b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 365b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 366b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 367b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 368131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek if (mIsExpanded) { 369131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek // The touchable region is always the full area when expanded 370131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek return; 371131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek } 372737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (mHasPinnedNotification) { 373a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int minX = Integer.MAX_VALUE; 374a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int maxX = 0; 375a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int minY = Integer.MAX_VALUE; 376a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int maxY = 0; 377684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (HeadsUpEntry entry : mSortedEntries) { 378a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek ExpandableNotificationRow row = entry.entry.row; 379684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (row.isPinned()) { 380a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek row.getLocationOnScreen(mTmpTwoArray); 381a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek minX = Math.min(minX, mTmpTwoArray[0]); 382a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek minY = Math.min(minY, 0); 383a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth()); 384a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek maxY = Math.max(maxY, row.getHeadsUpHeight()); 385a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 386a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 387a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek 388a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 38979d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek info.touchableRegion.set(minX, minY, maxX, maxY + mNotificationsTopPadding); 390737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) { 391737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 392737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); 393a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 394b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 395b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 396b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setUser(int user) { 397b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mUser = user; 398b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 399b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 400b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 401b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.println("HeadsUpManager state:"); 402684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay); 403b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs); 404b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" now="); pw.println(SystemClock.elapsedRealtime()); 405b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" mUser="); pw.println(mUser); 406a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek for (HeadsUpEntry entry: mSortedEntries) { 407a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek pw.print(" HeadsUpEntry="); pw.println(entry.entry); 408b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 409b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek int N = mSnoozedPackages.size(); 410b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.println(" snoozed packages: " + N); 411b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (int i = 0; i < N; i++) { 412b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" "); pw.print(mSnoozedPackages.valueAt(i)); 413b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(", "); pw.println(mSnoozedPackages.keyAt(i)); 414b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 415b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 416b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 417b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean hasPinnedHeadsUp() { 418684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek return mHasPinnedNotification; 419b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 420b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 421684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private boolean hasPinnedNotificationInternal() { 422684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : mHeadsUpEntries.keySet()) { 423b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 424684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (entry.entry.row.isPinned()) { 425b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 426b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 427b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 428b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 429b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 430b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 431684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 432684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Notifies that a notification was swiped out and will be removed. 433684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 434684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param key the notification key 435684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 436684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void addSwipedOutNotification(String key) { 437b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSwipedOutKeys.add(key); 438b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 439b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 440684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void unpinAll() { 441684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : mHeadsUpEntries.keySet()) { 442b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 443684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek setEntryPinned(entry, false /* isPinned */); 444b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 445b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 446b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 447b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void onExpandingFinished() { 448b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (mReleaseOnExpandFinish) { 449b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek releaseAllImmediately(); 450b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mReleaseOnExpandFinish = false; 451b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else { 452b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) { 453b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek removeHeadsUpEntry(entry); 454b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 455b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 456acd0df65dd8be97aae5617c9a8346d4a4ab88abdSelim Cinek mEntriesToRemoveAfterExpand.clear(); 457b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 458b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 459b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setTrackingHeadsUp(boolean trackingHeadsUp) { 460b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mTrackingHeadsUp = trackingHeadsUp; 461b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 462b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 463b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setIsExpanded(boolean isExpanded) { 4641f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek if (isExpanded != mIsExpanded) { 4651f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek mIsExpanded = isExpanded; 4661f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek if (isExpanded) { 467737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek // make sure our state is sane 468737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mWaitingOnCollapseWhenGoingAway = false; 469737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mHeadsUpGoingAway = false; 470737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 4711f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 4721f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 473b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 474b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 475b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public int getTopHeadsUpHeight() { 476b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry topEntry = getTopEntry(); 477b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0; 478b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 479b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 480684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 481684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Compare two entries and decide how they should be ranked. 482684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 483684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @return -1 if the first argument should be ranked higher than the second, 1 if the second 484684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * one should be ranked higher and 0 if they are equal. 485684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 486fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek public int compare(NotificationData.Entry a, NotificationData.Entry b) { 487fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek HeadsUpEntry aEntry = getHeadsUpEntry(a.key); 488fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek HeadsUpEntry bEntry = getHeadsUpEntry(b.key); 489fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek if (aEntry == null || bEntry == null) { 490fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek return aEntry == null ? 1 : -1; 491fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek } 492fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek return aEntry.compareTo(bEntry); 493fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek } 494fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek 495737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek /** 496737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * Set that we are exiting the headsUp pinned mode, but some notifications might still be 497737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * animating out. This is used to keep the touchable regions in a sane state. 498737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek */ 499737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek public void setHeadsUpGoingAway(boolean headsUpGoingAway) { 500737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (headsUpGoingAway != mHeadsUpGoingAway) { 501737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mHeadsUpGoingAway = headsUpGoingAway; 502737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (!headsUpGoingAway) { 503737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek waitForStatusBarLayout(); 504737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 505737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 506737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 507737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 508737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek 509737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek /** 510737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * We need to wait on the whole panel to collapse, before we can remove the touchable region 511737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * listener. 512737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek */ 513737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private void waitForStatusBarLayout() { 514737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mWaitingOnCollapseWhenGoingAway = true; 515737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 516737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek @Override 517737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek public void onLayoutChange(View v, int left, int top, int right, int bottom, 518737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek int oldLeft, 519737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek int oldTop, int oldRight, int oldBottom) { 520737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) { 521737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.removeOnLayoutChangeListener(this); 522737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mWaitingOnCollapseWhenGoingAway = false; 523737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 524737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 525737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 526737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek }); 527737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 528737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek 529684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 530684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * This represents a notification and how long it is in a heads up mode. It also manages its 531684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * lifecycle automatically when created. 532684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 533b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public class HeadsUpEntry implements Comparable<HeadsUpEntry> { 534b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public NotificationData.Entry entry; 535b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public long postTime; 536b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public long earliestRemovaltime; 537b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private Runnable mRemoveHeadsUpRunnable; 538b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 539b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setEntry(final NotificationData.Entry entry) { 540b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek this.entry = entry; 541b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 542b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek // The actual post time will be just after the heads-up really slided in 543684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay; 544b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mRemoveHeadsUpRunnable = new Runnable() { 545b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 546b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void run() { 547b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (!mTrackingHeadsUp) { 548b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek removeHeadsUpEntry(entry); 549b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else { 550b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mEntriesToRemoveAfterExpand.add(entry); 551b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 552b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 553b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek }; 554b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek updateEntry(); 555b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 556b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 557b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void updateEntry() { 558f87baef1b18f2564664a73f78859f23f92ad1d26Selim Cinek mSortedEntries.remove(HeadsUpEntry.this); 559b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek long currentTime = mClock.currentTimeMillis(); 560b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek earliestRemovaltime = currentTime + mMinimumDisplayTime; 56131d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek postTime = Math.max(postTime, currentTime); 562684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek removeAutoRemovalCallbacks(); 563131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek if (!hasFullScreenIntent(entry)) { 56431d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek long finishTime = postTime + mHeadsUpNotificationDecay; 56531d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime); 56631d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay); 56731d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek } 568f87baef1b18f2564664a73f78859f23f92ad1d26Selim Cinek mSortedEntries.add(HeadsUpEntry.this); 569b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 570b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 571b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 572b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public int compareTo(HeadsUpEntry o) { 573a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return postTime < o.postTime ? 1 574f87baef1b18f2564664a73f78859f23f92ad1d26Selim Cinek : postTime == o.postTime ? entry.key.compareTo(o.entry.key) 575a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek : -1; 576b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 577b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 578684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void removeAutoRemovalCallbacks() { 579b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHandler.removeCallbacks(mRemoveHeadsUpRunnable); 580b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 581b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 582b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean wasShownLongEnough() { 583b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return earliestRemovaltime < mClock.currentTimeMillis(); 584b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 585b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 586684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void removeAsSoonAsPossible() { 587684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek removeAutoRemovalCallbacks(); 588b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHandler.postDelayed(mRemoveHeadsUpRunnable, 589b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek earliestRemovaltime - mClock.currentTimeMillis()); 590b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 591684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 592684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void reset() { 593684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek removeAutoRemovalCallbacks(); 594684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek entry = null; 595684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mRemoveHeadsUpRunnable = null; 596684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek } 597b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 598b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 599b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public static class Clock { 600b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public long currentTimeMillis() { 601b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return SystemClock.elapsedRealtime(); 602b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 603b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 604b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 605b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public interface OnHeadsUpChangedListener { 606684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 607684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * The state whether there exist pinned heads-ups or not changed. 608684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 609684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param inPinnedMode whether there are any pinned heads-ups 610684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 611b349af573121cc659d775696066a13ed7101c308John Spurlock void onHeadsUpPinnedModeChanged(boolean inPinnedMode); 612684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 613684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 614684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A notification was just pinned to the top. 615684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 616684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek void onHeadsUpPinned(ExpandableNotificationRow headsUp); 617684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 618684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 619684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A notification was just unpinned from the top. 620684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 621684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek void onHeadsUpUnPinned(ExpandableNotificationRow headsUp); 622684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 623684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 624684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A notification just became a heads up or turned back to its normal state. 625684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 626684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param entry the entry of the changed notification 627684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param isHeadsUp whether the notification is now a headsUp notification 628684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 629684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp); 630b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 631b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek} 632