1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.systemui.statusbar.notification.interruption;
18 
19 import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_HEADS_UP;
20 
21 import android.util.ArrayMap;
22 
23 import androidx.annotation.Nullable;
24 import androidx.core.os.CancellationSignal;
25 
26 import com.android.internal.util.NotificationMessagingUtil;
27 import com.android.systemui.dagger.SysUISingleton;
28 import com.android.systemui.statusbar.NotificationPresenter;
29 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
30 import com.android.systemui.statusbar.notification.collection.coordinator.HeadsUpCoordinator;
31 import com.android.systemui.statusbar.notification.row.NotifBindPipeline.BindCallback;
32 import com.android.systemui.statusbar.notification.row.RowContentBindParams;
33 import com.android.systemui.statusbar.notification.row.RowContentBindStage;
34 
35 import java.util.Map;
36 
37 import javax.inject.Inject;
38 
39 /**
40  * Wrapper around heads up view binding logic. {@link HeadsUpViewBinder} is responsible for
41  * figuring out the right heads up inflation parameters and inflating/freeing the heads up
42  * content view.
43  *
44  * TODO: This should be moved into {@link HeadsUpCoordinator} when the old pipeline is deprecated.
45  */
46 @SysUISingleton
47 public class HeadsUpViewBinder {
48     private final RowContentBindStage mStage;
49     private final NotificationMessagingUtil mNotificationMessagingUtil;
50     private final Map<NotificationEntry, CancellationSignal> mOngoingBindCallbacks =
51             new ArrayMap<>();
52     private final HeadsUpViewBinderLogger mLogger;
53 
54     private NotificationPresenter mNotificationPresenter;
55 
56     @Inject
HeadsUpViewBinder( NotificationMessagingUtil notificationMessagingUtil, RowContentBindStage bindStage, HeadsUpViewBinderLogger logger)57     HeadsUpViewBinder(
58             NotificationMessagingUtil notificationMessagingUtil,
59             RowContentBindStage bindStage, HeadsUpViewBinderLogger logger) {
60         mNotificationMessagingUtil = notificationMessagingUtil;
61         mStage = bindStage;
62         mLogger = logger;
63     }
64 
65     /**
66      * Set notification presenter to determine parameters for heads up view inflation.
67      */
setPresenter(NotificationPresenter presenter)68     public void setPresenter(NotificationPresenter presenter) {
69         mNotificationPresenter = presenter;
70     }
71 
72     /**
73      * Bind heads up view to the notification row.
74      * @param callback callback after heads up view is bound
75      */
bindHeadsUpView(NotificationEntry entry, @Nullable BindCallback callback)76     public void bindHeadsUpView(NotificationEntry entry, @Nullable BindCallback callback) {
77         RowContentBindParams params = mStage.getStageParams(entry);
78         final boolean isImportantMessage = mNotificationMessagingUtil.isImportantMessaging(
79                 entry.getSbn(), entry.getImportance());
80         final boolean useIncreasedHeadsUp = isImportantMessage
81                 && !mNotificationPresenter.isPresenterFullyCollapsed();
82         params.setUseIncreasedHeadsUpHeight(useIncreasedHeadsUp);
83         params.requireContentViews(FLAG_CONTENT_VIEW_HEADS_UP);
84         CancellationSignal signal = mStage.requestRebind(entry, en -> {
85             mLogger.entryBoundSuccessfully(entry);
86             en.getRow().setUsesIncreasedHeadsUpHeight(params.useIncreasedHeadsUpHeight());
87             // requestRebing promises that if we called cancel before this callback would be
88             // invoked, then we will not enter this callback, and because we always cancel before
89             // adding to this map, we know this will remove the correct signal.
90             mOngoingBindCallbacks.remove(entry);
91             if (callback != null) {
92                 callback.onBindFinished(en);
93             }
94         });
95         abortBindCallback(entry);
96         mLogger.startBindingHun(entry);
97         mOngoingBindCallbacks.put(entry, signal);
98     }
99 
100     /**
101      * Abort any callbacks waiting for heads up view binding to finish for a given notification.
102      * @param entry notification with bind in progress
103      */
abortBindCallback(NotificationEntry entry)104     public void abortBindCallback(NotificationEntry entry) {
105         CancellationSignal ongoingBindCallback = mOngoingBindCallbacks.remove(entry);
106         if (ongoingBindCallback != null) {
107             mLogger.currentOngoingBindingAborted(entry);
108             ongoingBindCallback.cancel();
109         }
110     }
111 
112     /**
113      * Unbind the heads up view from the notification row.
114      */
unbindHeadsUpView(NotificationEntry entry)115     public void unbindHeadsUpView(NotificationEntry entry) {
116         abortBindCallback(entry);
117 
118         // params may be null if the notification was already removed from the collection but we let
119         // it stick around during a launch animation. In this case, the heads up view has already
120         // been unbound, so we don't need to unbind it.
121         // TODO(b/253081345): Change this back to getStageParams and remove null check.
122         RowContentBindParams params = mStage.tryGetStageParams(entry);
123         if (params == null) {
124             mLogger.entryBindStageParamsNullOnUnbind(entry);
125             return;
126         }
127 
128         params.markContentViewsFreeable(FLAG_CONTENT_VIEW_HEADS_UP);
129         mLogger.entryContentViewMarkedFreeable(entry);
130         mStage.requestRebind(entry, e -> mLogger.entryUnbound(e));
131     }
132 }
133