HeadsUpManager.java revision 131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796
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; 84b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private PhoneStatusBar mBar; 85b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private int mSnoozeLengthMs; 86b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private ContentObserver mSettingsObserver; 87a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private HashMap<String, HeadsUpEntry> mHeadsUpEntries = new HashMap<>(); 88a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private TreeSet<HeadsUpEntry> mSortedEntries = new TreeSet<>(); 89b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HashSet<String> mSwipedOutKeys = new HashSet<>(); 90b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private int mUser; 91b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private Clock mClock; 92b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mReleaseOnExpandFinish; 93b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mTrackingHeadsUp; 94b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HashSet<NotificationData.Entry> mEntriesToRemoveAfterExpand = new HashSet<>(); 95b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean mIsExpanded; 96684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private boolean mHasPinnedNotification; 97a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private int[] mTmpTwoArray = new int[2]; 98737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private boolean mHeadsUpGoingAway; 99737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private boolean mWaitingOnCollapseWhenGoingAway; 100737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private boolean mIsObserving; 101b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 102737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek public HeadsUpManager(final Context context, View statusBarWindowView) { 103b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek Resources resources = context.getResources(); 104684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mTouchAcceptanceDelay = resources.getInteger(R.integer.touch_acceptance_delay); 105b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozedPackages = new ArrayMap<>(); 106b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mDefaultSnoozeLengthMs = resources.getInteger(R.integer.heads_up_default_snooze_length_ms); 107b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozeLengthMs = mDefaultSnoozeLengthMs; 108b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mMinimumDisplayTime = resources.getInteger(R.integer.heads_up_notification_minimum_time); 109e53e6bbb82b411f99083e4a6d2071fde45d68d53Selim Cinek mHeadsUpNotificationDecay = resources.getInteger(R.integer.heads_up_notification_decay); 110b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mClock = new Clock(); 111b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 112b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozeLengthMs = Settings.Global.getInt(context.getContentResolver(), 113b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek SETTING_HEADS_UP_SNOOZE_LENGTH_MS, mDefaultSnoozeLengthMs); 114b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSettingsObserver = new ContentObserver(mHandler) { 115b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 116b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void onChange(boolean selfChange) { 117b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final int packageSnoozeLengthMs = Settings.Global.getInt( 118b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek context.getContentResolver(), SETTING_HEADS_UP_SNOOZE_LENGTH_MS, -1); 119b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (packageSnoozeLengthMs > -1 && packageSnoozeLengthMs != mSnoozeLengthMs) { 120b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozeLengthMs = packageSnoozeLengthMs; 121b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "mSnoozeLengthMs = " + mSnoozeLengthMs); 122b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 123b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 124b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek }; 125b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek context.getContentResolver().registerContentObserver( 126b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek Settings.Global.getUriFor(SETTING_HEADS_UP_SNOOZE_LENGTH_MS), false, 127b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSettingsObserver); 128737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView = statusBarWindowView; 129737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarHeight = resources.getDimensionPixelSize( 130737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek com.android.internal.R.dimen.status_bar_height); 131737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 132737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek 133737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private void updateTouchableRegionListener() { 134737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek boolean shouldObserve = mHasPinnedNotification || mHeadsUpGoingAway 135737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek || mWaitingOnCollapseWhenGoingAway; 136737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (shouldObserve == mIsObserving) { 137737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek return; 138737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 139737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (shouldObserve) { 140737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.getViewTreeObserver().addOnComputeInternalInsetsListener(this); 141737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.requestLayout(); 142737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } else { 143737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.getViewTreeObserver().removeOnComputeInternalInsetsListener(this); 144737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 145737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mIsObserving = shouldObserve; 146b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 147b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 148b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setBar(PhoneStatusBar bar) { 149b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mBar = bar; 150b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 151b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 152b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void addListener(OnHeadsUpChangedListener listener) { 153b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mListeners.add(listener); 154b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 155b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 156b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public PhoneStatusBar getBar() { 157b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mBar; 158b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 159b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 160b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 161b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Called when posting a new notification to the heads up. 162b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 163b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void showNotification(NotificationData.Entry headsUp) { 164b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "showNotification"); 165b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek addHeadsUpEntry(headsUp); 166b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek updateNotification(headsUp, true); 167b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUp.setInterruption(); 168b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 169b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 170b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 171b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Called when updating or posting a notification to the heads up. 172b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 173b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void updateNotification(NotificationData.Entry headsUp, boolean alert) { 174b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "updateNotification"); 175b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 176b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUp.row.setChildrenExpanded(false /* expanded */, false /* animated */); 177b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUp.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 178b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 179b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (alert) { 180b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = mHeadsUpEntries.get(headsUp.key); 181b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUpEntry.updateEntry(); 182131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(headsUp)); 183b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 184b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 185b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 186b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private void addHeadsUpEntry(NotificationData.Entry entry) { 187b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = mEntryPool.acquire(); 188a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek 189a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek // This will also add the entry to the sortedList 190b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek headsUpEntry.setEntry(entry); 191b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHeadsUpEntries.put(entry.key, headsUpEntry); 192a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek entry.row.setHeadsUp(true); 193131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek setEntryPinned(headsUpEntry, shouldHeadsUpBecomePinned(entry)); 194b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (OnHeadsUpChangedListener listener : mListeners) { 195684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek listener.onHeadsUpStateChanged(entry, true); 196b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 197b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 198b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 199b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 200131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek private boolean shouldHeadsUpBecomePinned(NotificationData.Entry entry) { 201131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek return !mIsExpanded || hasFullScreenIntent(entry); 202131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek } 203131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek 204131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek private boolean hasFullScreenIntent(NotificationData.Entry entry) { 205131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek return entry.notification.getNotification().fullScreenIntent != null; 206131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek } 207131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek 208684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private void setEntryPinned(HeadsUpEntry headsUpEntry, boolean isPinned) { 2091f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek ExpandableNotificationRow row = headsUpEntry.entry.row; 210684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (row.isPinned() != isPinned) { 211684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek row.setPinned(isPinned); 212684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek updatePinnedMode(); 213684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (OnHeadsUpChangedListener listener : mListeners) { 214684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (isPinned) { 215684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek listener.onHeadsUpPinned(row); 216684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek } else { 217684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek listener.onHeadsUpUnPinned(row); 2181f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 2191f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 2201f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 2211f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 2221f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek 223b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private void removeHeadsUpEntry(NotificationData.Entry entry) { 224b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry remove = mHeadsUpEntries.remove(entry.key); 225a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mSortedEntries.remove(remove); 226b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek entry.row.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED); 227b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek entry.row.setHeadsUp(false); 228684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek setEntryPinned(remove, false /* isPinned */); 229b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (OnHeadsUpChangedListener listener : mListeners) { 230684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek listener.onHeadsUpStateChanged(entry, false); 231b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 232684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mEntryPool.release(remove); 233b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 234b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 235684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private void updatePinnedMode() { 236684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek boolean hasPinnedNotification = hasPinnedNotificationInternal(); 237684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (hasPinnedNotification == mHasPinnedNotification) { 238b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return; 239b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 240684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mHasPinnedNotification = hasPinnedNotification; 241737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 242684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (OnHeadsUpChangedListener listener : mListeners) { 243b349af573121cc659d775696066a13ed7101c308John Spurlock listener.onHeadsUpPinnedModeChanged(hasPinnedNotification); 244b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 245b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 246b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 247b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 248b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * React to the removal of the notification in the heads up. 249b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * 250b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * @return true if the notification was removed and false if it still needs to be kept around 251b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * for a bit since it wasn't shown long enough 252b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 253b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean removeNotification(String key) { 254b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "remove"); 255b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (wasShownLongEnough(key)) { 256b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek releaseImmediately(key); 257b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 258b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else { 259684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek getHeadsUpEntry(key).removeAsSoonAsPossible(); 260b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 261b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 262b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 263b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 264b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private boolean wasShownLongEnough(String key) { 265b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); 266b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry topEntry = getTopEntry(); 267b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (mSwipedOutKeys.contains(key)) { 268b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek // We always instantly dismiss views being manually swiped out. 269b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSwipedOutKeys.remove(key); 270b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 271b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 272b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (headsUpEntry != topEntry) { 273b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 274b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 275b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return headsUpEntry.wasShownLongEnough(); 276b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 277b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 278b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean isHeadsUp(String key) { 279b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mHeadsUpEntries.containsKey(key); 280b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 281b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 282b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 283b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * Push any current Heads Up notification down into the shade. 284b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 285b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void releaseAllImmediately() { 286b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, "releaseAllImmediately"); 287684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek ArrayList<String> keys = new ArrayList<>(mHeadsUpEntries.keySet()); 288684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : keys) { 289b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek releaseImmediately(key); 290b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 291b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 292b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 293b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void releaseImmediately(String key) { 294b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry headsUpEntry = getHeadsUpEntry(key); 295b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (headsUpEntry == null) { 296b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return; 297b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 298b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek NotificationData.Entry shadeEntry = headsUpEntry.entry; 299b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek removeHeadsUpEntry(shadeEntry); 300b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 301b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 302b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean isSnoozed(String packageName) { 303b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek final String key = snoozeKey(packageName, mUser); 304b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek Long snoozedUntil = mSnoozedPackages.get(key); 305b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (snoozedUntil != null) { 306b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (snoozedUntil > SystemClock.elapsedRealtime()) { 307b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (DEBUG) Log.v(TAG, key + " snoozed"); 308b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 309b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 310b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozedPackages.remove(packageName); 311b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 312b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 313b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 314b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 315b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void snooze() { 316684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : mHeadsUpEntries.keySet()) { 317b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 318b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek String packageName = entry.entry.notification.getPackageName(); 319b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSnoozedPackages.put(snoozeKey(packageName, mUser), 320b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek SystemClock.elapsedRealtime() + mSnoozeLengthMs); 321b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 322b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mReleaseOnExpandFinish = true; 323b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 324b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 325b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private static String snoozeKey(String packageName, int user) { 326b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return user + "," + packageName; 327b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 328b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 329b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private HeadsUpEntry getHeadsUpEntry(String key) { 330b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mHeadsUpEntries.get(key); 331b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 332b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 333b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public NotificationData.Entry getEntry(String key) { 334b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return mHeadsUpEntries.get(key).entry; 335b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 336b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 337a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek public TreeSet<HeadsUpEntry> getSortedEntries() { 338a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return mSortedEntries; 339b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 340b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 341b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public HeadsUpEntry getTopEntry() { 342a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return mSortedEntries.isEmpty() ? null : mSortedEntries.first(); 343b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 344b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 345b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek /** 346684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Decides whether a click is invalid for a notification, i.e it has not been shown long enough 347684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * that a user might have consciously clicked on it. 348684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 349b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek * @param key the key of the touched notification 350684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @return whether the touch is invalid and should be discarded 351b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek */ 352b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean shouldSwallowClick(String key) { 3532f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 3542f6b3fb90f069fdb8502dedf657790bf3d94dbd0Selim Cinek if (entry != null && mClock.currentTimeMillis() < entry.postTime) { 355b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 356b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 357b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 358b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 359b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 360b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) { 361131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek if (mIsExpanded) { 362131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek // The touchable region is always the full area when expanded 363131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek return; 364131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek } 365737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (mHasPinnedNotification) { 366a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int minX = Integer.MAX_VALUE; 367a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int maxX = 0; 368a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int minY = Integer.MAX_VALUE; 369a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek int maxY = 0; 370684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (HeadsUpEntry entry : mSortedEntries) { 371a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek ExpandableNotificationRow row = entry.entry.row; 372684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (row.isPinned()) { 373a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek row.getLocationOnScreen(mTmpTwoArray); 374a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek minX = Math.min(minX, mTmpTwoArray[0]); 375a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek minY = Math.min(minY, 0); 376a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek maxX = Math.max(maxX, mTmpTwoArray[0] + row.getWidth()); 377a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek maxY = Math.max(maxY, row.getHeadsUpHeight()); 378a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 379a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 380a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek 381a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 382a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek info.touchableRegion.set(minX, minY, maxX, maxY); 383737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } else if (mHeadsUpGoingAway || mWaitingOnCollapseWhenGoingAway) { 384737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek info.setTouchableInsets(ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION); 385737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek info.touchableRegion.set(0, 0, mStatusBarWindowView.getWidth(), mStatusBarHeight); 386a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 387b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 388b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 389b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setUser(int user) { 390b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mUser = user; 391b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 392b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 393b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 394b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.println("HeadsUpManager state:"); 395684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek pw.print(" mTouchAcceptanceDelay="); pw.println(mTouchAcceptanceDelay); 396b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" mSnoozeLengthMs="); pw.println(mSnoozeLengthMs); 397b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" now="); pw.println(SystemClock.elapsedRealtime()); 398b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" mUser="); pw.println(mUser); 399a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek for (HeadsUpEntry entry: mSortedEntries) { 400a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek pw.print(" HeadsUpEntry="); pw.println(entry.entry); 401b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 402b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek int N = mSnoozedPackages.size(); 403b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.println(" snoozed packages: " + N); 404b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (int i = 0; i < N; i++) { 405b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(" "); pw.print(mSnoozedPackages.valueAt(i)); 406b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek pw.print(", "); pw.println(mSnoozedPackages.keyAt(i)); 407b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 408b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 409b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 410b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean hasPinnedHeadsUp() { 411684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek return mHasPinnedNotification; 412b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 413b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 414684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek private boolean hasPinnedNotificationInternal() { 415684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : mHeadsUpEntries.keySet()) { 416b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 417684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek if (entry.entry.row.isPinned()) { 418b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return true; 419b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 420b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 421b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return false; 422b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 423b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 424684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 425684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Notifies that a notification was swiped out and will be removed. 426684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 427684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param key the notification key 428684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 429684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void addSwipedOutNotification(String key) { 430b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mSwipedOutKeys.add(key); 431b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 432b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 433684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void unpinAll() { 434684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek for (String key : mHeadsUpEntries.keySet()) { 435b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry entry = mHeadsUpEntries.get(key); 436684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek setEntryPinned(entry, false /* isPinned */); 437b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 438b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 439b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 440b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void onExpandingFinished() { 441b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (mReleaseOnExpandFinish) { 442b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek releaseAllImmediately(); 443b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mReleaseOnExpandFinish = false; 444b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else { 445b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek for (NotificationData.Entry entry : mEntriesToRemoveAfterExpand) { 446b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek removeHeadsUpEntry(entry); 447b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 448b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mEntriesToRemoveAfterExpand.clear(); 449b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 450b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 451b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 452b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setTrackingHeadsUp(boolean trackingHeadsUp) { 453b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mTrackingHeadsUp = trackingHeadsUp; 454b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 455b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 456b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setIsExpanded(boolean isExpanded) { 4571f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek if (isExpanded != mIsExpanded) { 4581f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek mIsExpanded = isExpanded; 4591f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek if (isExpanded) { 460737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek // make sure our state is sane 461737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mWaitingOnCollapseWhenGoingAway = false; 462737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mHeadsUpGoingAway = false; 463737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 4641f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 4651f3f544f21cab3728f749ab66cd6859e9dfcf389Selim Cinek } 466b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 467b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 468b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public int getTopHeadsUpHeight() { 469b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek HeadsUpEntry topEntry = getTopEntry(); 470b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return topEntry != null ? topEntry.entry.row.getHeadsUpHeight() : 0; 471b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 472b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 473684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 474684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * Compare two entries and decide how they should be ranked. 475684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 476684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @return -1 if the first argument should be ranked higher than the second, 1 if the second 477684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * one should be ranked higher and 0 if they are equal. 478684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 479fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek public int compare(NotificationData.Entry a, NotificationData.Entry b) { 480fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek HeadsUpEntry aEntry = getHeadsUpEntry(a.key); 481fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek HeadsUpEntry bEntry = getHeadsUpEntry(b.key); 482fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek if (aEntry == null || bEntry == null) { 483fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek return aEntry == null ? 1 : -1; 484fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek } 485fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek return aEntry.compareTo(bEntry); 486fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek } 487fbe9a44a15addf9d94cd40da56835501241b8d3eSelim Cinek 488737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek /** 489737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * Set that we are exiting the headsUp pinned mode, but some notifications might still be 490737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * animating out. This is used to keep the touchable regions in a sane state. 491737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek */ 492737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek public void setHeadsUpGoingAway(boolean headsUpGoingAway) { 493737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (headsUpGoingAway != mHeadsUpGoingAway) { 494737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mHeadsUpGoingAway = headsUpGoingAway; 495737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (!headsUpGoingAway) { 496737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek waitForStatusBarLayout(); 497737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 498737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 499737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 500737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 501737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek 502737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek /** 503737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * We need to wait on the whole panel to collapse, before we can remove the touchable region 504737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek * listener. 505737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek */ 506737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek private void waitForStatusBarLayout() { 507737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mWaitingOnCollapseWhenGoingAway = true; 508737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() { 509737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek @Override 510737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek public void onLayoutChange(View v, int left, int top, int right, int bottom, 511737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek int oldLeft, 512737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek int oldTop, int oldRight, int oldBottom) { 513737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek if (mStatusBarWindowView.getHeight() <= mStatusBarHeight) { 514737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mStatusBarWindowView.removeOnLayoutChangeListener(this); 515737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek mWaitingOnCollapseWhenGoingAway = false; 516737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek updateTouchableRegionListener(); 517737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 518737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 519737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek }); 520737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek } 521737bff3476a3af8f930d29fccce16d033fbc3efaSelim Cinek 522684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 523684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 524684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * This represents a notification and how long it is in a heads up mode. It also manages its 525684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * lifecycle automatically when created. 526684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 527b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public class HeadsUpEntry implements Comparable<HeadsUpEntry> { 528b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public NotificationData.Entry entry; 529b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public long postTime; 530b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public long earliestRemovaltime; 531b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek private Runnable mRemoveHeadsUpRunnable; 532b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 533b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void setEntry(final NotificationData.Entry entry) { 534b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek this.entry = entry; 535b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 536b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek // The actual post time will be just after the heads-up really slided in 537684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek postTime = mClock.currentTimeMillis() + mTouchAcceptanceDelay; 538b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mRemoveHeadsUpRunnable = new Runnable() { 539b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 540b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void run() { 541b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek if (!mTrackingHeadsUp) { 542b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek removeHeadsUpEntry(entry); 543b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } else { 544b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mEntriesToRemoveAfterExpand.add(entry); 545b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 546b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 547b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek }; 548b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek updateEntry(); 549b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 550b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 551b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public void updateEntry() { 552b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek long currentTime = mClock.currentTimeMillis(); 553b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek earliestRemovaltime = currentTime + mMinimumDisplayTime; 55431d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek postTime = Math.max(postTime, currentTime); 555684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek removeAutoRemovalCallbacks(); 556131c1e2960fa5bdf54bfb6fcd5ac98c9f728f796Selim Cinek if (!hasFullScreenIntent(entry)) { 55731d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek long finishTime = postTime + mHeadsUpNotificationDecay; 55831d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek long removeDelay = Math.max(finishTime - currentTime, mMinimumDisplayTime); 55931d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek mHandler.postDelayed(mRemoveHeadsUpRunnable, removeDelay); 56031d9ef7a402b58b10758da1d71ff5e2181abe8a4Selim Cinek } 561a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek updateSortOrder(HeadsUpEntry.this); 562b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 563b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 564b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek @Override 565b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public int compareTo(HeadsUpEntry o) { 566a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek return postTime < o.postTime ? 1 567b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek : postTime == o.postTime ? 0 568a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek : -1; 569b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 570b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 571684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void removeAutoRemovalCallbacks() { 572b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHandler.removeCallbacks(mRemoveHeadsUpRunnable); 573b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 574b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 575b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public boolean wasShownLongEnough() { 576b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return earliestRemovaltime < mClock.currentTimeMillis(); 577b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 578b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 579684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void removeAsSoonAsPossible() { 580684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek removeAutoRemovalCallbacks(); 581b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek mHandler.postDelayed(mRemoveHeadsUpRunnable, 582b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek earliestRemovaltime - mClock.currentTimeMillis()); 583b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 584684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 585684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek public void reset() { 586684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek removeAutoRemovalCallbacks(); 587684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek entry = null; 588684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek mRemoveHeadsUpRunnable = null; 589684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek } 590b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 591b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 592a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek /** 593a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek * Update the sorted heads up order. 594a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek * 595a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek * @param headsUpEntry the headsUp that changed 596a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek */ 597a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek private void updateSortOrder(HeadsUpEntry headsUpEntry) { 598a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mSortedEntries.remove(headsUpEntry); 599a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek mSortedEntries.add(headsUpEntry); 600a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek } 601a59ecc3401de0c4bf1e13665158f54669f22d06cSelim Cinek 602b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public static class Clock { 603b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public long currentTimeMillis() { 604b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek return SystemClock.elapsedRealtime(); 605b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 606b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 607b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek 608b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek public interface OnHeadsUpChangedListener { 609684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 610684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * The state whether there exist pinned heads-ups or not changed. 611684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 612684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param inPinnedMode whether there are any pinned heads-ups 613684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 614b349af573121cc659d775696066a13ed7101c308John Spurlock void onHeadsUpPinnedModeChanged(boolean inPinnedMode); 615684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 616684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 617684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A notification was just pinned to the top. 618684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 619684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek void onHeadsUpPinned(ExpandableNotificationRow headsUp); 620684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 621684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 622684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A notification was just unpinned from the top. 623684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 624684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek void onHeadsUpUnPinned(ExpandableNotificationRow headsUp); 625684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek 626684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek /** 627684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * A notification just became a heads up or turned back to its normal state. 628684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * 629684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param entry the entry of the changed notification 630684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek * @param isHeadsUp whether the notification is now a headsUp notification 631684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek */ 632684a442b812a5e95d813700ffa2fd17ca72048a7Selim Cinek void onHeadsUpStateChanged(NotificationData.Entry entry, boolean isHeadsUp); 633b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek } 634b8f09cf5533d458868a335ce334e4880b2b0788dSelim Cinek} 635