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.row;
18 
19 import static com.android.systemui.Dependency.ALLOW_NOTIFICATION_LONG_PRESS_NAME;
20 import static com.android.systemui.statusbar.NotificationRemoteInputManager.ENABLE_REMOTE_INPUT;
21 import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
22 
23 import android.view.View;
24 import android.view.ViewGroup;
25 
26 import androidx.annotation.NonNull;
27 
28 import com.android.systemui.R;
29 import com.android.systemui.classifier.FalsingCollector;
30 import com.android.systemui.plugins.FalsingManager;
31 import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
32 import com.android.systemui.plugins.statusbar.StatusBarStateController;
33 import com.android.systemui.shared.plugins.PluginManager;
34 import com.android.systemui.statusbar.NotificationMediaManager;
35 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
36 import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager;
37 import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager;
38 import com.android.systemui.statusbar.notification.collection.render.NodeController;
39 import com.android.systemui.statusbar.notification.logging.NotificationLogger;
40 import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
41 import com.android.systemui.statusbar.notification.row.dagger.AppName;
42 import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
43 import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
44 import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
45 import com.android.systemui.statusbar.phone.KeyguardBypassController;
46 import com.android.systemui.statusbar.policy.HeadsUpManager;
47 import com.android.systemui.util.time.SystemClock;
48 import com.android.systemui.wmshell.BubblesManager;
49 
50 import java.util.List;
51 import java.util.Optional;
52 
53 import javax.inject.Inject;
54 import javax.inject.Named;
55 
56 /**
57  * Controller for {@link ExpandableNotificationRow}.
58  */
59 @NotificationRowScope
60 public class ExpandableNotificationRowController implements NodeController {
61     private final ExpandableNotificationRow mView;
62     private final NotificationListContainer mListContainer;
63     private final ActivatableNotificationViewController mActivatableNotificationViewController;
64     private final NotificationMediaManager mMediaManager;
65     private final PluginManager mPluginManager;
66     private final SystemClock mClock;
67     private final String mAppName;
68     private final String mNotificationKey;
69     private final KeyguardBypassController mKeyguardBypassController;
70     private final GroupMembershipManager mGroupMembershipManager;
71     private final GroupExpansionManager mGroupExpansionManager;
72     private final RowContentBindStage mRowContentBindStage;
73     private final NotificationLogger mNotificationLogger;
74     private final HeadsUpManager mHeadsUpManager;
75     private final ExpandableNotificationRow.OnExpandClickListener mOnExpandClickListener;
76     private final StatusBarStateController mStatusBarStateController;
77 
78     private final ExpandableNotificationRow.ExpansionLogger mExpansionLogger =
79             this::logNotificationExpansion;
80     private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener;
81     private final NotificationGutsManager mNotificationGutsManager;
82     private final OnUserInteractionCallback mOnUserInteractionCallback;
83     private final FalsingManager mFalsingManager;
84     private final FalsingCollector mFalsingCollector;
85     private final boolean mAllowLongPress;
86     private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
87     private final Optional<BubblesManager> mBubblesManagerOptional;
88 
89     private final ExpandableNotificationRowDragController mDragController;
90 
91     @Inject
ExpandableNotificationRowController( ExpandableNotificationRow view, NotificationListContainer listContainer, ActivatableNotificationViewController activatableNotificationViewController, NotificationMediaManager mediaManager, PluginManager pluginManager, SystemClock clock, @AppName String appName, @NotificationKey String notificationKey, KeyguardBypassController keyguardBypassController, GroupMembershipManager groupMembershipManager, GroupExpansionManager groupExpansionManager, RowContentBindStage rowContentBindStage, NotificationLogger notificationLogger, HeadsUpManager headsUpManager, ExpandableNotificationRow.OnExpandClickListener onExpandClickListener, StatusBarStateController statusBarStateController, NotificationGutsManager notificationGutsManager, @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress, OnUserInteractionCallback onUserInteractionCallback, FalsingManager falsingManager, FalsingCollector falsingCollector, PeopleNotificationIdentifier peopleNotificationIdentifier, Optional<BubblesManager> bubblesManagerOptional, ExpandableNotificationRowDragController dragController)92     public ExpandableNotificationRowController(
93             ExpandableNotificationRow view,
94             NotificationListContainer listContainer,
95             ActivatableNotificationViewController activatableNotificationViewController,
96             NotificationMediaManager mediaManager,
97             PluginManager pluginManager,
98             SystemClock clock,
99             @AppName String appName,
100             @NotificationKey String notificationKey,
101             KeyguardBypassController keyguardBypassController,
102             GroupMembershipManager groupMembershipManager,
103             GroupExpansionManager groupExpansionManager,
104             RowContentBindStage rowContentBindStage,
105             NotificationLogger notificationLogger,
106             HeadsUpManager headsUpManager,
107             ExpandableNotificationRow.OnExpandClickListener onExpandClickListener,
108             StatusBarStateController statusBarStateController,
109             NotificationGutsManager notificationGutsManager,
110             @Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
111             OnUserInteractionCallback onUserInteractionCallback,
112             FalsingManager falsingManager,
113             FalsingCollector falsingCollector,
114             PeopleNotificationIdentifier peopleNotificationIdentifier,
115             Optional<BubblesManager> bubblesManagerOptional,
116             ExpandableNotificationRowDragController dragController) {
117         mView = view;
118         mListContainer = listContainer;
119         mActivatableNotificationViewController = activatableNotificationViewController;
120         mMediaManager = mediaManager;
121         mPluginManager = pluginManager;
122         mClock = clock;
123         mAppName = appName;
124         mNotificationKey = notificationKey;
125         mKeyguardBypassController = keyguardBypassController;
126         mGroupMembershipManager = groupMembershipManager;
127         mGroupExpansionManager = groupExpansionManager;
128         mRowContentBindStage = rowContentBindStage;
129         mNotificationLogger = notificationLogger;
130         mHeadsUpManager = headsUpManager;
131         mOnExpandClickListener = onExpandClickListener;
132         mStatusBarStateController = statusBarStateController;
133         mNotificationGutsManager = notificationGutsManager;
134         mOnUserInteractionCallback = onUserInteractionCallback;
135         mFalsingManager = falsingManager;
136         mOnFeedbackClickListener = mNotificationGutsManager::openGuts;
137         mAllowLongPress = allowLongPress;
138         mFalsingCollector = falsingCollector;
139         mPeopleNotificationIdentifier = peopleNotificationIdentifier;
140         mBubblesManagerOptional = bubblesManagerOptional;
141         mDragController = dragController;
142     }
143 
144     /**
145      * Initialize the controller.
146      */
init(NotificationEntry entry)147     public void init(NotificationEntry entry) {
148         mActivatableNotificationViewController.init();
149         mView.initialize(
150                 entry,
151                 mAppName,
152                 mNotificationKey,
153                 mExpansionLogger,
154                 mKeyguardBypassController,
155                 mGroupMembershipManager,
156                 mGroupExpansionManager,
157                 mHeadsUpManager,
158                 mRowContentBindStage,
159                 mOnExpandClickListener,
160                 mMediaManager,
161                 mOnFeedbackClickListener,
162                 mFalsingManager,
163                 mFalsingCollector,
164                 mStatusBarStateController,
165                 mPeopleNotificationIdentifier,
166                 mOnUserInteractionCallback,
167                 mBubblesManagerOptional,
168                 mNotificationGutsManager
169         );
170         mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
171         if (mAllowLongPress) {
172             if (mView.getResources().getBoolean(R.bool.config_notificationToContents)) {
173                 mView.setDragController(mDragController);
174             }
175 
176             mView.setLongPressListener((v, x, y, item) -> {
177                 if (mView.isSummaryWithChildren()) {
178                     mView.expandNotification();
179                     return true;
180                 }
181                 return mNotificationGutsManager.openGuts(v, x, y, item);
182             });
183         }
184         if (ENABLE_REMOTE_INPUT) {
185             mView.setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS);
186         }
187 
188         mView.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
189             @Override
190             public void onViewAttachedToWindow(View v) {
191                 mView.getEntry().setInitializationTime(mClock.elapsedRealtime());
192                 mPluginManager.addPluginListener(mView,
193                         NotificationMenuRowPlugin.class, false /* Allow multiple */);
194                 mView.setOnKeyguard(mStatusBarStateController.getState() == KEYGUARD);
195                 mStatusBarStateController.addCallback(mStatusBarStateListener);
196             }
197 
198             @Override
199             public void onViewDetachedFromWindow(View v) {
200                 mPluginManager.removePluginListener(mView);
201                 mStatusBarStateController.removeCallback(mStatusBarStateListener);
202             }
203         });
204     }
205 
206     private final StatusBarStateController.StateListener mStatusBarStateListener =
207             new StatusBarStateController.StateListener() {
208                 @Override
209                 public void onStateChanged(int newState) {
210                     mView.setOnKeyguard(newState == KEYGUARD);
211                 }
212             };
213 
logNotificationExpansion(String key, boolean userAction, boolean expanded)214     private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
215         mNotificationLogger.onExpansionChanged(key, userAction, expanded);
216     }
217 
218     @Override
219     @NonNull
getNodeLabel()220     public String getNodeLabel() {
221         return mView.getEntry().getKey();
222     }
223 
224     @Override
225     @NonNull
getView()226     public View getView() {
227         return mView;
228     }
229 
230     @Override
getChildAt(int index)231     public View getChildAt(int index) {
232         return mView.getChildNotificationAt(index);
233     }
234 
235     @Override
addChildAt(NodeController child, int index)236     public void addChildAt(NodeController child, int index) {
237         ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView();
238 
239         mView.addChildNotification((ExpandableNotificationRow) child.getView());
240         mListContainer.notifyGroupChildAdded(childView);
241     }
242 
243     @Override
moveChildTo(NodeController child, int index)244     public void moveChildTo(NodeController child, int index) {
245         ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView();
246         mView.removeChildNotification(childView);
247         mView.addChildNotification(childView, index);
248     }
249 
250     @Override
removeChild(NodeController child, boolean isTransfer)251     public void removeChild(NodeController child, boolean isTransfer) {
252         ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView();
253 
254         mView.removeChildNotification(childView);
255         if (!isTransfer) {
256             mListContainer.notifyGroupChildRemoved(childView, mView);
257         }
258     }
259 
260     @Override
getChildCount()261     public int getChildCount() {
262         final List<ExpandableNotificationRow> mChildren = mView.getAttachedChildren();
263         return mChildren != null ? mChildren.size() : 0;
264     }
265 }
266