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