1 package com.android.car.notification; 2 3 import android.car.drivingstate.CarUxRestrictions; 4 import android.os.Build; 5 import android.os.Handler; 6 import android.os.Message; 7 import android.util.Log; 8 import android.view.View; 9 import android.widget.Toast; 10 11 import java.util.List; 12 13 /** 14 * This class is a bridge to collect signals from the notification and ux restriction services and 15 * trigger the correct UI updates. 16 */ 17 public class NotificationViewController { 18 private static final boolean DEBUG = Build.IS_ENG || Build.IS_USERDEBUG; 19 private static final String TAG = "NotificationViewControl"; 20 private final CarNotificationView mCarNotificationView; 21 private final PreprocessingManager mPreprocessingManager; 22 private final CarNotificationListener mCarNotificationListener; 23 private final boolean mShowRecentsAndOlderHeaders; 24 private CarUxRestrictionManagerWrapper mUxResitrictionListener; 25 private NotificationDataManager mNotificationDataManager; 26 private NotificationUpdateHandler mNotificationUpdateHandler = new NotificationUpdateHandler(); 27 private boolean mShowLessImportantNotifications; 28 private boolean mIsVisible; 29 NotificationViewController(CarNotificationView carNotificationView, PreprocessingManager preprocessingManager, CarNotificationListener carNotificationListener, CarUxRestrictionManagerWrapper uxResitrictionListener)30 public NotificationViewController(CarNotificationView carNotificationView, 31 PreprocessingManager preprocessingManager, 32 CarNotificationListener carNotificationListener, 33 CarUxRestrictionManagerWrapper uxResitrictionListener) { 34 mCarNotificationView = carNotificationView; 35 mPreprocessingManager = preprocessingManager; 36 mCarNotificationListener = carNotificationListener; 37 mUxResitrictionListener = uxResitrictionListener; 38 mNotificationDataManager = NotificationDataManager.getInstance(); 39 mShowRecentsAndOlderHeaders = mCarNotificationView.getContext() 40 .getResources().getBoolean(R.bool.config_showRecentAndOldHeaders); 41 42 // Long clicking on the notification center title toggles hiding media, navigation, and 43 // less important (< IMPORTANCE_DEFAULT) ongoing foreground service notifications. 44 // This is only available for ENG and USERDEBUG builds. 45 View view = mCarNotificationView.findViewById(R.id.notification_center_title); 46 if (view != null && (Build.IS_ENG || Build.IS_USERDEBUG)) { 47 view.setOnLongClickListener(v -> { 48 mShowLessImportantNotifications = !mShowLessImportantNotifications; 49 Toast.makeText( 50 carNotificationView.getContext(), 51 "Foreground, navigation and media notifications " + ( 52 mShowLessImportantNotifications ? "ENABLED" : "DISABLED"), 53 Toast.LENGTH_SHORT).show(); 54 resetNotifications(mShowLessImportantNotifications); 55 return true; 56 }); 57 } 58 59 resetNotifications(mShowLessImportantNotifications); 60 } 61 62 /** 63 * Updates UI and registers required listeners 64 */ enable()65 public void enable() { 66 mCarNotificationListener.setHandler(mNotificationUpdateHandler); 67 mUxResitrictionListener.setCarNotificationView(mCarNotificationView); 68 try { 69 CarUxRestrictions currentRestrictions = 70 mUxResitrictionListener.getCurrentCarUxRestrictions(); 71 mCarNotificationView.onUxRestrictionsChanged(currentRestrictions); 72 } catch (RuntimeException e) { 73 Log.e(TAG, "Car not connected", e); 74 } 75 } 76 77 /** 78 * Remove listeners. 79 */ disable()80 public void disable() { 81 mCarNotificationListener.setHandler(null); 82 mUxResitrictionListener.setCarNotificationView(null); 83 } 84 85 /** 86 * Called when the notification view's visibility is changed. 87 */ onVisibilityChanged(boolean isVisible)88 public void onVisibilityChanged(boolean isVisible) { 89 mIsVisible = isVisible; 90 // Reset and collapse all groups when notification view disappears. 91 if (!mIsVisible) { 92 resetNotifications(mShowLessImportantNotifications); 93 mCarNotificationView.resetState(); 94 } 95 } 96 97 /** 98 * Reset notifications to the latest state. 99 */ resetNotifications(boolean showLessImportantNotifications)100 private void resetNotifications(boolean showLessImportantNotifications) { 101 mPreprocessingManager.init(mCarNotificationListener.getNotifications(), 102 mCarNotificationListener.getCurrentRanking()); 103 104 if (DEBUG) { 105 Log.d(TAG, "Unprocessed notification map: " 106 + mCarNotificationListener.getNotifications()); 107 } 108 109 List<NotificationGroup> notificationGroups = mPreprocessingManager.process( 110 showLessImportantNotifications, 111 mCarNotificationListener.getNotifications(), 112 mCarNotificationListener.getCurrentRanking()); 113 114 if (DEBUG) { 115 Log.d(TAG, "Processed notification groups: " + notificationGroups); 116 } 117 118 if (!mShowRecentsAndOlderHeaders) { 119 mNotificationDataManager.updateUnseenNotificationGroups(notificationGroups); 120 } 121 122 mCarNotificationView.setNotifications(notificationGroups); 123 } 124 125 /** 126 * Update notifications: no grouping/ranking updates will go through. 127 * Insertion, deletion and content update will apply immediately. 128 */ updateNotifications( boolean showLessImportantNotifications, int what, AlertEntry alertEntry)129 private void updateNotifications( 130 boolean showLessImportantNotifications, int what, AlertEntry alertEntry) { 131 132 if (mPreprocessingManager.shouldFilter(alertEntry, 133 mCarNotificationListener.getCurrentRanking())) { 134 // if the new notification should be filtered out, return early 135 return; 136 } 137 138 List<NotificationGroup> notificationGroups = mPreprocessingManager.updateNotifications( 139 showLessImportantNotifications, 140 alertEntry, 141 what, 142 mCarNotificationListener.getCurrentRanking()); 143 if (what == CarNotificationListener.NOTIFY_NOTIFICATION_REMOVED) { 144 mCarNotificationView.removeNotification(alertEntry); 145 } else { 146 if (DEBUG) { 147 Log.d(TAG, "Updated notification groups: " + notificationGroups); 148 } 149 150 mCarNotificationView.setNotifications(notificationGroups); 151 } 152 } 153 154 private class NotificationUpdateHandler extends Handler { 155 @Override handleMessage(Message message)156 public void handleMessage(Message message) { 157 if (mIsVisible) { 158 if (message.what == CarNotificationListener.NOTIFY_RANKING_UPDATED) { 159 // Do not update notifications if ranking is updated while panel is visible. 160 return; 161 } 162 163 updateNotifications( 164 mShowLessImportantNotifications, 165 message.what, 166 (AlertEntry) message.obj); 167 } else { 168 resetNotifications(mShowLessImportantNotifications); 169 } 170 } 171 } 172 } 173