/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.systemui.statusbar; import static com.android.systemui.statusbar.notification.row.NotificationContentInflater.FLAG_CONTENT_VIEW_AMBIENT; import android.annotation.NonNull; import android.content.Context; import android.content.res.Resources; import android.util.ArraySet; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.row.NotificationContentInflater.InflationFlag; import javax.inject.Inject; import javax.inject.Singleton; /** * Manager which handles high priority notifications that should "pulse" in when the device is * dozing and/or in AOD. The pulse uses the notification's ambient view and pops in briefly * before automatically dismissing the alert. */ @Singleton public class AmbientPulseManager extends AlertingNotificationManager { protected final ArraySet<OnAmbientChangedListener> mListeners = new ArraySet<>(); @VisibleForTesting protected long mExtensionTime; @Inject public AmbientPulseManager(@NonNull final Context context) { Resources resources = context.getResources(); mAutoDismissNotificationDecay = resources.getInteger(R.integer.ambient_notification_decay); mMinimumDisplayTime = resources.getInteger(R.integer.ambient_notification_minimum_time); mExtensionTime = resources.getInteger(R.integer.ambient_notification_extension_time); } /** * Adds an OnAmbientChangedListener to observe events. */ public void addListener(@NonNull OnAmbientChangedListener listener) { mListeners.add(listener); } /** * Removes the OnAmbientChangedListener from the observer list. */ public void removeListener(@NonNull OnAmbientChangedListener listener) { mListeners.remove(listener); } /** * Extends the lifetime of the currently showing pulsing notification so that the pulse lasts * longer. */ public void extendPulse() { AmbientEntry topEntry = getTopEntry(); if (topEntry == null) { return; } topEntry.extendPulse(); } public @InflationFlag int getContentFlag() { return FLAG_CONTENT_VIEW_AMBIENT; } @Override protected void onAlertEntryAdded(AlertEntry alertEntry) { NotificationEntry entry = alertEntry.mEntry; entry.setAmbientPulsing(true); for (OnAmbientChangedListener listener : mListeners) { listener.onAmbientStateChanged(entry, true); } } @Override protected void onAlertEntryRemoved(AlertEntry alertEntry) { NotificationEntry entry = alertEntry.mEntry; entry.setAmbientPulsing(false); for (OnAmbientChangedListener listener : mListeners) { listener.onAmbientStateChanged(entry, false); } entry.freeContentViewWhenSafe(FLAG_CONTENT_VIEW_AMBIENT); } @Override protected AlertEntry createAlertEntry() { return new AmbientEntry(); } /** * Get the top pulsing entry. This should be the currently showing one if there are multiple. * @return the currently showing entry */ private AmbientEntry getTopEntry() { if (mAlertEntries.isEmpty()) { return null; } AlertEntry topEntry = null; for (AlertEntry entry : mAlertEntries.values()) { if (topEntry == null || entry.compareTo(topEntry) < 0) { topEntry = entry; } } return (AmbientEntry) topEntry; } /** * Observer interface for any changes in the ambient entries. */ public interface OnAmbientChangedListener { /** * Called when an entry starts or stops pulsing. * @param entry the entry that changed * @param isPulsing true if the entry is now pulsing, false otherwise */ void onAmbientStateChanged(@NonNull NotificationEntry entry, boolean isPulsing); } private final class AmbientEntry extends AlertEntry { private boolean extended; /** * Extend the lifetime of the alertEntry so that it auto-removes later. Can only be * extended once. */ private void extendPulse() { if (!extended) { extended = true; updateEntry(false); } } @Override public void reset() { super.reset(); extended = false; } @Override protected long calculateFinishTime() { return super.calculateFinishTime() + (extended ? mExtensionTime : 0); } } }