1 /*
2  * Copyright (C) 2018 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 package com.android.systemui.statusbar.notification;
17 
18 import android.app.Notification;
19 import android.os.PowerManager;
20 import android.service.notification.StatusBarNotification;
21 import android.util.Log;
22 import android.view.View;
23 
24 import com.android.systemui.DejankUtils;
25 import com.android.systemui.power.domain.interactor.PowerInteractor;
26 import com.android.systemui.statusbar.notification.collection.NotificationEntry;
27 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
28 import com.android.wm.shell.bubbles.Bubbles;
29 
30 import java.util.Optional;
31 
32 import javax.inject.Inject;
33 
34 /**
35  * Click handler for generic clicks on notifications. Clicks on specific areas (expansion caret,
36  * app ops icon, etc) are handled elsewhere.
37  */
38 public final class NotificationClicker implements View.OnClickListener {
39     private static final String TAG = "NotificationClicker";
40 
41     private final NotificationClickerLogger mLogger;
42     private final PowerInteractor mPowerInteractor;
43     private final Optional<Bubbles> mBubblesOptional;
44     private final NotificationActivityStarter mNotificationActivityStarter;
45 
46     private ExpandableNotificationRow.OnDragSuccessListener mOnDragSuccessListener =
47             new ExpandableNotificationRow.OnDragSuccessListener() {
48                 @Override
49                 public void onDragSuccess(NotificationEntry entry) {
50                     mNotificationActivityStarter.onDragSuccess(entry);
51                 }
52             };
53 
NotificationClicker( NotificationClickerLogger logger, PowerInteractor powerInteractor, Optional<Bubbles> bubblesOptional, NotificationActivityStarter notificationActivityStarter)54     private NotificationClicker(
55             NotificationClickerLogger logger,
56             PowerInteractor powerInteractor,
57             Optional<Bubbles> bubblesOptional,
58             NotificationActivityStarter notificationActivityStarter) {
59         mLogger = logger;
60         mPowerInteractor = powerInteractor;
61         mBubblesOptional = bubblesOptional;
62         mNotificationActivityStarter = notificationActivityStarter;
63     }
64 
65     @Override
onClick(final View v)66     public void onClick(final View v) {
67         if (!(v instanceof ExpandableNotificationRow)) {
68             Log.e(TAG, "NotificationClicker called on a view that is not a notification row.");
69             return;
70         }
71 
72         mPowerInteractor.wakeUpIfDozing("NOTIFICATION_CLICK", PowerManager.WAKE_REASON_GESTURE);
73 
74         final ExpandableNotificationRow row = (ExpandableNotificationRow) v;
75         final NotificationEntry entry = row.getEntry();
76         mLogger.logOnClick(entry);
77 
78         // Check if the notification is displaying the menu, if so slide notification back
79         if (isMenuVisible(row)) {
80             mLogger.logMenuVisible(entry);
81             row.animateResetTranslation();
82             return;
83         } else if (row.isChildInGroup() && isMenuVisible(row.getNotificationParent())) {
84             mLogger.logParentMenuVisible(entry);
85             row.getNotificationParent().animateResetTranslation();
86             return;
87         } else if (row.isSummaryWithChildren() && row.areChildrenExpanded()) {
88             // We never want to open the app directly if the user clicks in between
89             // the notifications.
90             mLogger.logChildrenExpanded(entry);
91             return;
92         } else if (row.areGutsExposed()) {
93             // ignore click if guts are exposed
94             mLogger.logGutsExposed(entry);
95             return;
96         }
97 
98         // Mark notification for one frame.
99         row.setJustClicked(true);
100         DejankUtils.postAfterTraversal(() -> row.setJustClicked(false));
101 
102         if (!row.getEntry().isBubble() && mBubblesOptional.isPresent()) {
103             mBubblesOptional.get().collapseStack();
104         }
105 
106         mNotificationActivityStarter.onNotificationClicked(entry, row);
107     }
108 
isMenuVisible(ExpandableNotificationRow row)109     private boolean isMenuVisible(ExpandableNotificationRow row) {
110         return row.getProvider() != null && row.getProvider().isMenuVisible();
111     }
112 
113     /**
114      * Attaches the click listener to the row if appropriate.
115      */
register(ExpandableNotificationRow row, StatusBarNotification sbn)116     public void register(ExpandableNotificationRow row, StatusBarNotification sbn) {
117         Notification notification = sbn.getNotification();
118         if (notification.contentIntent != null || notification.fullScreenIntent != null
119                 || row.getEntry().isBubble()) {
120             row.setOnClickListener(this);
121             row.setOnDragSuccessListener(mOnDragSuccessListener);
122         } else {
123             row.setOnClickListener(null);
124             row.setOnDragSuccessListener(null);
125         }
126     }
127 
128     /** Daggerized builder for NotificationClicker. */
129     public static class Builder {
130         private final NotificationClickerLogger mLogger;
131         private final PowerInteractor mPowerInteractor;
132 
133         @Inject
Builder(NotificationClickerLogger logger, PowerInteractor powerInteractor)134         public Builder(NotificationClickerLogger logger, PowerInteractor powerInteractor) {
135             mLogger = logger;
136             mPowerInteractor = powerInteractor;
137         }
138 
139         /** Builds an instance. */
build( Optional<Bubbles> bubblesOptional, NotificationActivityStarter notificationActivityStarter )140         public NotificationClicker build(
141                 Optional<Bubbles> bubblesOptional,
142                 NotificationActivityStarter notificationActivityStarter
143         ) {
144             return new NotificationClicker(
145                     mLogger,
146                     mPowerInteractor,
147                     bubblesOptional,
148                     notificationActivityStarter);
149         }
150     }
151 }
152