HeadsUpManager.java revision f87baef1b18f2564664a73f78859f23f92ad1d26
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 32b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.R; 33a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekimport com.android.systemui.statusbar.ExpandableNotificationRow; 34b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.NotificationData; 35b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport com.android.systemui.statusbar.phone.PhoneStatusBar; 36b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 37b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.io.FileDescriptor; 38b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.io.PrintWriter; 39684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinekimport java.util.ArrayList; 40a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekimport java.util.HashMap; 41b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.util.HashSet; 42b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinekimport java.util.Stack; 43a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekimport java.util.TreeSet; 44b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 45684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek/** 46684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A manager which handles heads up notifications which is a special mode where 47684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * they simply peek from the top of the screen. 48684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 49a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinekpublic class HeadsUpManager implements ViewTreeObserver.OnComputeInternalInsetsListener { 50b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private static final String TAG = "HeadsUpManager"; 51b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private static final boolean DEBUG = false; 52b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private static final String SETTING_HEADS_UP_SNOOZE_LENGTH_MS = "heads_up_snooze_length_ms"; 53b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 54b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final int mHeadsUpNotificationDecay; 55b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final int mMinimumDisplayTime; 56b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 57684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private final int mTouchAcceptanceDelay; 58b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final ArrayMap<String, Long> mSnoozedPackages; 59b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final HashSet<OnHeadsUpChangedListener> mListeners = new HashSet<>(); 60b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final int mDefaultSnoozeLengthMs; 61b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final Handler mHandler = new Handler(); 62b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private final Pools.Pool<HeadsUpEntry> mEntryPool = new Pools.Pool<HeadsUpEntry>() { 63b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 64b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private Stack<HeadsUpEntry> mPoolObjects = new Stack<>(); 65b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 66b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 67b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public HeadsUpEntry acquire() { 68b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (!mPoolObjects.isEmpty()) { 69b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mPoolObjects.pop(); 70b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 71b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return new HeadsUpEntry(); 72b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 73b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 74b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 75b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean release(HeadsUpEntry instance) { 76684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek instance.reset(); 77b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mPoolObjects.push(instance); 78b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 79b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 80b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek }; 81b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 82737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private final View mStatusBarWindowView; 83737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private final int mStatusBarHeight; 8479d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek private final int mNotificationsTopPadding; 85b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private PhoneStatusBar mBar; 86b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private int mSnoozeLengthMs; 87b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private ContentObserver mSettingsObserver; 88a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>(); 89a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>(); 90b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HashSet<String> mSwipedOutKeys = new HashSet<>(); 91b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private int mUser; 92b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private Clock mClock; 93b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mReleaseOnExpandFinish; 94b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mTrackingHeadsUp; 95b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>(); 96b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mIsExpanded; 97684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private boolean mHasPinnedNotification; 98a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private int[] mTmpTwoArray = new int[2]; 99737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private boolean mHeadsUpGoingAway; 100737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private boolean mWaitingOnCollapseWhenGoingAway; 101737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private boolean mIsObserving; 102b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 103737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek public HeadsUpManager(final Context context, View statusBarWindowView) { 104b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek Resources resources = context.getResources(); 105684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay); 106b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozedPackages = new ArrayMap<>(); 107b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms); 108b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozeLengthMs = mDefaultSnoozeLengthMs; 109b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time); 110e53e6bbb82b411f99083e4a6d2071fde45d68d53Selim Cinek mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay); 111b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mClock = new Clock(); 112b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 113b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(), 114b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs); 115b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSettingsObserver = new ContentObserver(mHandler) { 116b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 117b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void onChange(boolean selfChange) { 118b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final int packageSnoozeLengthMs = Settings.Global.getInt( 119b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1); 120b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) { 121b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozeLengthMs = packageSnoozeLengthMs; 122b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs); 123b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 124b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 125b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek }; 126b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek context.getContentResolver().registerContentObserver( 127b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false, 128b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSettingsObserver); 129737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView = statusBarWindowView; 130737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarHeight = resources.getDimensionPixelSize( 131737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek com.android.internal.R.dimen.status_bar_height); 13279d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek mNotificationsTopPadding = context.getResources() 13379d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek .getDimensionPixelSize(R.dimen.notifications_top_padding); 134737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 135737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek 136737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private void updateTouchableRegionListener() { 137737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway 138737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek || mWaitingOnCollapseWhenGoingAway; 139737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (shouldObserve == mIsObserving) { 140737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek return; 141737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 142737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (shouldObserve) { 143737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); 144737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.requestLayout(); 145737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } else { 146737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this); 147737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 148737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mIsObserving = shouldObserve; 149b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 150b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 151b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setBar(PhoneStatusBar bar) { 152b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mBar = bar; 153b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 154b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 155b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void addListener(OnHeadsUpChangedListener listener) { 156b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mListeners.add(listener); 157b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 158b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 159b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public PhoneStatusBar getBar() { 160b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mBar; 161b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 162b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 163b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 164b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Called when posting a new notification to the heads up. 165b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 166b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void showNotification(NotificationData.Entry headsUp) { 167b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "showNotification"); 168b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek addHeadsUpEntry(headsUp); 169b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek updateNotification(headsUp, true); 170b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUp.setInterruption(); 171b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 172b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 173b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 174b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Called when updating or posting a notification to the heads up. 175b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 176b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void updateNotification(NotificationData.Entry headsUp, boolean alert) { 177b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "updateNotification"); 178b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 179b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */); 180b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 181b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 182b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (alert) { 183b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key); 184b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUpEntry.updateEntry(); 185131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp)); 186b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 187b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 188b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 189b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private void addHeadsUpEntry(NotificationData.Entry entry) { 190b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = mEntryPool.acquire(); 191a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek 192a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek // This will also add the entry to the sortedList 193b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUpEntry.setEntry(entry); 194b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHeadsUpEntries.put(entry.key, headsUpEntry); 195a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek entry.row.setHeadsUp(true); 196131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(entry)); 197b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (OnHeadsUpChangedListener listener : mListeners) { 198684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek listener.onHeadsUpStateChanged(entry, true); 199b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 200b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 201b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 202b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 203131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) { 204131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek return !mIsExpanded || hasFullScreenIntent(entry); 205131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek } 206131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek 207131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek private boolean hasFullScreenIntent(NotificationData.Entry entry) { 208131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek return entry.notification.getNotification().fullScreenIntent != null; 209131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek } 210131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek 211684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) { 2121f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek ExpandableNotificationRow row = headsUpEntry.entry.row; 213684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (row.isPinned() != isPinned) { 214684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek row.setPinned(isPinned); 215684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek updatePinnedMode(); 216684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (OnHeadsUpChangedListener listener : mListeners) { 217684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (isPinned) { 218684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek listener.onHeadsUpPinned(row); 219684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek } else { 220684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek listener.onHeadsUpUnPinned(row); 2211f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 2221f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 2231f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 2241f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 2251f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek 226b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private void removeHeadsUpEntry(NotificationData.Entry entry) { 227b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key); 228a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mSortedEntries.remove(remove); 229b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 230b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek entry.row.setHeadsUp(false); 231684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek setEntryPinned(remove, false /* isPinned */); 232b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (OnHeadsUpChangedListener listener : mListeners) { 233684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek listener.onHeadsUpStateChanged(entry, false); 234b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 235684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mEntryPool.release(remove); 236b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 237b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 238684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private void updatePinnedMode() { 239684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek boolean hasPinnedNotification = hasPinnedNotificationInternal(); 240684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (hasPinnedNotification == mHasPinnedNotification) { 241b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return; 242b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 243684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mHasPinnedNotification = hasPinnedNotification; 244737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 245684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (OnHeadsUpChangedListener listener : mListeners) { 246b349af573121cc659d775696066a13ed7101c308John Spurlock listener.onHeadsUpPinnedModeChanged(hasPinnedNotification); 247b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 248b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 249b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 250b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 251b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * React to the removal of the notification in the heads up. 252b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * 253b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * @return true if the notification was removed and false if it still needs to be kept around 254b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * for a bit since it wasn't shown long enough 255b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 256b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean removeNotification(String key) { 257b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "remove"); 258b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (wasShownLongEnough(key)) { 259b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek releaseImmediately(key); 260b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 261b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else { 262684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek getHeadsUpEntry(key).removeAsSoonAsPossible(); 263b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 264b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 265b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 266b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 267b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean wasShownLongEnough(String key) { 268b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); 269b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry topEntry = getTopEntry(); 270b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (mSwipedOutKeys.contains(key)) { 271b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek // We always instantly dismiss views being manually swiped out. 272b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSwipedOutKeys.remove(key); 273b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 274b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 275b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (headsUpEntry != topEntry) { 276b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 277b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 278b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return headsUpEntry.wasShownLongEnough(); 279b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 280b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 281b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean isHeadsUp(String key) { 282b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mHeadsUpEntries.containsKey(key); 283b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 284b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 285b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 286b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Push any current Heads Up notification down into the shade. 287b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 288b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void releaseAllImmediately() { 289b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "releaseAllImmediately"); 290684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet()); 291684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : keys) { 292b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek releaseImmediately(key); 293b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 294b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 295b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 296b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void releaseImmediately(String key) { 297b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); 298b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (headsUpEntry == null) { 299b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return; 300b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 301b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek NotificationData.Entry shadeEntry = headsUpEntry.entry; 302b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek removeHeadsUpEntry(shadeEntry); 303b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 304b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 305b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean isSnoozed(String packageName) { 306b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final String key = snoozeKey(packageName, mUser); 307b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek Long snoozedUntil = mSnoozedPackages.get(key); 308b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (snoozedUntil != null) { 309b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (snoozedUntil > SystemClock.elapsedRealtime()) { 310b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, key + " snoozed"); 311b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 312b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 313b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozedPackages.remove(packageName); 314b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 315b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 316b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 317b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 318b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void snooze() { 319684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : mHeadsUpEntries.keySet()) { 320b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 321b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek String packageName = entry.entry.notification.getPackageName(); 322b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozedPackages.put(snoozeKey(packageName, mUser), 323b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek SystemClock.elapsedRealtime() + mSnoozeLengthMs); 324b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 325b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mReleaseOnExpandFinish = true; 326b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 327b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 328b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private static String snoozeKey(String packageName, int user) { 329b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return user + "," + packageName; 330b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 331b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 332b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HeadsUpEntry getHeadsUpEntry(String key) { 333b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mHeadsUpEntries.get(key); 334b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 335b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 336b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public NotificationData.Entry getEntry(String key) { 337b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mHeadsUpEntries.get(key).entry; 338b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 339b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 340a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek public TreeSet<HeadsUpEntry> getSortedEntries() { 341a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return mSortedEntries; 342b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 343b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 344b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public HeadsUpEntry getTopEntry() { 345a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return mSortedEntries.isEmpty() ? null : mSortedEntries.first(); 346b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 347b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 348b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 349684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Decides whether a click is invalid for a notification, i.e it has not been shown long enough 350684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * that a user might have consciously clicked on it. 351684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 352b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * @param key the key of the touched notification 353684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @return whether the touch is invalid and should be discarded 354b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 355b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean shouldSwallowClick(String key) { 3562f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 3572f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek if (entry != null && mClock.currentTimeMillis() < entry.postTime) { 358b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 359b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 360b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 361b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 362b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 363b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 364131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek if (mIsExpanded) { 365131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek // The touchable region is always the full area when expanded 366131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek return; 367131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek } 368737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (mHasPinnedNotification) { 369a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int minX = Integer.MAX_VALUE; 370a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int maxX = 0; 371a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int minY = Integer.MAX_VALUE; 372a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int maxY = 0; 373684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (HeadsUpEntry entry : mSortedEntries) { 374a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek ExpandableNotificationRow row = entry.entry.row; 375684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (row.isPinned()) { 376a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek row.getLocationOnScreen(mTmpTwoArray); 377a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek minX = Math.min(minX, mTmpTwoArray[0]); 378a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek minY = Math.min(minY, 0); 379a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth()); 380a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek maxY = Math.max(maxY, row.getHeadsUpHeight()); 381a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 382a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 383a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek 384a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 38579d79c455beecf7a70460575ab21d9bf49767e2cSelim Cinek info.touchableRegion.set(minX, minY, maxX, maxY + mNotificationsTopPadding); 386737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) { 387737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 388737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); 389a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 390b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 391b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 392b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setUser(int user) { 393b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mUser = user; 394b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 395b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 396b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 397b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.println("HeadsUpManager state:"); 398684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay); 399b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs); 400b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" now="); pw.println(SystemClock.elapsedRealtime()); 401b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" mUser="); pw.println(mUser); 402a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek for (HeadsUpEntry entry: mSortedEntries) { 403a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek pw.print(" HeadsUpEntry="); pw.println(entry.entry); 404b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 405b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek int N = mSnoozedPackages.size(); 406b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.println(" snoozed packages: " + N); 407b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (int i = 0; i < N; i++) { 408b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" "); pw.print(mSnoozedPackages.valueAt(i)); 409b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(", "); pw.println(mSnoozedPackages.keyAt(i)); 410b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 411b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 412b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 413b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean hasPinnedHeadsUp() { 414684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek return mHasPinnedNotification; 415b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 416b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 417684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private boolean hasPinnedNotificationInternal() { 418684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : mHeadsUpEntries.keySet()) { 419b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 420684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (entry.entry.row.isPinned()) { 421b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 422b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 423b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 424b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 425b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 426b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 427684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 428684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Notifies that a notification was swiped out and will be removed. 429684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 430684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param key the notification key 431684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 432684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void addSwipedOutNotification(String key) { 433b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSwipedOutKeys.add(key); 434b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 435b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 436684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void unpinAll() { 437684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : mHeadsUpEntries.keySet()) { 438b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 439684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek setEntryPinned(entry, false /* isPinned */); 440b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 441b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 442b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 443b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void onExpandingFinished() { 444b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (mReleaseOnExpandFinish) { 445b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek releaseAllImmediately(); 446b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mReleaseOnExpandFinish = false; 447b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else { 448b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) { 449b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek removeHeadsUpEntry(entry); 450b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 451b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mEntriesToRemoveAfterExpand.clear(); 452b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 453b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 454b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 455b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setTrackingHeadsUp(boolean trackingHeadsUp) { 456b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mTrackingHeadsUp = trackingHeadsUp; 457b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 458b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 459b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setIsExpanded(boolean isExpanded) { 4601f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek if (isExpanded != mIsExpanded) { 4611f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek mIsExpanded = isExpanded; 4621f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek if (isExpanded) { 463737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek // make sure our state is sane 464737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mWaitingOnCollapseWhenGoingAway = false; 465737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mHeadsUpGoingAway = false; 466737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 4671f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 4681f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 469b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 470b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 471b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public int getTopHeadsUpHeight() { 472b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry topEntry = getTopEntry(); 473b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0; 474b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 475b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 476684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 477684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Compare two entries and decide how they should be ranked. 478684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 479684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @return -1 if the first argument should be ranked higher than the second, 1 if the second 480684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * one should be ranked higher and 0 if they are equal. 481684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 482fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek public int compare(NotificationData.Entry a, NotificationData.Entry b) { 483fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek HeadsUpEntry aEntry = getHeadsUpEntry(a.key); 484fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek HeadsUpEntry bEntry = getHeadsUpEntry(b.key); 485fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek if (aEntry == null || bEntry == null) { 486fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek return aEntry == null ? 1 : -1; 487fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek } 488fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek return aEntry.compareTo(bEntry); 489fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek } 490fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek 491737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek /** 492737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * Set that we are exiting the headsUp pinned mode, but some notifications might still be 493737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * animating out. This is used to keep the touchable regions in a sane state. 494737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek */ 495737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek public void setHeadsUpGoingAway(boolean headsUpGoingAway) { 496737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (headsUpGoingAway != mHeadsUpGoingAway) { 497737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mHeadsUpGoingAway = headsUpGoingAway; 498737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (!headsUpGoingAway) { 499737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek waitForStatusBarLayout(); 500737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 501737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 502737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 503737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 504737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek 505737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek /** 506737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * We need to wait on the whole panel to collapse, before we can remove the touchable region 507737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * listener. 508737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek */ 509737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private void waitForStatusBarLayout() { 510737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mWaitingOnCollapseWhenGoingAway = true; 511737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 512737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek @Override 513737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek public void onLayoutChange(View v, int left, int top, int right, int bottom, 514737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek int oldLeft, 515737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek int oldTop, int oldRight, int oldBottom) { 516737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) { 517737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.removeOnLayoutChangeListener(this); 518737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mWaitingOnCollapseWhenGoingAway = false; 519737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 520737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 521737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 522737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek }); 523737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 524737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek 525684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 526684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 527684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * This represents a notification and how long it is in a heads up mode. It also manages its 528684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * lifecycle automatically when created. 529684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 530b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public class HeadsUpEntry implements Comparable<HeadsUpEntry> { 531b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public NotificationData.Entry entry; 532b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public long postTime; 533b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public long earliestRemovaltime; 534b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private Runnable mRemoveHeadsUpRunnable; 535b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 536b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setEntry(final NotificationData.Entry entry) { 537b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek this.entry = entry; 538b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 539b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek // The actual post time will be just after the heads-up really slided in 540684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay; 541b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mRemoveHeadsUpRunnable = new Runnable() { 542b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 543b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void run() { 544b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (!mTrackingHeadsUp) { 545b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek removeHeadsUpEntry(entry); 546b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else { 547b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mEntriesToRemoveAfterExpand.add(entry); 548b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 549b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 550b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek }; 551b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek updateEntry(); 552b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 553b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 554b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void updateEntry() { 555f87baef1b18f2564664a73f78859f23f92ad1d26Selim Cinek mSortedEntries.remove(HeadsUpEntry.this); 556b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek long currentTime = mClock.currentTimeMillis(); 557b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek earliestRemovaltime = currentTime + mMinimumDisplayTime; 55831d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek postTime = Math.max(postTime, currentTime); 559684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek removeAutoRemovalCallbacks(); 560131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek if (!hasFullScreenIntent(entry)) { 56131d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek long finishTime = postTime + mHeadsUpNotificationDecay; 56231d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime); 56331d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay); 56431d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek } 565f87baef1b18f2564664a73f78859f23f92ad1d26Selim Cinek mSortedEntries.add(HeadsUpEntry.this); 566b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 567b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 568b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 569b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public int compareTo(HeadsUpEntry o) { 570a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return postTime < o.postTime ? 1 571f87baef1b18f2564664a73f78859f23f92ad1d26Selim Cinek : postTime == o.postTime ? entry.key.compareTo(o.entry.key) 572a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek : -1; 573b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 574b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 575684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void removeAutoRemovalCallbacks() { 576b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHandler.removeCallbacks(mRemoveHeadsUpRunnable); 577b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 578b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 579b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean wasShownLongEnough() { 580b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return earliestRemovaltime < mClock.currentTimeMillis(); 581b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 582b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 583684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void removeAsSoonAsPossible() { 584684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek removeAutoRemovalCallbacks(); 585b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHandler.postDelayed(mRemoveHeadsUpRunnable, 586b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek earliestRemovaltime - mClock.currentTimeMillis()); 587b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 588684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 589684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void reset() { 590684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek removeAutoRemovalCallbacks(); 591684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek entry = null; 592684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mRemoveHeadsUpRunnable = null; 593684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek } 594b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 595b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 596b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public static class Clock { 597b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public long currentTimeMillis() { 598b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return SystemClock.elapsedRealtime(); 599b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 600b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 601b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 602b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public interface OnHeadsUpChangedListener { 603684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 604684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * The state whether there exist pinned heads-ups or not changed. 605684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 606684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param inPinnedMode whether there are any pinned heads-ups 607684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 608b349af573121cc659d775696066a13ed7101c308John Spurlock void onHeadsUpPinnedModeChanged(boolean inPinnedMode); 609684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 610684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 611684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A notification was just pinned to the top. 612684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 613684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek void onHeadsUpPinned(ExpandableNotificationRow headsUp); 614684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 615684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 616684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A notification was just unpinned from the top. 617684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 618684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek void onHeadsUpUnPinned(ExpandableNotificationRow headsUp); 619684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 620684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 621684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A notification just became a heads up or turned back to its normal state. 622684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 623684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param entry the entry of the changed notification 624684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param isHeadsUp whether the notification is now a headsUp notification 625684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 626684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp); 627b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 628b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek} 629